redmineのプロジェクト一覧ページを見やすくする

# 関連URL

- [Redmineのプロジェクト一覧ページを見やすくする(View customize plugin) - Enjoy*Study](http://blog.enjoyxstudy.com/entry/2015/05/25/000000)
- [GitHub - onozaty/redmine-view-customize: View customize plugin for Redmine](https://github.com/onozaty/redmine-view-customize)

# 背景

Redmineのプロジェクト一覧はやや使い勝手が悪いです。具体的にはプロジェクト名だけでなくプロジェクト概要まで表示されること。

以下のようにプロジェクト名と概要説明のスタイル(フォントサイズ)が似たりよったり(特に以下の例の最初のように概要説明にリンクが含まれるとさらに識別しづらい)なので数が増えてくると目的のプロジェクトを探すのに時間を要します。(数秒のロス)

[![Image from Gyazo](https://i.gyazo.com/2c2a098d135985c199100c079020a654.png)](https://gyazo.com/2c2a098d135985c199100c079020a654) 

各プロジェクトページをブックマークしておけばプロジェクト一覧は必要ないので問題ないので放置していましたが、管理しているプロジェクトが20近くなって、さらに複数のプロジェクトが同時稼働していて、一覧からたどる作業が増えてきたので改善することにしました。

# 対策案

以下が考えられます。スタイルの変更でもとりあえず見やすくできると思いましたが、プラグインがないか探してみたら「[View customize plugin for Redmine](https://github.com/onozaty/redmine-view-customize)」という素晴らしいのがありました。ので適用しました。

- 各プロジェクトページをブックマーク => すでに実施していたが限界を迎えた
- スタイル(*.css)をカスタマイズしてプロジェクト名を強調表示して視認性を向上させる
    - 例えば`font-size:150%;font-weight:bold`とか
- プラグインでカスタマイズ

以下のようにプロジェクト名と概要説明が明確に分離されて瞬時に探せるようになりました。

[![Image from Gyazo](https://i.gyazo.com/bed6a3aad0715ece1cb53406207de8b7.png)](https://gyazo.com/bed6a3aad0715ece1cb53406207de8b7) 

# 導入手順

## プラグイン導入

[プラグイン公式ページ](https://github.com/onozaty/redmine-view-customize)に記載されているとおりです。

```bash
cd {RAILS_ROOT}/plugins
git clone https://github.com/onozaty/redmine-view-customize.git view_customize
cd ../
bundle install --without development test
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
```

このあとWebサーバを再起動するとRedmineの管理ページに当該プラグイン用メニューが表示されます。

[![Image from Gyazo](https://i.gyazo.com/b79bb99cf6dbd508b26b1cee8428e234.png)](https://gyazo.com/b79bb99cf6dbd508b26b1cee8428e234)

## プロジェクト一覧ページのカスタマイズ

以下のように設定します。ここではプロジェクト一覧ページだけを対象としていますが、プラグイン自体は「全て」もしくは「任意のページ」のカスタマイズが可能です。

### 設定内容

| 設定項目 | 設定内容 |
|:--|:--|
| パスのパターン  | `/projects/?$` |
| 挿入位置  | 全ページのヘッダ  |
| 種別  | JavaScript  |
| コード  | 後述  |
| コメント  | 任意  |
| 有効 | チェック |
| プライベート | チェックなし |

[![Image from Gyazo](https://i.gyazo.com/80f8000dcc5c66bc84c5138a6cd4bd30.png)](https://gyazo.com/80f8000dcc5c66bc84c5138a6cd4bd30)

### 設定コード

コードには以下を記載します。Enjoy*Study様ページのまんまです。感謝。

[Redmineのプロジェクト一覧ページを見やすくする(View customize plugin) - Enjoy*Study](http://blog.enjoyxstudy.com/entry/2015/05/25/000000)


```javascript
// Path pattern: /projects/?$
// Type        : JavaScript
$(function () {
    var root = $('#projects-index').children('.projects').children('li');
    var rows = [];
    for (var i = 0, len = root.length; i < len; i++) {
        var target = root.eq(i);
        rows.push(createRow(target));
        Array.prototype.push.apply( rows, createSubtreeRows(target, 1));
    }
    var table = $('<table class="list"><th width="30%">プロジェクト</th><th>概要</th></table>');
    for (var i = 0; i < rows.length; i++) {
        table.append(rows[i].addClass((i % 2 == 0) ? 'odd' : 'even'));
    }
    $('#projects-index').append(table);
    /////////////////////////////////////////////////////////
    function createSubtreeRows(parent, level) {
        var rows = [];
        var sub = parent.children('.projects').children('li.child');
        for (var i = 0, len = sub.length; i < len; i++) {
            var target = sub.eq(i);
            rows.push( createRow(target, parent, level));
            Array.prototype.push.apply( rows, createSubtreeRows(target, level + 1));
        }
        return rows;
    }
    function createRow(target, parent, level) 
    {
        var link = target.children('div').children('a');
        var description = target.children('div').children('div');
        var tr = $('<tr>');
        tr.addClass('project');
        if (parent) {
            tr.addClass('child').addClass('leaf');
        }
        else {
            tr.addClass('parent').addClass('root');
        }
        if (level > 0) {
            tr.addClass('idnt').addClass('idnt-' + level);
        }
        tr.append($('<td>').append($('<span>').append(link)).addClass('name'));
        tr.append($('<td>').append(description).addClass('description'));
        return tr;
    }
});
```