2019 年 5 月のメンテナンスで追加される Garoon API の紹介

目次

caution
警告

Moment.js はメンテナンスモードになり、 日付処理できる代替ライブラリへの移行 (External link) が推奨されています。
代替ライブラリのひとつ Luxon (External link) については、 kintone カスタマイズでの導入方法の紹介記事 があります。

はじめに

2019/05/12 の定期メンテナンスのお知らせ で告知した Garoon の 5 月アップデートで、 Garoon API が複数追加されます。

この記事では、追加される Garoon API のうち、次の API を使ったカスタマイズ例を紹介します。

ファイルキーから画像 URL を取得する(画像アセット)

画像アセット

「画像アセット」は、画像ファイルを Garoon の共有リソースとして保存する機能です。

画像アセットでは、画像ファイルをファイルキーと一緒に登録します。
そのあと、利用したい画像のファイルキーを指定し、 ファイルキーから画像 URL を取得する API を使うと、画像の URL を取得できます。

「画像アセット」を使うメリット

これまで、Garoon のカスタマイズで画像を使う場合、「ファイル管理」アプリケーションに画像ファイルを保存していました。
そして、保存した画像ファイルのアドレス情報(URL)を右クリックで確認し、カスタマイズファイルの画像 URL を書き換える必要がありました。

今回追加された ファイルキーから画像 URL を取得する API を使うと、画像ファイルの URL を自動で取得できるため、カスタマイズファイルの画像 URL を書き換える必要がなくなります!

これにより、カスタマイズの手順は下図のようにぐっと簡略化されます。

「画像アセット」を使ったポータルカスタマイズ

完成例

スケジュールに登録された、自分の予定と施設を表示するポートレットを作ります。
このポートレットでは、いまの予定や 30 分以内の次の予定について、予定名や時間、施設の画像を表示します。

カスタマイズ手順

手順 1.「画像アセット」に画像ファイルを登録する

