LoginSignup
8
7

More than 3 years have passed since last update.

Redmineでタグを使う

Last updated at Posted at 2020-10-04

JQueryプラグインのTag-it!を使って、Redmineにタグ機能をつけて見ました。

Tag-it!
http://aehlke.github.io/tag-it/

Redmineのタグ関連プラグインに比べると機能的に劣りますが、SaaSのサービスでRedmineを運用していて、自由にプラグインのインストールができないなどの場合には、スクリプトだけで簡単に実現できるので、使ってみてはどうでしょうか。

1.まず最初に、「管理 » カスタムフィールド » 新しいカスタムフィールド」で、「タグ」の入力項目を作成します。
カスタムフィールド作成.png

2.この時点では、チケット一覧画面での表示は以下のようになっています(通常のテキスト入力項目です)。
スクリプト適用前.png

3.View Customize Pluginを使って、作成したカスタムフィールドにTag-it!を適用します。
外部JavaScriptを読み込むため、View Customize Pluginはv2.1.0以上を使用してください。

<!--
パスのパターン:/issues
挿入位置:全ページのヘッダ
種別:HTML
-->

<style>
    /* 作成したカスタムフィールドの番号に合わせて変更してください */
    .cf_52 {
        display: none;
    }

    p.tagbox {
        padding: 0.1em 0.5em;
        background: #fcfcfc;
        border: solid 1px #ccc;
        border-radius: 5px;
        float: left;
        margin: 2px 3px 0px 0;
        font-weight: 500;
        color: #269;
        line-height: 17px;
    }

    ul.tagit {
        padding: 1px 5px;
        overflow: auto;
        margin-left: inherit;
        /* usually we don't want the regular ul margins. */
        margin-right: inherit;
    }

    ul.tagit li {
        display: block;
        float: left;
        margin: 2px 5px 2px 0;
    }

    ul.tagit li.tagit-choice {
        position: relative;
        line-height: inherit;
    }

    input.tagit-hidden-field {
        display: none;
    }

    ul.tagit li.tagit-choice-read-only {
        padding: .2em .5em .2em .5em;
    }

    ul.tagit li.tagit-choice-editable {
        padding: .2em 18px .2em .5em;
    }

    ul.tagit li.tagit-new {
        padding: .25em 4px .25em 0;
    }

    ul.tagit li.tagit-choice a.tagit-label {
        cursor: pointer;
        text-decoration: none;
    }

    ul.tagit li.tagit-choice .tagit-close {
        cursor: pointer;
        position: absolute;
        right: .1em;
        top: 50%;
        margin-top: -8px;
        line-height: 17px;
    }

    /* used for some custom themes that don't need image icons */
    ul.tagit li.tagit-choice .tagit-close .text-icon {
        display: none;
    }

    ul.tagit li.tagit-choice input {
        display: block;
        float: left;
        margin: 2px 5px 2px 0;
    }

    ul.tagit input[type="text"] {
        -moz-box-sizing: border-box;
        -webkit-box-sizing: border-box;
        box-sizing: border-box;

        -moz-box-shadow: none;
        -webkit-box-shadow: none;
        box-shadow: none;

        border: none;
        margin: 0;
        padding: 0;
        width: inherit;
        background-color: inherit;
        outline: none;
    }
</style>

<script src="https://cdn.jsdelivr.net/npm/tag-it@2.0.0/js/tag-it.min.js"></script>

