RedmineのWikiでSQLを発行した結果を利用するマクロを作成する

RedmineのWikiにチケットの情報を自動的にまとめるようにしたい。Redmineのデータベースに対してSQLを発行した結果からHTMLを生成するマクロを作れば実現可能だと思い調査しました。私自身は、Redmine利用歴は長いですが、Ruby, Ruby on Rails, SQLに関してはすべて初心者(2日目)です。Redmineのマクロを作ってみたいという初心者向けの参考になれば幸いですが、ここに記載されている内容の正確性や安全性については保証できませんのでご注意ください。

# wikiで自作マクロを使用するための準備

今回は、wikiを作成しているプロジェクトに属するバージョンと、そのバージョン期日、オープンチケット(残チケット)数を示す表を作る。

自作マクロを作るためにはプラグインとしてマクロを組み込むことになるらしい。
[プラグインチュートリアル](http://guide.redmine.jp/Plugin_Tutorial/) によると、Redmineディレクトリでコマンドを実行することでテンプレートが作成される模様だが、理解できる最小の単位で実施するために最低限のファイルのみ生成する。

```
mkdir -p [Redmineインストールディレクトリ]/plugin/[プラグイン名のディレクトリ]
touch [Redmineインストールディレクトリ]/plugin/[プラグイン名のディレクトリ]/init.rb
```

init.rb にRedmineのプラグインページに載る情報などを記載する。
```ruby:init.rb
Redmine::Plugin.register :get_version_status do
  name 'Get current project status plugin'
  author 'akasa_y'
  description 'Add macro for Redmine wikl to get current project status'
  version '0.0.1'
  url 'http://example.com/path/to/plugin'
  author_url 'bar'
end
```

# 自作マクロの作成

続いてマクロ作成、登録部分を作成する。ここでは get_version_status というマクロ名にする。

```ruby:init.rg
  Redmine::WikiFormatting::Macros.register do
    desc "Get current project version and status"
    macro :get_version_status do |obj, args|
      # ここに処理を書く
    end
  end
```

上記に続けてSQL文を作って結果を取得する部分を作る。Ruby, SQL文に関しては色々間違っているかと思いますが、共に2日目なので今後レベルがあがるはず。

```ruby:init.rg
  Redmine::WikiFormatting::Macros.register do
    desc "Get current project version and status"
    macro :get_version_status do |obj, args|
      sqlstr = "SELECT versions.id, versions.name, versions.effective_date, issue_statuses.is_closed, COUNT(is_closed) AS count "
      sqlstr << "FROM issues LEFT OUTER JOIN issue_statuses ON issues.status_id = issue_statuses.id LEFT OUTER JOIN versions ON issues.fixed_version_id = versions.id "
      sqlstr << "WHERE versions.effective_date IS NOT NULL AND issue_statuses.is_closed = false AND issues.project_id = "
      sqlstr << @project.id.to_s
      sqlstr << " GROUP BY versions.name, issue_statuses.is_closed, versions.effective_date, versions.id "
      sqlstr << "ORDER BY versions.effective_date ASC"
      results = ActiveRecord::Base.connection.select_all(sqlstr)
    end
  end
```

あとは、取得したresultsを使ってHTMLを生成する部分を作るだけ。tr,tdのところはresults.columnsでキーも取得できるのですが力尽きたので手抜きしました。

```ruby:init.rg
  Redmine::WikiFormatting::Macros.register do
    desc "Get current project version and status"
    macro :get_version_status do |obj, args|
      sqlstr = "SELECT versions.id, versions.name, versions.effective_date, issue_statuses.is_closed, COUNT(is_closed) AS count "
      sqlstr << "FROM issues LEFT OUTER JOIN issue_statuses ON issues.status_id = issue_statuses.id LEFT OUTER JOIN versions ON issues.fixed_version_id = versions.id "
      sqlstr << "WHERE versions.effective_date IS NOT NULL AND issue_statuses.is_closed = false AND issues.project_id = "
      sqlstr << @project.id.to_s
      sqlstr << " GROUP BY versions.name, issue_statuses.is_closed, versions.effective_date, versions.id "
      sqlstr << "ORDER BY versions.effective_date ASC"
      results = ActiveRecord::Base.connection.select_all(sqlstr)
      str = "<table>"
      str << "<thead><tr><th>バージョン</th><th>期日</th><th>残チケット</th></tr></thead>"
      str << "<tbody>"
      results.each do |row|
        str << "<tr><td>" << link_to(row['name'],{:controller => 'versions', :action => 'show', :id => row['id']}, :class => 'version')  << "</td><td>" << row['effective_date'].to_s << "</td><td>" << link_to(row['count'], project_issues_path(@project, :fixed_version_id => row['id'], :set_filter => '1', :status_id => 'o')) << "</td></tr>"
      end
      str << "</tbody></table>"
      str.html_safe
    end
  end
```

link_toでRedmine上のリンクを生成しているが、ここの引数がまだよくわからない。
今は既存のRedmineソースコードから、該当しそうな箇所を探し出した。（ここなんとかならないだろうか…）

結果以下のようなファイルを生成した。

```ruby:init.rg
Redmine::Plugin.register :get_version_status do
  name 'Get current project status plugin'
  author 'akasa_y'
  description 'Add macro for Redmine wikl to get current project status'
  version '0.0.1'
  url 'http://example.com/path/to/plugin'
  author_url 'bar'

  Redmine::WikiFormatting::Macros.register do
    desc "Get current project version and status"
    macro :get_version_status do |obj, args|
      sqlstr = "SELECT versions.id, versions.name, versions.effective_date, issue_statuses.is_closed, COUNT(is_closed) AS count "
      sqlstr << "FROM issues LEFT OUTER JOIN issue_statuses ON issues.status_id = issue_statuses.id LEFT OUTER JOIN versions ON issues.fixed_version_id = versions.id "
      sqlstr << "WHERE versions.effective_date IS NOT NULL AND issue_statuses.is_closed = false AND issues.project_id = "
      sqlstr << @project.id.to_s
      sqlstr << " GROUP BY versions.name, issue_statuses.is_closed, versions.effective_date, versions.id "
      sqlstr << "ORDER BY versions.effective_date ASC"
      results = ActiveRecord::Base.connection.select_all(sqlstr)
      str = "<table>"
      str << "<thead><tr><th>バージョン</th><th>期日</th><th>残チケット</th></tr></thead>"
      str << "<tbody>"
      results.each do |row|
        str << "<tr><td>" << link_to(row['name'],{:controller => 'versions', :action => 'show', :id => row['id']}, :class => 'version')  << "</td><td>" << row['effective_date'].to_s << "</td><td>" << link_to(row['count'], project_issues_path(@project, :fixed_version_id => row['id'], :set_filter => '1', :status_id => 'o')) << "</td></tr>"
      end
      str << "</tbody></table>"
      str.html_safe
    end
  end
end
```

Redmine(httpd)を再起動してwikiで{{get_version_status}}と実行して結果を確認する。

# 動作確認

以下の3の環境で動作確認したところ、環境3が動かなかった。今日はもう疲れたので環境3については後日調べる。

* 環境1 OK
    * Redmine version                3.4.13.stable.21521
    * Ruby version                   2.4.1-p111 (2017-03-22) [x86_64-linux]
    * Rails version                  4.2.11.1
    * Database adapter               PostgreSQL
* 環境2 OK
    * Redmine version                3.3.2.stable
    * Ruby version                   2.3.3-p222 (2016-11-21) [x86_64-linux]
    * Rails version                  4.2.7.1
    * Database adapter               Mysql2
* 環境3 NG(Redmineが起動しない)
    * Redmine version                4.2.5.stable
    * Ruby version                   2.7.5-p203 (2021-11-24) [x86_64-linux]
    * Rails version                  5.2.6.3
    * Database adapter               Mysql2

# 所感

最初はSQLの叩き方が分かれば後はどうにかなるだろうと思っていましたが、生SQLを叩いているため 環境1(PostgreSQL環境)では動いて環境2(MariaDB環境)では動かないという事態になってしまった。生SQLを叩かずにDatabase adapter経由でSQLを叩く方法がきっとあると思うので、そこの調査をするべきだったかも。（databaseによって差が出ることを想定していなかった…）

微修正でどうにか環境1, 環境2では動いたが環境3は別の理由で起動していないようなので、近々また調べようと思う。

# 参考文献

以下の方々のページを参考にさせて頂きました。ありがとうございました。

* [Redmine 独自のWikiマクロを追加するプラグインの作成](https://symfoware.blog.fc2.com/blog-entry-1820.html)
* [Redmineのpluginの習作（wikiのマクロでsqlを発行し、それを埋め込む)](https://itdepends.hateblo.jp/entry/2018/11/25/140307)
* [RedmineでWikiページにカスタムクエリへのリンクを記述する](https://torutk.hatenablog.jp/entry/20161007/p1)
* [ActiveRecordで生SQLを使いたいときに便利なメソッド達](https://qiita.com/yut_h1979/items/4cb3d9a3b3fc87ca0435)