ポートレットに表示する施設の画像ファイルを準備し、「画像アセット」アプリケーションに追加します。

  1. [Garoon システム管理]から、[各アプリケーションの管理]タブを選択し、[画像アセット]を選択します。
  2. [画像アセット一覧]を選択し、[画像アセットを追加する]をクリックします。

  3. 「画像アセットの追加」画面で、[ファイルを添付]をクリックし、施設の画像ファイルを指定します。
    次のように入力します。

    項目名
    ファイルキー ファイルキーから画像 URL を取得する API で画像を指定するためのキーです。
    今回のカスタマイズでは、施設情報の施設コードをキーに画像を指定するので、ファイルキーには「施設コード」を入力します。

    「施設コード」の確認は、[Garoon システム管理] > [各アプリケーションの管理] > [スケジュール] > [施設/施設グループ] で確認できます(参考: 施設を管理する (External link)
  4. [追加する]ボタンをクリックします。
  5. 同様の手順で、ポートレットに表示する施設の画像ファイルすべてを、画像アセットに登録します。
手順2. カスタマイズファイルの準備

今回のカスタマイズでは、次の 2 つのカスタマイズファイルを利用します。
また、カスタマイズファイル内では、 Cybozu CDN に公開されている Moment.js と jQuery を利用しています。

  • カスタマイズファイル
    • ポートレットを表示する JavaScript ファイル(next_room_main.js)
    • ポートレットに適用する css ファイル(next_room.css)
  • 外部ライブラリ
    • Moment.js 2.10.3
      https://js.cybozu.com/momentjs/2.10.3/moment-with-locales.min.js
    • jQuery 3.3.1
      https://js.cybozu.com/jquery/3.3.1/jquery.min.js
カスタマイズファイル
  • ポートレットを表示する JavaScript ファイル(next_room_main.js)
    Garoon の予定を取得し、予定から HTML を生成するスクリプトファイルです。

    • 今回のポイントは、97 行目の garoon.assets.images.getUrl(facility.code) です。
      garoon.assets.images.getUrl は、画像アセットに登録された画像について、ファイルキーから画像 URL を取得する API です。
      さきほど画像アセットに登録するとき、ファイルキーに「施設コード」を設定しました。
      そのため、API に「施設コード」を指定し、施設の画像 URL を取得しています。

        1
        2
        3
        4
        5
        6
        7
        8
        9
       10
       11
       12
       13
       14
       15
       16
       17
       18
       19
       20
       21
       22
       23
       24
       25
       26
       27
       28
       29
       30
       31
       32
       33
       34
       35
       36
       37
       38
       39
       40
       41
       42
       43
       44
       45
       46
       47
       48
       49
       50
       51
       52
       53
       54
       55
       56
       57
       58
       59
       60
       61
       62
       63
       64
       65
       66
       67
       68
       69
       70
       71
       72
       73
       74
       75
       76
       77
       78
       79
       80
       81
       82
       83
       84
       85
       86
       87
       88
       89
       90
       91
       92
       93
       94
       95
       96
       97
       98
       99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      
      /**
      * Garoon sample program
      * schedule room portlet (main)
      * Copyright (c) 2019 Cybozu
      *
      * Licensed under the MIT License
      */
      
      (function() {
        'use strict';
        const myJQuery = jQuery.noConflict(true);
        (function($) {
      
          // ログインユーザの今日の予定を取得する
          const fetchTodayEvents = function(now) {
            const start = now.clone().hour(0).minute(0).seconds(0).toISOString();
            const end = now.clone().hour(23).minute(49).seconds(59).toISOString();
            const url = '/api/v1/schedule/events?rangeStart=' + start + '&rangeEnd=' + end;
            return garoon.api(url, 'GET', {}).then((resp) => {
              return resp.data;
            });
          };
      
          // 現在の予定か30分以内の予定の場合を対象とする
          const seekNearyEvents = function(events, now) {
            const enableEvents = [];
            const nextTime = now.clone().add(30, 'minutes');
      
            for (let i = 0; i < events.length; i++) {
              const event = events[i];
              // 終日イベントは除外
              if (event.eventType === 'ALL_DAY') {
                continue;
              }
              const start = event.start.dateTime;
              const end = event.end.dateTime;
              // 時間未指定のイベントを除外
              if (start === now.format('YYYY-MM-DD') + 'T00:00:00+09:00' && end === now.format('YYYY-MM-DD') + 'T23:59:59+09:00') {
                continue;
              }
              // 現在の予定か、30分以内の予定の場合を対象とする
              if (now.isBetween(moment(start), moment(end))) {
                event.isCurrent = true;
                enableEvents.push(event);
                continue;
              }
              if (nextTime.isAfter(moment(start)) && nextTime.isBetween(moment(start), moment(end))) {
                event.isCurrent = false;
                enableEvents.push(event);
                continue;
              }
            }
            return enableEvents;
          };
      
          moment.locale('ja');
          const now = moment();
      
          const $title = $('<h2></h2>').text('現在時刻 ' + now.format('HH:mm'));
          $('.next_room').append($title);
          const $contents = $('<div></div>').addClass('next_room_contents');
          $('.next_room').append($contents);
      
          return fetchTodayEvents(now).then((resp) => {
            const events = seekNearyEvents(resp.events, now);
            // 予定がない場合
            if (!events) {
              const $subject = $('<p></p>').addClass('next_room_subject');
              $subject.text('なし');
              $contents.append($subject);
              return;
            }
      
            for (let i = 0; i < events.length; i++) {
              const event = events[i];
              const $event = $('<div></div>').addClass('next_room_event');
              const $subject = $('<p></p>').addClass('next_room_subject');
              let subjectText = event.subject + ' (' +
                  moment(event.start.dateTime).format('HH:mm') + ' ~ ' +
                  moment(event.end.dateTime).format('HH:mm') + ')';
              if (event.isCurrent) {
                subjectText = '現在の予定:' + subjectText;
              } else {
                subjectText = '次の予定:' + subjectText;
              }
              $subject.text(subjectText);
              $event.append($subject);
      
              const $list = $('<div></div>').addClass('next_room_facilities');
              for (let j = 0; j < event.facilities.length; j++) {
                const facility = event.facilities[j];
                const $item = $('<div></div>').addClass('facilitiy_item');
                const $facilityName = $('<div></div>').addClass('facilitiy_name');
                const $facilityImage = $('<div></div>').addClass('facilitiy_image');
      
                $facilityName.text(facility.name);
                const imgUrl = garoon.assets.images.getUrl(facility.code);
                $facilityImage.html('<img src="' + imgUrl + '" width="150" height="auto"></img>');
      
                $item.append($facilityName);
                $item.append($facilityImage);
                $list.append($item);
              }
              $event.append($list);
              $contents.append($event);
            }
          });
        })(myJQuery);
      })();
  • ポートレットに適用する css ファイル(next_room.css)
    ポートレットに適用するスタイルシートファイルです。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    .next_room {
      background-color: #b1d1d6;
      padding: 10px;
    }
    
    .next_room > h2 {
      background-color: #F9E5DA;
      padding: 10px;
      font-weight: bold;
      font-size: 16px;
    }
    .next_room .next_room_contents {
      font-size: 12px;
      padding: 0;
    }
    
    .next_room_subject {
      font-weight: bold;
    }
    
    .next_room_event {
      padding: 3px 10px 5px;
      margin: 5px;
      background-color: #d3e5e8;
    }
    
    .next_room_facilities {
      margin: 0px auto;
      padding: 0;
      width: 100%;
      list-style-type: none;
      display: flex;
      flex-wrap: wrap;
    }
    
    .next_room_facilities .facilitiy_item {
      padding: 5px;
      background-color: #f2f2f2;
    }
手順3. カスタマイズの適用
  1. [Garoon システム管理]から、[各アプリケーションの管理]タブを選択し、[ポータル]を選択します。
  2. [HTML ポートレット]を選択し、[HTML ポートレットを追加する]をクリックします。
  3. 次のように入力します。HTML ポートレット設定の詳細は、 HTMLポートレットの設定 (External link) を参照してください。
    項目名
    ポートレット名 ポートレット名を入力します。
    今回は「次の予定ポートレット」としました。
    グループ 適宜設定してください。
    My ポータル 適宜設定してください。
    ポートレットの内容 「テキスト」を選択し、次の内容を入力してください。
    <div class="next_room">
    </div>
  4. 入力が完了したら、[追加する]ボタンをクリックし、ポートレットを追加します。
  5. HTML ポートレットの一覧から、先ほど追加したポートレットを選択します。
  6. [JavaScript/CSS によるカスタマイズ]をクリックします。
  7. 次のように入力します。入力後、[設定する]ボタンをクリックします。

    項目名
    カスタマイズ 適用する
    JavaScript カスタマイズ 次の順でリンクおよびファイルを追加します。
    • https://js.cybozu.com/momentjs/2.10.3/moment-with-locales.min.js
    • https://js.cybozu.com/jquery/3.3.1/jquery.min.js
    • ポートレットを表示する JavaScript ファイル(next_room_main.js)
    CSS カスタマイズ 次のファイルを追加します。
    • ポートレットに適用する css ファイル(nextRoom.css)
  8. 追加したポートレットをポータルに追加します。
    ポータルへの追加方法は「 ポートレットを配置する (External link) 」を参照してください。

手順は以上です。

「予定の登録」「予定の変更」画面の登録/変更実行前イベント(スケジュール)

「予定の登録」画面や「予約の変更」画面で、「登録する」または「変更する」ボタンをクリックした後に発生するイベントです。

このイベントによって、「入力内容のエラーチェック」や「定例ミーティングの場合はメモ欄にアジェンダのテンプレートを追加する」といった処理ができるようになりました。

information

2019 年 5 月現在、このイベントは Promise 対応していません。そのため、非同期処理の実行を待ってから登録または変更処理の実行はできません。

「予定の登録画面/予約の変更画面の登録前実行イベント」を使ったカスタマイズ例

完成例

予定を登録するときうっかり施設を設定し忘れて、当日打ち合わせする場所がない!となってしまったことはありませんか?
そんなうっかりミスを防ぐため、予定の登録または変更時に、施設の指定がなければエラーにするカスタマイズを紹介します。

カスタマイズ手順

手順1. カスタマイズファイルの準備

今回のカスタマイズでは、次のカスタマイズファイルを利用します。

  • カスタマイズファイル
    • 施設の設定有無をチェックする JavaScript ファイル(check_set_facility.js)
カスタマイズファイル
  • 施設の設定有無をチェックする JavaScript ファイル(check_set_facility.js)
    Garoon 予定の登録または変更時に、施設が設定されていなければエラーにして予定を登録させないスクリプトファイルです。
    • 今回のポイントは、9 行目と 11 行目です。

      • エラーダイアログを表示し予定を登録(変更)させないようにするには、イベントオブジェクトのエラープロパティに、表示したいエラーメッセージを設定します。
        今回は、施設の設定がされていない場合、つまり 施設に関するプロパティ の長さが 0 なら、エラープロパティに値を設定します。
      • 最後にイベントオブジェクトを返却して event の内容を Garoon に反映させます。
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      
      (function() {
        'use strict';
      
        const events = ['schedule.event.create.submit', 'schedule.event.edit.submit'];
        garoon.events.on(events, (event) => {
          const scheduleEvent = event.event;
          const facilities = scheduleEvent.facilities;
          if (facilities.length === 0) {
            event.error = '施設を指定してください。';
          }
          return event;
        });
      })();
手順2. カスタマイズの適用
  1. [Garoon システム管理]から、[各アプリケーションの管理]タブを選択し、[スケジュール]を選択します。
  2. [JavaScript/CSS によるカスタマイズ]を選択し、[カスタマイズグループを追加する]をクリックします。
  3. 次のように入力します。入力後、[追加する]ボタンをクリックします。

    項目名
    カスタマイズ 適用する
    カスタマイズグループ名 カスタマイズグループ名を入力します。
    適用対象 カスタマイズを適用したい対象(ユーザー、組織またはロール)を選択します。
    JavaScript カスタマイズ カスタマイズファイルを追加します。
    • 施設の設定有無をチェックする JavaScript ファイル(check_set_facility.js)
    CSS カスタマイズ なし

手順は以上です。

おわりに

2019 年 5 月の定期メンテナンスで、Garoon API が複数登録され、Garoon カスタマイズの幅が広がりました。

この記事で紹介している API 以外にも、ワークフローのフィールドを編集可/不可に設定できたり、在席情報を取得・更新できる API なども追加されています。

これからも Garoon API は追加される予定なので、Garoon カスタマイズに挑戦して、運用に合わせてより使いやすくしていただければと思います。

information

この Tips は、2019 年 5 月版 Garoon で動作を確認しています。