JqueryでRedmineの「wikiページ」をもっと便利に(フィルタリング機能と移動機能)

## JqueryでRedmineの「wikiページ」をもっと便利に(フィルタリング機能と移動機能)

[Redmine Advent Calendar 2023](https://adventar.org/calendars/8974)の13日目の記事として作成しました。

[Redmine Advent Calendar 2023](https://adventar.org/calendars/8974)の11日目の記事として[JqueryでRedmineの「活動ページ」をもっと便利に②(フィルタリング機能と移動機能)](https://qiita.com/yam_asaki/items/2428610fd89bc73ce0ab)を作成しましたが、「活動ページ」だけではなく、「wikiページ」にも「フィルタリング機能と移動機能」を追加できそうな気がしてきましたので、試してみた結果を記事にまとめました。



* * *
### 既存のRedmineで不便なこと、改善したい点

Redmineのwikiページは、マークダウン形式でリッチなテキストを作成できる便利な機能です。ただし、かなり長い文章をwikiで作成すると、文章全体を行ったり来たりする時に、マウスのホイールをグリグリ動かしながら、ページを縦スクロースさせる必要があり、いつも面倒だなと感じていました。

<img width="100%" alt="wikiページが長くなると縦スクロールが面倒" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3633321/bb76c2df-aade-a507-c8fa-3ab7d7272644.gif">

　　　　　　　　　　　　　　　　　　＜wikiページが長くなると縦スクロールが面倒＞

                  
この問題を解決するためにwikiページに「{{>toc}}」を記載すれば、wikiの目次を作成してくれて、自動的に各hタグに移動するハイパーリンクが生成される機能がRedmineの標準機能として提供されています。
しかし、「{{>toc}}」で目次を作成すると、画面のトップの場所から下部の場所に移動するのはリンクをクリックすることによって可能ですが、画面の下部から画面のトップに戻る時は、マウスを使って縦スクロールさせる必要があり、ページが長いと不便さを感じます。



<img width="100%" alt="「{{>toc}}」による目次作成機能" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3633321/43ff2269-2ea6-7358-5be6-9ceef003d608.png">

　　　　　　　　　　　　　　　　　　＜「{{>toc}}」による目次作成機能＞


しかし、「{{>toc}}」を記載するのも面倒な場合や、「wikiページ」でフィルタリング機能を追加して、見たい情報だけを簡単に確認をしたいというニーズを満たすために、今回、「wikiページ」に、JavaScript（Jquery）だけで、独自のフィルター機能と、各h要素に簡単に移動できる機能を追加して、Redmineの「wikiページ」をもっと便利に、プチ改造をしてみます。

下図が完成イメージです。サンプル動画のように、
１）トップの位置からマウスで縦スクロールさせる必要なく、各h要素に移動することができます。
２）フィルター（検索）用のテキストボックスを追加して、リアルタイムで検索した情報のみを表示します。

<img width="100%" alt="JqueryでRedmineの「wikiページ」をもっと便利にした画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3633321/e9c3e8c9-f2cf-5f41-5094-02f1fe6394ad.gif">

　　　　　　　　　　　　　　　　　　＜JqueryでRedmineの「wikiページ」をもっと便利にした画面＞
                  
* * *         
### 実現した方法

#### 下記のプロセスでRedmineの「wikiページ」をもっと便利に改造します。

１）各h要素に移動するボタンを追加
２）各h要素ごとにトップに戻るボタンを追加
３）検索用のテキストボックスの追加、検索機能の実装

#### 具体的な方法を説明します。

1.1.　各h要素に移動するボタンを追加＆各h要素ごとにトップに戻るボタンを追加
         
各h要素に移動するボタンのhtmlタグを取得するために、linksという変数を準備します。
各h要素に移動するボタンの作成は、リンク先のタグ名と、リンク先のタグに追加するID名が被らないようにするために、kubunという２つの引数を持つprocessHeaderという関数で処理をします。この部分の処理は前回の[JqueryでRedmineの「活動ページ」をもっと便利に②(フィルタリング機能と移動機能)](https://qiita.com/yam_asaki/items/2428610fd89bc73ce0ab)にはなかった処理です。
下記のプログラムソースのように、h1,h2,h3を移動先のタグとして指定していますが、processHeaderの引数を変えてあげれば、その他のタグに対しても移動ボタンを作ることができます。processHeaderで関数化しているので、簡単に他のタグのための移動ボタンを作成することができます。
各h要素ごとにトップに戻るボタンも一緒に追加します。


```JavaScript

  var links = []; // 各要素の情報を蓄積する配列
  links.push("<br>");

  function processHeader(headerSelector, kubun) {
    $(headerSelector).each(function (index) {
      // 連番のIDを作成して付与
      var newID = "youso" + kubun + (index + 1);
      $(this).attr("id", newID);

      // 各要素の情報を配列に追加
      var linkContent =
        '　<span class="badge badge-status-open"><a href="#' +
        newID +
        '"><span class="kensu">' +
        $(this).text() +
        "</span></a></span>";

      links.push(linkContent);
      //links.push("<br>");

      // トップに戻るボタンを追加
      $(this).append(
        `  <span class="each_content badge badge-status-locked"><a href="#content">TOPに戻る</a></span>`
      );
    });
  }

  processHeader("#content > div.wiki.wiki-page > h1", "h1");
  processHeader("#content > div.wiki.wiki-page > h2", "h2");
  processHeader("#content > div.wiki.wiki-page > h3", "h3");

```
1.2.　作ったボタンをwikiページ内に追加

上記で作成したボタンはまだwikiページ内に挿入されていないので、下記のプログラムソースでwikiページ内に追加します。h1タグが複数あるので、eq(0)で初めのh1タグの後ろに作成したボタンを追加しています。
後で説明しますが、訳がありまして、最後のボタンの後ろにafter_thisクラスのspanタグを挿入します。
         

```JavaScript

   links.push("<span class='after_this'></span>");
  // 各h要素の情報をまとめて追加
  $("#content > div.wiki.wiki-page > h1").eq(0).after(links.join(""));
    
 ```


        
2.1.　検索用のテキストボックスの追加、検索機能の実装

検索用のテキストボックスの追加自体は非常に簡単です。下記の一文だけで実現できます、
前回の[Redmine Advent Calendar 2023](https://adventar.org/calendars/8974)で作成したプログラムソースをそのまま流用しています。

```JavaScript

  //検索ボックスの追加
  links.push('<input type="text" id="search_activity">');
  
```

               
2.2.　「検索用のテキストボックスの機能の実装①」入力された文字列の取得

上記で検索用のテキストボックスを追加できましたが、テキストボックスに何か値を入れても、このままでは何の反応もしません。検索用のテキストボックスに検索機能を実装する必要があります。
検索用のテキストボックスのinputイベントを監視して、検索用のテキストボックスに文字が入力される時にリアルタイムで、検索用のテキストボックスに入力された文字列を「searchText」という変数で取得します、
前回の[Redmine Advent Calendar 2023](https://adventar.org/calendars/8974)で作成したプログラムソースをそのまま流用しています。

```JavaScript

  // input要素の変更イベントを監視
  $("#search_activity").on("input", function () {
    // 入力された文字列を取得
    var searchText = $(this).val().toLowerCase();
  }  

```

2.3.　「検索用のテキストボックスの機能の実装②」検索結果の表示

上記で取得した文字列がwikiの各タグに含まれていたら表示、含まれていない場合は非表示にします。「toLowerCase」を使用して、検索用のテキストボックスに入力された文字列も、wikiの各タグにに含まれている文字列も全て小文字化させて、大文字、小文字を区別しないで検索を可能にします。
その際、表示、非表示にさせる要素は、.after_thisクラスの兄弟要素で、かつ、.after_thisクラス以降の要素だけを対象にしています。
これをしないと検索ボックス自体も非表示になるため、この処理が必要になります。

<img width="100%" alt="検索用のテキストボックスの機能の実装" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3633321/5d6aa5c4-0434-2d86-0a15-8e26c2170610.png">

　　　　　　　　　　　　　　　　　　＜検索用のテキストボックスの機能の実装＞

                  
```JavaScript

    // 各要素をループ処理
    $(".after_this")
      .nextAll()
      .each(function () {
        // 要素内のテキストを取得
        var yosoText = $(this).text().toLowerCase();

        // 入力された文字列が要素のテキストに含まれているか判定
        var isMatch = yosoText.includes(searchText);
        // 該当の要素を表示または非表示にする
        $(this).toggle(isMatch);
      });
  });

```


               

3.上記のJqueryのスクリプトを実行する方法は色々とありますが、[View Customize](https://github.com/onozaty/redmine-view-customize/blob/master/README.ja.md)がインストールされていれば、一番簡単にJqueryを適用できます。


* * *
### 感想






* 前回紹介した[JqueryでRedmineの「活動ページ」をもっと便利に②(フィルタリング機能と移動機能)](https://qiita.com/yam_asaki/items/2428610fd89bc73ce0ab)をwikiページに移植をした事例でした。
* 前回作成した「活動ページ」用のプログラムソースをwikiページに移植しましたが、思ったよりかなりの部分のプログラムソースを再利用することができました。
* しかし、当然のことながら「活動ページ」と「wikiページ」では、ページの構造が異なっているため、「wikiページ」に合わせたカスタマイズが必要でした。
* 目次の作成だけであれば、「{{>toc}}」を使用するのが一番簡単ですが、毎回、「{{>toc}}」を記述するのも面倒な場合は、View Customizeで事前にJqueryを適用させすれば、全てのwikiページに目次機能が追加されるので、多少の省力化に寄与できるのではと思います。
* 「{{>toc}}」では実現できない「トップに戻る」ボタンが追加されますが、ページが縦長になる場合、「トップに戻る」ボタンは思った以上に便利だと感じました。
* そもそもページを縦スクロールする理由は、wikiページに情報が多いからで、検索機能で必要な情報だけが表示されていれば、スクロールさせる回数も削減できるため、必要な情報により迅速にアクセスすることができます。
* プラグインやテーマをインストールする必要がない為に、Redmineのサーバー上でファイルの追加などをする必要がなく、サーバー上のファイルを変更するアクセス権限がない場合でも、手軽にRedmineのプチ改造が可能になります。

* * *
#### この記事の作成者の紹介
* * *
山崎進
* Redmine、Jquery、JavaScript,Rails、Ruby、SQL、VBA、RPAの開発を行なっています。
* 自動化、業務の効率化に高い関心があります。
* 下記の媒体で情報を発信しています。Redmineのプラグインの開発などに対応が可能ですので、お気軽にご連絡ください。

　* Qiita：https://qiita.com/ankosoft
　* Twitter：https://twitter.com/yamasaki24
　* Redmine Advent Calendarで記事投稿
　* redmine.tokyoで講演多数
　* Redmine Japan Vol.1 前夜祭、Redmine Japan Vol.3で講演
　* https://ankosoft.co.jp/blog/
　* https://technology.ankosoft.co.jp/

* * *
### 関連記事

 *　[JqueryでRedmineのメニューにアイコンを入れる方法](https://qiita.com/yam_asaki/items/32097598cb0a653dd916)

 *　[JqueryでRedmineのメニューを閉じたり開いたりする方法](https://qiita.com/yam_asaki/items/5ac4a1127beeed53059c)

　*　[JqueryでRedmineの「活動ページ」をもっと便利に(タイトルを開閉したり、曜日を入れたり)](https://qiita.com/yam_asaki/items/718513b52b30b30f65bc)

　*　[JqueryでRedmineの「活動ページ」をもっと便利に②(フィルタリング機能と移動機能)](https://qiita.com/yam_asaki/items/2428610fd89bc73ce0ab)

　*　[JqueryでRedmineの「wikiページ」をもっと便利に(フィルタリング機能と移動機能)](https://qiita.com/yam_asaki/items/a6a32c45dbbc72a28836)

　*　[JqueryでRedmineの「チケットページ」をもっと便利に(年ごと、月ごと、四半期ごとの集計機能の開発)](https://qiita.com/yam_asaki/items/176d07588793b3e66b5d)

　*　[RedmineのパスワードやログインIDを忘れた時の復旧方法](https://qiita.com/yam_asaki/items/6881c92b30dc85f589ad)

　*　[「rails console」を使ってRedmine上のスケジュールを一括変更する方法](https://qiita.com/yam_asaki/items/1d41dc8653773f3522e6)