Redmine: チケットの説明欄を非表示にする(View customize plugin)

GoogleグループのRedmine Users (japanese)で、チケットの説明欄を非表示にするにはといった質問があったので、View customize pluginで対応してみました。

非表示にするだけならば、CSSで設定できる場合も多いですが、今回対象となる要素を指定するためには、xxを持った親要素といった指定が必要で、現状のCSSだとできない(CSS4でhasがあって、それが入って各ブラウザに実装されれば…)ので、JavaScriptで実施してみました。

View customize の設定内容

Path pattern

チケット画面を対象とします。

/issues

Code

Type:JavaScriptとして下記を設定します。

$(function() {
  $('#issue_description_and_toolbar').parent().hide();
  $('div.issue div.description').hide()
    .next('hr').hide();
});

設定後のイメージ

説明欄を消すことができました。

f:id:onozaty:20161120003509p:plain:w700 f:id:onozaty:20161120003512p:plain:w650

Redmine: 一部のカスタムフィールドを説明の下に移動する(View customize plugin)

といった質問をいただいたので、View customizeで試してみました。

該当の要素を取得して、それを説明欄の下に移動するイメージです。

なお、カスタムフィールドの表示は、divがネストして1行に2項目を表示しているような形になっているので、それを再現して配置します。

View customize の設定内容

Path pattern

チケット画面を対象とします。

/issues

Code

Type:JavaScriptとして下記を設定します。 cf_2のところは、移動したいカスタムフィールドに変更してください。

$(function() {
  // 対象のカスタムフィールドの要素を取得
  var customField = $('.cf_2.attribute');

  // 説明の後に移動
  $('.description')
    .after(
      $('<div class="splitcontent">')
        .append(
          $('<div class="splitcontentleft">').append(customField)));
})

設定後のイメージ

リスト2となっているカスタムフィールドが移動しました。

変更前

f:id:onozaty:20161116004019p:plain:w600

変更後

f:id:onozaty:20161116004023p:plain:w600

なお、今回は一番末尾の項目を移動したので、表示的に違和感ない形となっていますが、途中の項目を移動すると、そこだけ歯抜けになるので、きれいに見せたい場合には、詰めるような処理も必要になってきます…

Tomcat8をインストールするAnsibleのRoleを書きました

Tomcat8をCentOS7にインストールするためのRoleを書きました。

最初はAnsible Galaxyにあがっているものを試してみようと思いましたが、やっていることが理解できない部分も多かったので、勉強もかねて自分で書いてみました。

Tomcat8をインストールして、サービスとして登録するところまでの、シンプルなものになっています。

すぐに確認できるようVagrantfileを置いています。 cloneしてvagrant upとすると、bento/centos-7.2のboxを立ち上げて、Ansibleを実行してTomcatのインストールを確認できます。

Tomcat8で、デフォルトだとローカル以外からmanagerが表示できない

Tomcat8をインストールして、managerを見ようとしたところ、403 Access Denied となったので、画面に表示されていた通りtomcat-user.xmlmanager-guiというロールでユーザを追加します。Tomcat6の時は、managerだったのが、Tomcat7以降で変わったようですね。

<role rolename="manager-gui"/>
<user username="tomcat" password="s3cret" roles="manager-gui"/>

これだけだと、ローカル以外からアクセスできませんでした。

よくよく調べたところ、デフォルトだとローカル以外からアクセスできないように制限されているようです。(ちゃんとエラーページにも書いてありました)

By default the Manager is only accessible from a browser running on the same machine as Tomcat. If you wish to modify this restriction, you'll need to edit the Manager's context.xml file.

webapps/manager/META-INF/context.xmlには、下記のようにアクセスを制限するような記載がありました。

<Context antiResourceLocking="false" privileged="true" >
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
</Context>

allowのところに正規表現で許可するアドレスを書いてあげると、問題なくアクセスできるようになりました。

Redmine: コンテキストメニューからステータスを変えた際に、対象バージョンも変更する (View customize plugin)

Issueとして、コンテキストメニューからステータスを却下(6)に変えたときに、対象バージョンを未設定としたいんだけど、どうやれば、、といったものが上がっていたので、スクリプトを書いてみました。

やり方としては、コンテキストメニューが生成されたタイミングで、ステータスを却下に変えるリンクのパラメータに、対象バージョンも追加します。

設定内容

Path pattern

チケット一覧を対象にします。

/issues

Code

Type:JavaScriptとして下記を設定します。

$(function() {

  // コンテキストメニューを表示したタイミングでフックするために
  // jQueryのshow関数を差し替え
  jQuery.fn._show = jQuery.fn.show;

  jQuery.fn.show = function() {
    if (this.attr('id') == 'context-menu') {
      // ステータスを6:却下に変える場合に
      // 対象バージョンを未設定に
      var a = $('#context-menu a[href*="status_id%5D=6"]');
      a.attr('href',a.attr('href') + '&issue%5Bfixed_version_id%5D=none');
    }

    return jQuery.fn._show.apply(this, arguments);
  };
});

Redmine: チェックボックスのカスタムフィールドを横並びで表示する (View customize plugin)

Redmine Users (japanese) - Google グループで、チェックボックスを横並びにしたいといった要望が出ていたので、View customize でやってみます。

なお、段組みを行う方法は以前書きましたが、段組みだと、コンテンツは上から下に流れるため、並び順としては横に並ばない形になります。

横に並べる方法としては、CSS3でflexboxという仕組みがあるので、これを使います。

flexboxを使うと、コンテンツを横並びに隙間なく埋めることが出来ます。(昔はfloatで頑張らなければならなかったので、便利になりましたね!)

