概要
GitBucket は、GitHub のようにネットワーク上で Git リポジトリを共有できる Web サービスです。
- Apache-2.0 ライセンスで配布されている。
- Scala で開発されており、Java 17 実行環境があれば、起動可能。
- お試しであれば、内臓のH2データベースエンジンが利用できる。
- 実運用には、PostgreSQL や MariaDB などのリレーショナルデータベースサーバーの利用を推奨
- 非常に簡単にセルフホストが可能です。
現在、GitBucket の issue に以下の項目を設定可能にし、ガントチャートへ反映できる GitBucket Flexible Gantt Plugin を開発中です。
- 開始日
- 終了日
- 進捗
- 依存 issue (issue 番号をカンマ区切りで複数指定可能)
今回は、0.3.0 で依存 issue の入力支援ダイアログを追加した話です。
前回の記事
今回実装した機能の動作
編集状態にすると以下のように Dependencies のテキストボックスの右側に
のようなボタンが表示されます。
このボタンをクリックすると次のようなダイアログが表示されます。
ここから依存する issue を選択します。
Update ボタンをクリックすると以下のように Dependencies のテキストボックスに選択した issue の番号が、カンマ区切りで入力されます。
なお、Search issues ダイアログの上の方にある Search というラベルのついたテキストボックスに検索したい文字列を入力し、すぐ右にある虫眼鏡のアイコンをクリックすると issue が絞り込まれます。
大文字・小文字は区別しません。
既に Dependencies のテキストボックスに issue の入力がされている場合にダイアログを表示すると該当する issue のチェックボックスが選択された状態で表示されるようにしています。
以下、修正要領の説明です。
今回は、Twirl テンプレートである src/main/twirl/flexiblegantt/issuesidebar.scala.html のみの修正です。
参照ボタンを追加
参照ボタン用の HTML を追加しました。元々 GitBucket に同梱されている bootstrap の機能と octicons のアイコンを活用します。
src/main/twirl/flexiblegantt/issuesidebar.scala.html 62行目~
<button id="refIssue" type="button" class="btn" data-toggle="modal" data-target="#issueListDialog" style="display: none;">
<i class="menu-icon octicon octicon-issue-opened"></i>
</button>
なお、閲覧モード⇔編集モード切替時に表示・非表示を切り替える処理を追加していますが、割愛します。
ダイアログを追加
ダイアログ用の HTML を追加します。bootstrap の機能により、ダイアログを開く部分は、プログラム不要です。
src/main/twirl/flexiblegantt/issuesidebar.scala.html 78行目~
<div class="modal fade" id="issueListDialog" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
<h4 class="modal-title">Search issues</h4>
</div>
<div class="modal-body">
<div>
<label>Search</label>
<input type="text" id="searchIssueText">
<button id="searchIssue" class="btn btn-default"><i class="menu-icon octicon octicon-search"></i>></i></button>
</div>
<div id="issueListBody" style="height: 300px; overflow-y: scroll; border: 1px solid #ccc;">
</div>
</div>
<div class="modal-footer">
<button id="gfgUpdate" type="button" class="btn btn-primary">Update</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
issue のリストを取得する
GitBucket の API を利用し、issue の一覧を取得し、ダイアログに追加します。この際、現在開いている issue は除外するようにしています。
src/main/twirl/flexiblegantt/issuesidebar.scala.html 187行目~
const getIssues = function() {
const issueUrl = "@context.path/api/v3/repos/@owner/@repositoryName/issues";
fetch(issueUrl)
.then((res) =>{
if(res.ok) {
return res.json();
} else {
console.log(res);
}
})
.then(data => {
var currentIssue = parseInt("@issueId");
data
.filter((value, index) => {
return value.number != currentIssue;
})
.sort((a, b) => a.number -b.number)
.forEach((issue, index) => {
if (issue.state === "open") {
var panel = document.createElement("div");
panel.classList.add("panel","panel-primary");
var panelBody = document.createElement("div");
panelBody.classList.add("panel-body");
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("data-id", issue.number);
panelBody.appendChild(checkbox);
var span = document.createElement("span");
span.textContent = `#${issue.number} ${issue.title}`;
panelBody.appendChild(span);
panel.appendChild(panelBody);
issueListBody.appendChild(panel);
}
});
})
};
こちらは、document の DOMContentLoaded イベントのハンドラーに呼び出しを追加しています。
参照ボタンクリック時に Dependencies テキストボックスの内容を反映する
- テキストボックスが空でなければ、テキストをカンマで分割し、各チェックボックスに紐づいている issue 番号と比較し、チェックボックスの状態を変更します。
- テキストボックスが空であれば、各チェックボックスのチェックをはずします。
src/main/twirl/flexiblegantt/issuesidebar.scala.html 232行目~
refIssue.addEventListener('click', (e) => {
if (dependencies.value.length > 0) {
var idList = dependencies.value.split(",");
issueListBody.querySelectorAll("input[type='checkbox']").forEach((node, index) =>{
var dataId = node.getAttribute("data-id");
node.checked = idList.includes(dataId);
});
} else {
issueListBody.querySelectorAll("input[type='checkbox']").forEach((node, index) => {
node.checked = false;
});
}
}, true)
虫眼鏡のアイコンをクリックした際に issue を検索文字列で絞り込む
- 検索文字列用のテキストボックスの値を小文字に変換し、変数 target に代入
- 一旦、issue 用のパネルをすべて表示状態にする
- target が 0 文字以上の場合
- 各パネルを走査し、issue のタイトルを保持する
<span>タグの textContent を小文字に変換し、target の文字列が含まれるか確認し、含まれなければ、非表示にする。
- 各パネルを走査し、issue のタイトルを保持する
- target が 0 文字の場合
- issue 用のパネルをすべて表示状態にする
src/main/twirl/flexiblegantt/issuesidebar.scala.html 245行目~
searchIssue.addEventListener('click', (e) => {
var target = searchIssueText.value.toLowerCase();
showAllIssueListBody();
if (target.length > 0) {
issueListBody.querySelectorAll(".panel").forEach((node, index) =>{
if (!node.querySelector("span").textContent.toLowerCase().includes(target)) {
node.style.display = "none";
}
});
} else {
showAllIssueListBody();
}
}, true);
Update ボタンクリック時に Dependencies テキストボックスの内容を更新する
- issue 番号のリストを格納する空のリスト idList を準備
- 各チェックボックスを走査し、チェックされていれば、idList に追加
- idList をカンマ区切りで結合し、テキストボックスに設定
- ダイアログを非表示にする
src/main/twirl/flexiblegantt/issuesidebar.scala.html 258行目~
document.querySelector("#gfgUpdate").addEventListener('click', (e) => {
var idList = [];
//issueListBody
issueListBody.querySelectorAll("input[type='checkbox']").forEach((node, index) =>{
if (node.checked) {
idList.push(node.getAttribute("data-id"));
}
});
dependencies.value = idList.join(",");
$('#issueListDialog').modal('hide');
});