<script>
    $(function() {
        tags_list = [];
        tagitem_list = [];
        const apiKey = ViewCustomize.context.user.apiKey;
        //プロジェクトIDを取得
        if (ViewCustomize.context.project) {
            project_id = ViewCustomize.context.project.identifier;
            project_id_tag = '/projects/' + project_id;
        } else {
            project_id_tag = "";
        }
        // タグ入力項目追加(初期表示)
        makeavailableTags();

        //チケット一覧グリッド・チケット詳細表示項目にタグ表示デザインを適用
        //作成したカスタムフィールドの番号に合わせて変更してください(cf_52の部分)
        $("td.cf_52, .cf_52 .value").each(function() {
            const txt = $(this).text();
            if (txt.length > 0) {
                const resArray = txt.split(",");
                let ret = "";
                for (let i = 0; i < resArray.length; i++) {
                    ret += '<p class="tagbox"><a href="' + project_id_tag + '/search?utf8=✓&scope=&q=' + resArray[i] + '">' + resArray[i] + '</a></p>';
                }
                $(this).html(ret);
                $('.cf_52').show();
            }
        });

        //チケット一覧グリッドで表示項目の幅を確保
        if ($('td.cf_52').length) {
            $('td.cf_52').show();
            $(".cf_52").css('min-width', '250px');
        }

        // ステータス変更時など、フォームの内容が書き変わるたびにタグ再処理
        const _replaceIssueFormWith = replaceIssueFormWith;
        replaceIssueFormWith = function(html) {
            _replaceIssueFormWith(html);
            makeavailableTags();
        };
    })

    //カスタムフィールドをタグ入力可能にする
    function tagit(tagitem_list) {
        const tagAll = unique(tagitem_list);
        $("#issue_custom_field_values_52").tagit({
            singleField: true,
            //自動補完するワードを設定
            availableTags: tagAll
        });
    }

    //自動補完するワードを準備
    function makeavailableTags() {
        if (tagitem_list.length === 0) { //画面初期表示時に登録済みタグを取得する
            $.when(
                gettagList100()
            ).done(function() {
                //取得したタグをリスト化する
                for (let i = 0; i < tags_list.length; i++) {
                    const tagArray = tags_list[i].split(',');
                    for (let j = 0; j < tagArray.length; j++) {
                        tagitem_list.push(tagArray[j]);
                    }
                }
                tagit(tagitem_list);
            })
        } else { //トラッカー・ステータス変更時には再取得しない
            tagit(tagitem_list);

        }
    }

    let tags_list = [];
    let tagitem_list = [];
    //チケットに登録されているタグを取得 
    function gettagList100() {
        const deferred = new $.Deferred();
        if (ViewCustomize.context.project) {
            url = '' + '/issues.json' + '?project_id=' + project_id + '&status_id=*&cf_52=*&limit=100'; //最新の100件のみ取得
        } else {
            url = '' + '/issues.json' + '?status_id=*&cf_52=*&limit=100';
        }
        $.ajax({
            type: "GET",
            url: url,
            headers: {
                'X-Redmine-API-Key': apiKey
            },
            dataType: "text",
            contentType: 'application/json',
        }).done(function(data) {
            data = JSON.parse(data);
            data1 = data.issues;
            for (i in data1) {
                custom_fields = data1[i].custom_fields;
                for (j in custom_fields) {
                    targetfield = custom_fields[j].id;
                    //作成したカスタムフィールドの番号に合わせて変更してください(52の部分)
                    if (targetfield === 52 && custom_fields[j].value !== "") {
                        tags_list.push(custom_fields[j].value);
                    }
                };
            };
            deferred.resolve();
        });
        return deferred;
    }

    //タグリストの重複を削除
    function unique(list) {
        let result = [];
        $.each(list, function(i, e) {
            if ($.inArray(e, result) == -1) result.push(e);
        });
        return result;
    }
</script>

4.スクリプト適用後の画面表示は以下のようになります。

チケット詳細画面
スクリプト適用後.png
チケット一覧画面
チケット一覧画面.png

5.タグをクリックすると、タグをキーワードにして検索画面に遷移します。
検索画面に遷移.png

Selectize.jsを使用する場合

Selectize.js
https://selectize.github.io/selectize.js/

View Customize Pluginで読み込むjQueryプラグインを以下のように変更してください。

<!--
<script src="https://cdn.jsdelivr.net/npm/tag-it@2.0.0/js/tag-it.min.js"></script>
 -->
<script src="https://cdn.jsdelivr.net/npm/selectize@0.12.6/dist/js/standalone/selectize.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/selectize@0.12.6/dist/css/selectize.bootstrap3.css">

スクリプトを以下のように変更してください。

    //カスタムフィールドをタグ入力可能にする(selectizeを使用)
    function selectize(tagitem_list) {
        const tagAll = unique(tagitem_list);
        let listresult = [];
        for (i in tagAll) {
            listresult.push({
                name: tagAll[i]
            });
        }
        $('#issue_custom_field_values_52').selectize({
            plugins: ['remove_button', 'restore_on_backspace'],
            valueField: 'name',
            labelField: 'name',
            searchField: 'name',
            preload: 'focus',
            delimiter: ',',
            dropdownParent: 'body',
            closeAfterSelect: true,
            create: true,
            options: listresult,
            sortField: {
                field: 'name'
            }
        });
    }

    //自動補完するワードを準備
    function makeavailableTags() {
        if (tagitem_list.length === 0) { //画面初期表示
            $.when(
                gettagList100()
            ).done(function() {
                //取得したタグをリスト化する
                for (let i = 0; i < tags_list.length; i++) {
                    const tagArray = tags_list[i].split(',');
                    for (let j = 0; j < tagArray.length; j++) {
                        tagitem_list.push(tagArray[j]);
                    }
                }
                //tagit(tagitem_list);
                selectize(tagitem_list);
            })
        } else { //トラッカー・ステータス変更時
            //tagit(tagitem_list);
            selectize(tagitem_list);
        }
    }

Selectize.png

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7