設定内容

Path pattern

チケット編集系の画面を対象とします。

/issues

Code

Type:StyleSheetとして下記を設定します。

span.enumeration_cf.check_box_group {
  display: flex;
  display: -webkit-flex;
  flex-wrap: wrap; 
  -webkit-flex-wrap: wrap; 
}

項目のサイズを統一したい場合には、下記も追記します。(下記の例だと幅を60pxに)

span.enumeration_cf.check_box_group label {
  width: 60px;
}

結果

下記のように横並びで表示されるようになります。

f:id:onozaty:20161023004832p:plain

サイズを統一した場合は、下記のようになります。

f:id:onozaty:20161023005014p:plain

Redmine: チケット一覧のコンテキストメニューに、サーバにリクエストを送る項目を追加する (View customize plugin)

チケット一覧のコンテキストメニューに項目を追加して、その項目をクリックしたらサーバ側に選択しているチケットの番号を送って、サーバ側で処理させることができないかという質問をいただいたので、View customize pluginで試行錯誤してみました。

チケット一覧でのコンテキストメニュー生成の仕組み

チケット一覧のコンテキストメニューは、チケット一覧上での右クリックのタイミングで、選択されている行の情報などをサーバ側に送り、コンテキストメニューの情報(HTMLの断片)を受け取って、それをdiv#context-menuに設定しています。

具体的には、context_menu.js内の、contextMenuShow関数で行っています。$.ajaxで通信し、そのコールバックでコンテキストメニューを作ってしまっているので、Redmine側のコード上だとフックするポイントがありません。。

function contextMenuShow(event) {
  var mouse_x = event.pageX;
  var mouse_y = event.pageY;  
  var mouse_y_c = event.clientY;  
  var render_x = mouse_x;
  var render_y = mouse_y;
  var dims;
  var menu_width;
  var menu_height;
  var window_width;
  var window_height;
  var max_width;
  var max_height;

  $('#context-menu').css('left', (render_x + 'px'));
  $('#context-menu').css('top', (render_y + 'px'));
  $('#context-menu').html('');

  $.ajax({
    url: contextMenuUrl,
    data: $(event.target).parents('form').first().serialize(),
    success: function(data, textStatus, jqXHR) {
      $('#context-menu').html(data);
      menu_width = $('#context-menu').width();
      menu_height = $('#context-menu').height();
      max_width = mouse_x + 2*menu_width;
      max_height = mouse_y_c + menu_height;

      var ws = window_size();
      window_width = ws.width;
      window_height = ws.height;

      /* display the menu above and/or to the left of the click if needed */
      if (max_width > window_width) {
       render_x -= menu_width;
       $('#context-menu').addClass('reverse-x');
      } else {
       $('#context-menu').removeClass('reverse-x');
      }

      if (max_height > window_height) {
       render_y -= menu_height;
       $('#context-menu').addClass('reverse-y');
        // adding class for submenu
        if (mouse_y_c < 325) {
          $('#context-menu .folder').addClass('down');
        }
      } else {
        // adding class for submenu
        if (window_height - mouse_y_c < 345) {
          $('#context-menu .folder').addClass('up');
        } 
        $('#context-menu').removeClass('reverse-y');
      }

      if (render_x <= 0) render_x = 1;
      if (render_y <= 0) render_y = 1;
      $('#context-menu').css('left', (render_x + 'px'));
      $('#context-menu').css('top', (render_y + 'px'));
      $('#context-menu').show();

      //if (window.parseStylesheets) { window.parseStylesheets(); } // IE
    }
  });
}

jQueryの関数でフックする

しょうがないので、今回は、jQueryのshow関数でフックするような対応を取りました。

#context-menuに対してのshowの場合に、メニューを追加する処理を行うイメージです。

View customize の設定内容

Path pattern

チケット一覧を対象とします。

/issues/

Code

Type:JavaScriptとして下記を設定します。

送り先のURLなどは適宜変更してください。

$(function() {

  // 対象のURL(同一ドメイン)
  var commandUrl = '/test';
  var commandTitle = 'コマンド実行';

  // コンテキストメニューを表示したタイミングでフックするために
  // jQueryのshow関数を差し替え
  jQuery.fn._show = jQuery.fn.show;

  jQuery.fn.show = function() {
    if (this.attr('id') == 'context-menu') {
      // コンテキストメニューを表示したタイミングでコマンドを追加
      var menu = $('<a>').text(commandTitle).click(function() {
        execute();
        return false;
      })

      this.children('ul').append($('<li>').append(menu));
    }

    return jQuery.fn._show.apply(this, arguments);
  };

  // コマンドの実処理
  var execute = function() {

    // チェックされているチケット番号を取得(複数存在する場合はカンマ区切り)
    var issues = $('input[name="ids[]"]:checked').map(function() { return $(this).val();}).get().join(',');

    // 対象のURIにチケット番号をパラメータとして送信
    $.ajax({
      type: 'GET',
      url: commandUrl,
      data: 'issues=' + issues // チケット番号をパラメータとして
    }).done(function(data, textStatus, jqXHR){

      // 成功時の処理
      alert('成功しました。');

    }).fail(function(jqXHR, textStatus, errorThrown){

      // 失敗時の処理
      alert('失敗しました。 status:' + jqXHR.status);
    });

    contextMenuHide();
  };
});

設定後のイメージ

f:id:onozaty:20161018005342p:plain

コンテキストメニューに項目が追加され、これをクリックすると、対象のURLに対してチケット番号を送るようになります。