親のカスタムフィールドが選択されたら、子のカスタムフィールドを有効化する(Redmine View Customize Plugin)

github.com

上記の問い合わせに対応したサンプルコードを書いてみました。

親のカスタムフィールド(キーバリュー形式のリスト)が選択されたら、子のカスタムフィールドを有効化するコードです。

Redmineのデフォルトのスタイルだと、input:disabledbackground-colorが設定されておらず、disabledになったことがわかりずらかったので、明示的にbackground-colorを変えるようにしています。(CSSにしても良かったけど、一つのサンプルコードにまとめたかったので、、)

設定内容

  • Path pattern: .*
  • Insertion position: Bottom of issue form
$(function() {

  const parentField = $('#issue_custom_field_values_1');
  const childFields = [
    $('#issue_custom_field_values_2'),
    $('#issue_custom_field_values_3'),
    $('#issue_custom_field_values_4')
  ];

  const changeEnable = function() {

    if (parentField.val() != '') {
      childFields.forEach(function(child) {
        child.prop('disabled', false);
        child.css('background-color', '');
      });
    } else {
      childFields.forEach(function(child) {
        child.prop('disabled', true);
        child.css('background-color', '#ebebe4');
      });
    }
  }

  parentField.change(changeEnable);

  changeEnable();
})

動作

f:id:onozaty:20200210001354g:plain

RDBMS上でデータの整合性を保つこと

先日Twitter上で外部キーが話題にあがっていました。自分も大昔は外部キーを重要視していませんでしたが、1x年以上たった今では、様々な制約等を使って、RDBMS上でデータの整合性を保つべきと考えています。

なぜ制約を使うのか

データの不整合を、プログラム側で検知するのは難しいです。プログラム側ではデータがこうあるべきといった定義ができないためです。
そこでデータの格納先であるRDBMS側でデータの整合性を保証することが重要です。

プログラムのバグなどで不整合なデータが格納されてしまうと、不整合なデータを格納しようとしたトランザクションだけでなく、システム全体に影響を及ぼすことになりかねません。制約等ではじければ、エラーとなるのは不整合なデータを格納しようとしたトランザクションだけで、システム全体に影響を及ぼすということを防げます。

また、制約として定義することによって、「アプリケーションの仕様的に、こういったデータが格納されることはない」っていうのではなく、スキーマとしてデータの仕様を可視化することができます。

制約の種類

RDBMSはデータの整合性を保つために、様々な制約が用意されています。

  • 主キー制約
  • 外部キー制約
  • 一意性制約
  • 非NULL制約
  • 検査制約

下記はPostgreSQLのマニュアルで各制約の説明をしたページです。他のRDBMSでも同じようなものだと思います。(MySQLでも最近検査(CHECK)制約が追加されましたね!)

検査制約では、関数呼び出しもできるので、複雑なチェック処理をFUNCTIONとして定義しておいて、それを呼びだしてチェックするといったこともできます。

また、一意制約でも関数を使えるので、大文字小文字関係なく一意であるといったような制約も付与できます。

※各制約がどういったものかは後で追記するかも

制約以外の方法

当たり前のことかもしれませんが、データの型を正しく利用するといったことも重要です。たとえば数値を文字列型(VARCHARやTEXT)に格納してしまうと、データとしての整合性を維持する手段を手放してしまっていることになります。

また、数種類の文字列しか入らないようなものは、ENUM(列挙型)を使うことによって、何がはいるのかを限定することができます。種別を数値で入れているようなところも、ENUMにした方がデータとしてわかりやすくなるかもしれません。
ただし、取りえる値が変わるようなものに対して、このような制約を付けるのはメンテナンス性を落とす可能性もあるのでご注意ください。(SQLアンチパターンでも触れられているところで、参照テーブルを用意してそちらとの外部キーで制限した方がよい)

制約を避けることについて

INSERTやUPDATE、DELETE時のパフォーマンスが落ちるから、、といった理由で外部キー制約や検査制約を避けることがあるというのを、ネット上で見かけたことがあります。

実際に制約がパフォーマンス的に問題を起こしているということがわかったならば、制約を外すなり、違った形での制約に変えるなりすればよいと思いますが、最初から避けるのはとてももったいないかな、、と思っています。データの整合性がRDBMS上で保証されているというのは、プログラムを書くうえで大きな安心を得られますし、生産性をあげるものだとも思っています。

カスタムフィールドを3カラムで表示する(Redmine View Customize Plugin)

上記の問い合わせの中で、カスタムフィールドを3カラム表示したいといったのがあったので、サンプルコードを書いてみました。

設定内容

  • Path pattern: .*
  • Insertion position: Bottom of issue form
$(function() {

  const field1 = $('#issue_custom_field_values_1');
  const field2 = $('#issue_custom_field_values_2');
  const field3 = $('#issue_custom_field_values_3');

  // Change layout
  const content = $('<div class="splitcontent">')
    .append($('<div class="splitcontentleft">').append(field1.parent()))
    .append($('<div class="splitcontentleft">').append(field2.parent()))
    .append($('<div class="splitcontentright">').append(field3.parent()));

  $('#attributes').append(content);
})

