Redmine View Customize plugin カスタマイズ事例集

Redmineの表示をカスタマイズするプラグイン「[Redmine view customize plugin](https://github.com/onozaty/redmine-view-customize)」の活用事例集です。

## 共通設定

特に指定がなければ以下の設定です。

- 挿入位置：全てのページのヘッダ
- 種別：JavaScript

## チケットリンク作成用文字列を表示

Redmineのチケットリンクを貼るための文字列（Markdown記法）をチケットタイトル下部に表示するスクリプトです。
MattermostやGitLabでチケットを共有する際にコピペすると便利です。

- パスのパターン：`/issues/[0-9]+`

```js
$(function(){
    const tracker = $('#content > .contextual + h2').text();
    const title = $('#content .subject h3').text().trim();
    const text = tracker + "「" + title + "」";
    const linkText = '<br><span id="linkText" style="padding-left: 5px; font-size: 70%; color: #737373">' + '[' + text + ']' + '(' + location.href + ')' + '</span>';
    $('#content > .contextual + h2').html(tracker + linkText);
}); 
```

#### 出力サンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065197/093e77c2-9316-0bcc-807c-3cd24b10e916.png)

`トラッカー #チケット番号 「チケットタイトル」`　の形式で出力

## チケットのURLのハッシュ、クエリを除去

チケットのハッシュ、クエリ以降を除いた元のURLにジャンプするリンクを追加するスクリプトです。
`チケットリンク作成用文字列を表示`と組み合わせて利用すると良いと思います。

- パスのパターン：`/issues/[0-9]+`

```js
$(function(){
    $('#content > .contextual').append("<a href='" + location.href.split("?")[0].split("#")[0] + "' class='icon icon-reload'>元URL</a>");
}); 
```

#### 出力サンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065197/493f455f-a27b-89f7-4ec3-be7734900a5a.png)

`↩️元URL`ボタンをチケットメニューの右端に追加


## クエリリンク作成用文字列を表示

Redmineのチケットクエリリンクを貼るための文字列（Markdown記法）をタイトル下部に表示するスクリプトです。
MattermostやGitLabでクエリを共有する際にコピペすると便利です。

- パスのパターン：`/projects/gad/issues`

```js
$(function(){
    const title = $('#content > .contextual + h2').text();
    const text = "クエリー：" + title;
    const linkText = '<br><span id="linkText" style="padding-left: 5px; font-size: 50%; color: #737373">' + '[' + text + ']' + '(' + location.href + ')' + '</span>';
    $('#content > .contextual + h2').html(title + linkText);
}); 
```

## チケット説明欄の任意文字列を一括置換

チケットの説明欄に記載した`{{変更前}}`を`＜＜変更後＞＞`に置換するスクリプトです。
共有サーバのパス等、よく利用されるが変更の可能性がある情報をチケット更新せずに対処するために利用しています。
限定的なため、置換パターンが増える場合はもっと上手いやり方を検討すべきだと思います。

```js
$(function(){
    const AFTER_STR = "＜＜変更後＞＞";
    $('#content .description pre').each(function(){
        $(this).html($(this).html().replace(/{{変更前}}/g, AFTER_STR))
    });
}); 
```

## チケットの優先度による色変更

チケット一覧にて、チケットの優先度「高」「低」の文字色を変更するスクリプトです。

- パスのパターン：`/projects/gad/issues`

```js
$(function(){
    // 高
    $('.priority-4').each(function(){
        $(this).children(".priority").css("color","#c22");
    });
    // 低
    $('.priority-2').each(function(){
        $(this).children(".priority").css("color","blue");
    });
}); 
```

## 選択式項目の選択・解除をしやすくする

ユーザ複数選択項目の選択/解除をしやすくするスクリプトです。
選択肢をクッキーに保存しておくため、ユーザ単位によく利用する選択肢を保持できます。
カスタムフィールドのID`issue_custom_field_values_243`は適宜変更してください。

- パスのパターン：`/issues/[0-9]+`

```js
$(function () {
    // 項目の前に選択、全解除の部品を追加
    var $target = $("#issue_custom_field_values_243");
    var $worktext = $("<input type='text' id='issue_custom_field_values_243_text' title='半角カンマで区切って入力してください。（例：山田 太郎,田中 次郎）入力内容は「選択」ボタン押下時にクッキーに保存されます。'>");
    var $workbtn = $("<button type='button' id='issue_custom_field_values_243_btn'>選択</button>");
    var $workreset = $("<button type='button' id='issue_custom_field_values_243_reset'>全解除</button>");
    $target.before($worktext, $workbtn, $workreset);

    // クッキーに値があれば初期値として設定
    const cookieValue = document.cookie
        .split('; ')
        .find(row => row.startsWith('issue_custom_field_values_243_text'))
        .split('=')[1];
    $worktext.val(cookieValue);

    // 「選択」ボタン押下でテキスト欄で指定した人を選択状態にする
    $workbtn.on("click", function () {
        $worktext.val().split(/, ?/).forEach(function (value) {
            $target.find("option").filter(function () { return $(this).text() === value }).prop("selected", true);
        });
        // 現在の入力値をクッキーに保存
        document.cookie = "issue_custom_field_values_243_text=" + $worktext.val();
    });

    // 「全解除」ボタンで選択状態をクリアする
    $workreset.on("click", function () {
        $target.val("");
    });

});
```

#### 出力サンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065197/ef39111b-826c-35e6-8f0f-0dd7c1f2e17d.png)


## 最近更新があったチケットの更新日付を強調表示

最近更新があったチケットの更新日付を強調表示するスクリプトです。

- パスのパターン：`/projects/gad/issues`

```js
$(function () {
    // 強調したい日数
    const range = 3;
    // 適用スタイル
    const style = {"fontWeight": "bold"};

    // 基準日（今日 - 強調したい日数）
    var tmpdate = new Date();
    tmpdate.setDate(tmpdate.getDate() - range);
    const tmpdateText = tmpdate.getFullYear() + "/" + ("0" + (tmpdate.getMonth() + 1)).slice(-2) + "/" + ("0" + tmpdate.getDate()).slice(-2);

    $(".updated_on").each(function () {
        // 基準日以上の場合、スタイル適用
        const update = $(this).text().substr(0, 10);
        console.log("基準日：" + tmpdateText + " 更新日：" + update);
        if(update >= tmpdateText) {
            $(this).css(style);
        }
    });

});
```

#### 出力サンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065197/b54d4628-e749-a9bf-2dbb-557314902552.png)

現在日（2021/01/16）から3日以内に更新のあったチケットを強調

## 特定条件を満たす行のスタイル変更

チケット一覧にて、特定の条件を満たすチケットを強調表示するスクリプトです。

今回は親子チケットを一覧する際に区切りがわかりやすいように、親チケット（カスタムフィールド`cf_253`が特定の値`●●`）の場合に行の上下の線のスタイルを変更しました。
用途やアイデア次第で色々な表現ができると思います。

- パスのパターン：`/projects/gad/issues`

```js
$(function () {
    // 適用スタイル
    const style = {"borderTop": "6px solid gray","borderBottom": "2px dotted gray"};

    $(".cf_253.list").each(function () {
        // ●●の場合、スタイル適用
        const column = $(this).text();
        if(column >= "●●") {
            $(this).parent().css(style);
        }
    });

});
```

## ユーザ選択項目で自分を選択しやすくする

ユーザ選択項目で自分を選択しやすくするスクリプトです。

- パスのパターン：`/issues/[0-9]+`

以下のURL参照

https://blog.enjoyxstudy.com/entry/2016/01/17/000000

<!--
```js
$(function () {
  // ユーザのIDを取得
  const userUrl = $("div#loggedas a").attr("href");
  const userId = userUrl.substr(userUrl.lastIndexOf('/') + 1);

  // ユーザ一覧のカスタムフィールドを取得
  $('select.user_cf').each(function() {
    // 自分自身の選択肢を取得
    var meOption = $(this).find('option[value="' + userId + '"]').first();

    // 複製し、空項目の次に名前を変えて挿入
    meOption.clone()
      .removeAttr('selected')
      .insertAfter($(this).find('option[value=""]'))
      .text('<<自分>>');
  });

});
```
-->

#### 出力サンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065197/83375122-303f-50a5-29e4-d070a745b663.png)


## チケット一覧のヘッダを固定表示

チケット一覧のページをスクロールした際にもチケットのヘッダが常に表示されるようにするスクリプトです。
ヘッダが表示される範囲ではそのまま表示し、スクロールしてヘッダが見切れるタイミングでヘッダをウィンドウ上部に固定表示します。
※　項目数やウィンドウ幅によってはずれて上手く表示されません。解消方法求む。

- パスのパターン：`/projects/gad/issues`

```js
$(function () {
    // 適用スタイル
    const style_fix = { "position": "fixed", "top": "0", "display": "none" };
    // 操作対象
    $header = $("table.list thead");
    $header_clone = $header.clone().css(style_fix).insertAfter($header);
    $window = $(window);
    // 下にスクロールしたら表示するようにする
    $window.on("scroll", function () {
        if ($window.scrollTop() > $header.offset().top) {
            $header_clone.show();
            // ヘッダ幅調整
            headerResize();
        } else {
            $header_clone.hide();
        }
    });
    // ウィンドウをリサイズしたらヘッダも再調整
    $window.on("resize", function () {
        headerResize();
    });
    // ヘッダ幅調整
    function headerResize() {
        // コピー元の幅を保存しておく
        let tmp_width = new Array();
        $header.find("th").each(function () {
            tmp_width.push($(this).width());
        });
        // クローンの幅をコピー元と同一にする
        $header_clone.find("th").each(function (index) {
            $(this).width(tmp_width[index] + 1);
        });
    }
});

```

#### 出力サンプル

スクロール前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065197/ea6dac25-a3c4-554c-5a96-c48a71e362e7.png)
スクロール後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1065197/b3de1ba7-87f9-5c82-629b-6ef3860ac5ca.png)