RedmineのJavaScriptから各種データを取得する方法

RedmineのJavaScriptからRedmineの内部データを取得する方法の中で、authenticity_tokenを使う方法と、REST APIを使う方法を紹介する。


# authenticity_tokenを使ったデータの取得
Redmine自身のフロントエンドが呼び出している機能を利用する。
各ページに埋め込まれている`authenticity_token`が必要。


## authenticity_tokenの取得
`authenticity_token`はRedmineの全てのページには埋め込まれているわけではない。
何かしらのデータ作成や更新の機能があるページに埋め込まれている模様。

```js
$(':input[name="authenticity_token"]:first').val()
```

下記ページには埋め込まれているのを確認した。他にもあると思う。

* グローバルメニュー
    * 「マイページ」
    * 「プロジェクト」 > 新しいプロジェクト
    * 「個人設定」
* プロジェクトメニュー
    * 「概要」 > 新しいサブプロジェクト
    * 「ロードマップ」
    * 「リリース」 > 新しいリリース
    * 「チケット」
    * 「新しいチケット」
    * 「Wiki」
    * 「文書」
    * 「ニュース」
    * 「ファイル」 > 新しいファイル
    * 「設定」
    * 既存のチケット詳細
    * 作業時間の記録


## チケットに入力可能な値の一覧の取得
Redmine自身がチケット入力フォームを部分更新する際に使用している`update_form.js`を利用して、チケット入力フォームのHTMLと、HTMLをパースすることでチケットに入力可能なステータスなどの値を取得する。

ここで取得した`htmlIssueForm`をjQueryセレクターの第2引数に渡すことで、**`<input>`や`<select>`に入力されているパラメータを抽出することも**、**このHTMLをそのまま利用して自前のチケット情報入力フォームのパーツに流用することもできる**。

```js
var project_identifier = "redmine-test";
var issue_id = '7'; // if create new issue, set blank.
$.post(
    '/projects/' + project_identifier + '/issues/update_form.js' + (issue_id ? 'id=' + issue_id : ''),
    {utf8:"✓", authenticity_token:$(':input[name="authenticity_token"]:first').val()}
).done(function(data){
    eval(data.split("\n")[0].replace(/^replaceIssueFormWith\(/, 'var htmlIssueForm=').replace(/\);$/, ';'));
    // デバッグ表示：HTMLフォーム
    console.log(htmlIssueForm);
    // デバッグ表示：入力項目の抽出
    console.log($(":input", $(htmlIssueForm)));
});
```

*解説すると少しトリッキーなことをしている。（Redmineのバージョンアップで死ぬ可能性がままある）*

*このレスポンス（data）にはチケット編集のHTMLフォームを置き換えるJavaScriptコードが含まれる。
このJavaScriptの本来の使われ方は、例えばトラッカーが変化するとトラッカーごとに入力フォームの入力項目が変わったり、ユーザーのロールによって選択可能なステータスが変わるなどのリアルタイムなHTMLフォームの変化に用いられている。*

*上記の処理はそのHTMLフォームの置き換えコードを正規表現で無理矢理に変数`htmlIssueForm`への代入に変更してeval()実行している。
これにより置き換えに使われるはずだったHTMLがアクセス可能な変数`htmlIssueForm`に代入される。*


# REST APIを使ったデータの取得
[Rest api - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_api)

JavaScriptからREST APIでよく取得したくなるデータを抜粋した。

ここでは取得方法のみを軽く書いたが、APIによっては新規作成も更新もできる。
詳細はリンクしているREST APIドキュメントを参照して欲しい。

またJavaScriptからREST APIを呼び出す記事は別途作成した。

[RedmineのJavaScriptからREST APIを使用する](https://qiita.com/forenoonM/items/6121399cb9f78b8da9ca)

## ログインユーザーのapikeyの取得
apikeyを取得するような関数は存在しないためログインユーザーの「個人設定」をスクレイピングして取得する。
（下記コードは、当然ながらRedmineのバージョンアップなどで動かなくなる可能性はある）

```js
// Redmineの全バージョンに対応。
// REST APIがONになっていなければapikeyには空文字が入る。
let apikey = "";
$.get('/my/account').done(function(data){
     apikey = $("#api-access-key", $(data)).first().text();
});

// Redmine 3.1以上の低負荷版。専用ページなので読み込み負荷は若干低くなるはず。
// REST APIのON/OFFに関わらずapikeyにはAPIキーの文字列が入る。
let apikey = "";
$.get('/my/api_key').done(function(data){
    apikey = $('#content > div.box > pre', $(data)).first().text();
});
```


## プロジェクトデータの取得
### プロジェクト
[Projects - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_Projects)

```js
var apikey = '****************************************';

// プロジェクト一覧
$.ajax({
    type: 'get',
    url: '/projects.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));

// プロジェクト詳細
var project_identifier = "redmine-test";

$.ajax({
    type: 'get',
    url: '/projects/' + project_identifier + '.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```

### カテゴリー
[Issue Categories - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_IssueCategories)

```js
var apikey = '****************************************';
var project_identifier = "redmine-test";

$.ajax({
    type: 'get',
    url: '/projects/' + project_identifier + '/issue_categories.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```

### バージョン
[Versions - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_Versions)

```js
var apikey = '****************************************';
var project_identifier = "redmine-test";

$.ajax({
    type: 'get',
    url: '/projects/' + project_identifier + '/versions.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```

### Wiki
[Wiki Pages - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_WikiPages)

```js
var apikey = '****************************************';
var project_identifier = "redmine-test";

// ページ一覧
$.ajax({
    type: 'get',
    url: '/projects/' + project_identifier + '/wiki/index.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));

// ページ詳細
var page_name = 'Wiki';

$.ajax({
    type: 'get',
    url: '/projects/' + project_identifier + '/wiki/' + page_name + '.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```


## チケットデータの取得
### チケット
[Issues - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_Issues)

```js
var apikey = '****************************************';

// チケット
$.ajax({
    type: 'get',
    url: '/issues.json',
    headers: {'X-Redmine-API-Key': apikey},
    data: {project_id:1, limit:100, include:'relations'},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));

// ページ詳細
var issue_id = '15781';

$.ajax({
    type: 'get',
    url: '/issues/' + issue_id + '.json',
    headers: {'X-Redmine-API-Key': apikey},
    data: {include:'children,attachments,relations,changesets,journals,watchers'},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```

### 関連するチケット
[Issue Relations - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_IssueRelations)

```js
var apikey = '****************************************';
var issue_id = '16894';

$.ajax({
    type: 'get',
    url: '/issues/' + issue_id + '/relations.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```


## システム定義データの取得
### トラッカー
[Trackers - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_Trackers)

```js
var apikey = '****************************************';

$.ajax({
    type: 'get',
    url: '/trackers.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```

### チケットステータス
[Issue Statuses - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_IssueStatuses)

```js
var apikey = '****************************************';

$.ajax({
    type: 'get',
    url: '/issue_statuses.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```

### 列挙項目
[Enumerations - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_Enumerations)

```js
var apikey = '****************************************';

$.ajax({
    type: 'get',
    url: '/enumerations/issue_priorities.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```

### カスタムフィールド
[Custom Fields - Redmine](http://www.redmine.org/projects/redmine/wiki/Rest_CustomFields)
**※システム管理者のapikeyが必要**

```js
var apikey = '****************************************';

$.ajax({
    type: 'get',
    url: '/custom_fields.json',
    headers: {'X-Redmine-API-Key': apikey},
    dataType: 'json',
    contentType: 'application/json',
}).done((json) => console.log(json));
```