動作

f:id:onozaty:20200119224923p:plain

カスタムフィールドが大量にあって、少しでも詰めて表示したい、、って時には有用かもしれません。

特定のプロジェクトでファイル添付を無効にする(Redmine View Customize Plugin)

この記事はRedmine Advent Calendar 2019 19日目の記事です。

Redmine Users (Japanese)のメーリングリストで流れていた件で、既にJavaScriptで解決する方法が他の方から出ていましたが、CSSでもできるのでCSSで書いてみました。

設定内容

  • Path pattern: .*
  • Insertion position: Head of all pages

下記はaという識別子のプロジェクトに対して無効とする書き方です。

.project-a #issue-form #attachments_form, .project-a #issue-form .attachments_form {
  display: none;
}

動作

下記のような感じで非表示になります。

f:id:onozaty:20191215224743p:plain

チケットの編集時はちょっと微妙でタイトルまでは消しませんでした。ちょうど良いidやclassが振られていなくて、タイトル含めて消そうとすると #issue-form .filedroplistner fieldset:last-child で指定するくらいしかないのですが、必ず最後になるのか確信がもてなかったのでやめました。

f:id:onozaty:20191215224747p:plain

CSSの使いどころ

JavaScriptの方がいろいろなことができるので、View customizeのサンプルもJavaScriptを使ってものが多くなりますが、CSSの方がシンプルに書けることも多いので、シンプルなデザイン的なもの(今回の非表示のように)は、CSSで書くことが多いです。

プロジェクトの識別子のように、いろんな情報がHTML上に付与されているので、一度DOMインスペクタなどで眺めてみると良いと思います。

諭吉佳作/men

この1か月くらい、家では諭吉佳作/menさんの曲を聴きまくっている。それくらいハマっている。

最初に諭吉佳作/menさんを知ったのは、9月にNHK Eテレで放送された「前山田×体育のワンルーム☆ミュージック」だった。インタビューで、「打ち込みだったら腕が3本なければ叩けないような曲も作れる」(もっと違うニュアンスだったかも)ってとこが印象に残っている。

この時点では、名前は知っている(名前の印象はとても強い)くらいでしかない状態で、特に曲を聴いてみようとはならなかった。この時本人が歌っている曲が流れていたら、きっとこの時点ですぐにハマっていたと思う。
このとき番組で流れていたのは楽曲提供していたでんぱ組.incのMVだった。

そのあと、SpotifyでWeekly Buzzか何か聞いているときに、崎山蒼志くんの「むげん・ (with 諭吉佳作/men)」が流れて、この声って諭吉佳作/menさん!?ってなった。
Spotifyにはほとんど曲がなかったので、YouTube、SoundCloudで曲を聴いた。すごくいい。

曲自体も良いのだけど、そこに声が重なってさらに魅力的なものになっている。この曲とこの声が組み合わさることによる相乗効果というかなんというか。
どれもオリジナリティあふれる楽曲で、ビリー・アイリッシュを初めて聞いた時の感覚に似てる。(ビリー・アイリッシュの曲を初めて聞いた時も、とてもインパクト受けた)

一番のお気に入りは「洋装の語る今日は」で、リズミカルに流れる曲の上でさらに流れるような声、、すごくいい。

soundcloud.com

ちなみに、うちの娘たちは「の ぞ き」がお気に入りで、よく口ずさむようになった。

移動中などにも聞きたいので、Spotifyで配信(課金してるので、ネット接続なくても聴ける)するか、アルバム出して欲しいです。もしくはダウンロード販売でも。
同じように思っている人は既にいっぱいいそうなので、すぐ近い将来そうなるんだろうな。とても楽しみ。

プロジェクト切り替え時にウォッチャーを変更する(Redmine View Customize Plugin)

プロジェクト切り替え時にウォッチャーを変えたいのだけどうまくいかないといった問い合わせもらったので、サンプルコード書いてみました。

入力フォーム差し替えと絡んでハマりそうな箇所なので、他の方の参考にもなればと思います。

入力フォームの差し替え

プロジェクトやステータス、トラッカーを切り替えた際には、入力フォーム自体が差し替えられます。サーバ側を呼び出して、受け取ったHTMLの断片で差し替えるようなイメージです。

チケットの題名や説明、カスタムフィールドなどは、空の入力フォームを作った後に、もともと設定されていた情報に移し替えています。
ただ、ウォッチャーのところは、チェックの付与情報自体が付いたHTMLがサーバ側からかえってきて、それをそのまま差し替えるだけです。(サーバ呼び出しの際にフォームの情報も送っている)
そのため、サーバの呼び出しが行われた後に画面側の情報を変えてもそれが反映されません。

ということで、サーバ側呼び出しの前に画面側を変えてあげる必要がありますが、そうなるとイベントのキャプチャフェーズで拾ってあげる必要があります。jQueryではキャプチャフェーズを指定できないので、addEventListenerで指定する形となります。

設定内容

  • Path pattern: /issues/
  • Insertion position: Head of all pages
$(function() {

  const checkWatcher = function(userId) {
    if ($('#issue_watcher_user_ids_' + userId).length == 0) {
      // When the number of users exceeds 20, the check box is not initially displayed, so add a check box.
      const label = $('<label>').attr({
        id: 'issue_watcher_user_ids_' + userId,
        class: 'floating'
      }).append(
        $('<input>').attr({
          type: 'checkbox',
          name: 'issue[watcher_user_ids][]',
          value: userId
        })
      );

      $('#watchers_inputs').append(label);
    }
    
    $('#issue_watcher_user_ids_' + userId + ' input').prop('checked', true);
  }

  const changeWatcher = function() {

    // reset all watchers
    $('input[name="issue[watcher_user_ids][]"]').prop('checked', false);

    switch($('#issue_project_id').val()) {
      case '1':
        checkWatcher(1);
        break;

      case '2':
        checkWatcher(5);
        break;

      case '3':
        checkWatcher(6);
        break;
    }
  };

  const allAttributes = document.getElementById('all_attributes');
  if (!allAttributes) {
    return;
  }

  allAttributes.addEventListener(
    'change',
    function(e) {
      if (e.target.id == 'issue_project_id') {
        changeWatcher();
      }
    },
    // Capturing phase (To work before updateIssueFrom is executed)
    true);
});

追記@2022-12-25

ユーザ数が20を超えていた場合の考慮を追加しました。
20を超えると、ウォッチャーをチェックするためのチェックボックスが未選択のものだと表示されないため、うまく動きませんでした。

gitlab-project-member-exporter - GitLabからプロジェクト毎のメンバー一覧をエクスポートするツールを作った

この記事はGitLab Advent Calendar 2019の2日目の記事です。

GitLabからプロジェクト毎のメンバー一覧をエクスポートするツール、gitlab-project-member-exporter を作りました。

なぜ作ったか

GitLabでグループ、メンバーが多くなってくると、誰がどこにアクセス可能な状態なのかの把握が面倒になってきます。

適当なタイミングで確認しようとしても、プロジェクトやユーザの情報をGitLabの管理画面から見ていくしかありません。数プロジェクトならばどうにかなりそうですが、数十、数百となってくると、ページ辿るだけでも面倒な作業になります。

全プロジェクト×メンバの一覧ができてしまえば、確認が少しは楽になるだろうということで、今回ツールとして作りました。

できること

このツールを使うと、下記のようなCSVファイルをエクスポートできます。プロジェクトとそれに所属するメンバーの一覧になります。

実際の権限と同様に、グループに権限があった場合、その配下のプロジェクトに権限があるものとして一覧化しています。

プロジェクトメンバ一覧

Project: Name Project: Visibility User: Name User: Username User: State Permission
Administrator / my-project private Administrator root active Maintainer
group1 aaa / project-b private Administrator root active Owner
group1 aaa / project-b private user2 user2 active Developer
group1 aaa / project-a private Administrator root active Owner
group1 aaa / project-a private user2 user2 active Developer
group1 aaa / project-a private user1 user1 blocked Maintainer

また、ユーザ一覧、プロジェクト一覧もエクスポートできます。

ユーザ一覧

Name Username Email State Admin Created at Last sign in at Last activity on
user3 user3 user3@example.com active FALSE 2019-11-16T13:37:15.885Z
user2 user2 user2@example.com active FALSE 2019-11-16T13:36:29.651Z
user1 user1 user1@example.com blocked FALSE 2019-11-16T13:35:44.406Z
Administrator root admin@example.com active TRUE 2019-11-16T07:50:47.821Z 2019-11-24T14:50:54.996Z 2019-11-25

プロジェクト一覧

Name Visibility Created at Last activity at
Administrator / my-project private 2019-11-16T13:48:47.727Z 2019-11-16T13:48:47.727Z
group1 / project-b private 2019-11-16T13:48:11.161Z 2019-11-24T15:08:11.024Z
group1 / project-a private 2019-11-16T13:44:20.849Z 2019-11-16T13:44:20.849Z

これら情報を使うことによって、いちいちGitLabのページを辿らなくても、プロジェクトメンバーの状況が一覧で確認できます。
新しいユーザやプロジェクトが追加されたこともわかるので、それらに対してどのように権限が設定されているのか、、といったことも追うことができます。

定期的に出力して、差分を見るというのも良いと思います。

技術的なところ

GitLabのAPIを利用しています。

必要なリソースへアクセスするためのAPIは揃っており、特に悩むことはありませんでした。 (Paginationの情報がレスポンスのヘッダに乗ってくることを見逃していたくらい)

使っているAPIは下記でまとめています。

利用方法

利用方法については、下記READMEをご参照ください。

Javaで実装されています。必要な情報に過不足があるようでしたら、カスタマイズしてご利用ください。