ざんねんな行動予定表を Garoon ポータルに移行してみた

目次

caution
警告

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

はじめに

ざんねんな情報共有ずかん (External link) でも紹介された、ホワイトボードを使って共有している行動予定表を、
Garoon のポートレット機能を使用して、Garoon ポータルに作成します。

完成イメージ

現在より後の外出に関する予定が表示される行動予定表を作成していきます。
「帰社予定時刻」や「現在の予定」も表示されるようになっています。

前提条件と注意事項

  • 「帰社予定時刻」を表示するためには、「帰社」という名前の予定メニューを追加する必要があります。
  • 「現在の予定」には現在の時刻に該当する予定が表示されます。
  • 組織が設定されていないと行動予定表は表示されません。
  • このカスタマイズには、クラウド版 Garoon の環境が必要です。
  • サンプルでは一部の機能で Garoon がサポートしない DOM 操作をしています。Garoon のアップデートにより、機能を利用できなくなる可能性があります。

ファイルの準備

whiteBoard.js

以下のサンプルコードを作成し、ファイル名を「whiteBoard.js」、文字コードを「UTF-8」で保存します。

  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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*
 * Garoon white board sample program
 * Copyright (c) 2019 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
*/

(function($) {
  'use strict';
  // moment の設定を日本にする
  moment.locale('ja');

  function makeXMLHeader(service, action) {
    const xmlns = 'soap="http://www.w3.org/2003/05/soap-envelope"';
    return (
      '<?xml version="1.0" encoding="UTF-8"?>' +
        '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" ' +
        'xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
        'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
        'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" ' +
        'xmlns:' + xmlns + '>' +
        '<SOAP-ENV:Header>' +
        '<Action SOAP-ENV:mustUnderstand="1" ' +
            'xmlns="http://schemas.xmlsoap.org/ws/2003/03/addressing">' + action + '</Action>' +
        '<Timestamp SOAP-ENV:mustUnderstand="1" Id="id" ' +
            'xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">' +
            '<Created>2037-08-12T14:45:00Z</Created>' +
            '<Expires>2037-08-12T14:45:00Z</Expires>' +
        '</Timestamp>' +
        '<Locale>jp</Locale>' +
        '</SOAP-ENV:Header>'
    );
  }

  function runAjax(url, method, data, token) {
    const settings = {
      type: method,
      url: url,
      data: data,
    };

    settings.headers = {
      'Content-Type': 'text/xml; charset=UTF-8',
      'X-Cybozu-API-Token': 'XMLHttpRequest'
    };

    // Promiseでラップする
    return new garoon.Promise((resolve, reject) => {
      $.ajax(settings)
        .done(resolve)
        .fail(reject);
    });
  }

  // SOAP API BaseGetUsersById を実行する
  function getBaseGetUsersById() {

    const xmlGetUsers = makeXMLHeader('base', 'BaseGetUsersById') +
    '<SOAP-ENV:Body>' +
        '<BaseGetUsersById>' +
          '<parameters>' +
            '<user_id>' + garoon.base.user.getLoginUser().garoonId + '</user_id>' +
          '</parameters>' +
        '</BaseGetUsersById>' +
    '</SOAP-ENV:Body></SOAP-ENV:Envelope>';

    // ユーザーの詳細情報を取得する
    return runAjax('/g/cbpapi/base/api.csp', 'POST', xmlGetUsers)
      .then((resUsers) => {
        return resUsers;
      }).catch((err) => {
        return err;
      });
  }

  // garoon.api() を使って、レコード検索を一括で行う
  // 途中でエラーが発生した場合でも処理は続行する
  function fetchDatas(aryobj, opt_datas, pidx) {
    let allDatas = opt_datas || [],
      idx = pidx || 0;
    return garoon.api(aryobj[idx], 'GET', {}).catch((err) => {
      return err;
    }).then((resp) => {
      allDatas = allDatas.concat(resp);
      idx += 1;
      if (aryobj.length !== idx) {
        return fetchDatas(aryobj, allDatas, idx);
      }
      return allDatas;
    });
  }

  // 日時から日付または時刻を返す
  function getDateOrTime(dt) {
  // dt が当日だったら、時刻を返す
    if (moment(dt).format('YYYYMMDD') === moment().format('YYYYMMDD')) {
      return moment(dt).format('HH:mm');
    }

    // dt が当日でなければ、日付を返す
    return moment(dt).format('MM/DD');
  }

  // 行動掲示板を作成する
  function makeOutBoard(users, psch) {
    let sch, starthhmm, endhhmm;

    $('.schtr').remove();
    // 画面更新時の日時を表示
    $('#currenttime').html(moment().format('YYYY年MM月DD日(ddd) HH:mm'));

    // テーブルの 1行の構成を定義する
    const comptr = _.template(
      '<tr class="schtr">' +
        '<td id="tdschuser<%- userid %>" class="nowrap_grn" scope="row"></td>' +
        '<td><ul id="uischevents<%- userid %>" class="schedule_board_item_grn">' +
        '</ul></td>' +
        '<td id="tdschreturn<%- userid %>"></td>' +
        '<td><ul id="uischcurrent<%- userid %>" ' +
            'class="schedule_board_item_grn schedule_board_item_current_grn">' +
        '</ul></td>' +
      '</tr>'
    );

    // 外出予定セルの構成を定義する
    const compsch = _.template(
      '<li><a href="/g/schedule/view.csp?event=<%- evid %>" target="_blank">' +
        '<span class="schedule_board_time_grn"><%- evtime %></span>' +
        '<span class="<%- evcolor %> schedule_board_event_grn"><%- evmenu %></span>' +
        '<span><%- evsubject %></span>' +
      '</a></li>'
    );

    // 一般的なセルの構成を定義する
    const comptext = _.template(
      '<div class="tddiv"><span><%- txtbody %></span></div>'
    );

    // スケジュール表示部分を定義する
    const complist = _.template(
      '<li><span><%- libody %></span></li>'
    );

    // スケジュールのメニューに対応する色を返す
    const eventcolor = function(pmenu) {
      if (pmenu === '出張') {
        return 'event_color3_grn';
      } else if (pmenu === '往訪') {
        return 'event_color5_grn';
      } else if (pmenu === '休み') {
        return 'event_color4_grn';
      }
      return 'event_color2_grn';
    };

    // テーブルにユーザーごとのスケジュールを記入する
    for (let i = 0; i < users.length; i += 1) {
      if (!psch[i].statusCode || psch[i].statusCode > 201) continue;
      sch = psch[i].data.events;

      // テーブルの要素を追加
      $('#schtbody').append(comptr({userid: users[i].id}));

      // 名前の列に氏名を表示
      $('#tdschuser' + users[i].id).append(comptext({txtbody: users[i].name}));

      // スケジュールの内容をテーブルに表示
      for (let j = 0; j < sch.length; j += 1) {
      // スケジュールの開始日時と終了日時
      // 日付が当日であれば時刻のみ、当日でなければ日時を取得する
        starthhmm = getDateOrTime(sch[j].start.dateTime);
        endhhmm = sch[j].end ? getDateOrTime(sch[j].end.dateTime) : '';

        // メニューが「往訪」「 出張」「 休み」 に該当する場合は、予定に表示
        if (sch[j].eventMenu === '往訪' ||
                  sch[j].eventMenu === '出張' ||
                  sch[j].eventMenu === '休み') {
          // 「外出予定」列に、日時、メニュー、題目を表示する
          $('#uischevents' + users[i].id).append(
            compsch({
              evid: sch[j].id,
              evtime: starthhmm + ' - ' + endhhmm,
              evmenu: sch[j].eventMenu,

              evsubject: sch[j].subject,
              evcolor: eventcolor(sch[j].eventMenu)
            })
          );

          // スケジュールの日時が現在の時間に該当する場合は、「現在の予定」の列に表示
          if (moment(sch[j].start.dateTime) < moment() &&
                  (!sch[j].end || moment() < moment(sch[j].end.dateTime))) {
            $('#uischcurrent' + users[i].id).append(
              complist({libody: sch[j].subject})
            );
          }
        }

        // メニューが「帰社」に該当する場合は、「帰社時刻」の列に日時を表示
        if (sch[j].eventMenu === '帰社') {
        // 終了日時が入っていれば終了日時、なければ開始日時
          $('#tdschreturn' + users[i].id).append(
            comptext({txtbody: endhhmm ? endhhmm : starthhmm})
          );
        }
      }
    }
    return null;
  }

  // グループのメンバーとスケジュールを取得する
  function getScheduleData(porgid) {
    const orgid = porgid ? porgid : $('#selorgs').val();
    let users;

    // 優先する組織に所属しているメンバーを取得する
    return garoon.api('/api/v1/base/organizations/' + orgid + '/users', 'GET', {}).then((res) => {
      const aryquery = [],
        aryurl = [];
      users = res.data.users;

      if (users.length < 1) {
        return null;
      }

      // スケジュールのソート指定: 開始時刻の昇順
      aryquery.push('orderBy=start asc');
      // スケジュールを検索する日時の範囲:開始時間(現在の時刻)
      aryquery.push('rangeStart=' + moment().utc().format('YYYY-MM-DDTHH:mm:ss') + 'Z');
      // スケジュールを検索する日時の範囲:終了時間(東京時間で翌日朝5時)
      aryquery.push('rangeEnd=' + moment().format('YYYY-MM-DD') + 'T20:00:00Z');
      // スケジュールの検索対象:ユーザー
      aryquery.push('targetType=user');
      // スケジュールを検索するユーザーの id
      aryquery.push('target=');

      const queryhead = aryquery.join('&');

      // 所属メンバーのスケジュールを取得する
      for (let i = 0; i < users.length; i += 1) {
        aryurl.push('/api/v1/schedule/events?' + queryhead + users[i].id);
      }
      // 人数分のスケジュールを一括で取得する
      return fetchDatas(aryurl);
    }).then((resusers) => {
    // 行動掲示板を作成する
      return makeOutBoard(users, resusers);
    });
  }

  // 処理開始
  function init() {
    $('#divbody').append('<div id="divschtop"><select id="selorgs"></select></div>');
    // 組織選択セレクトボックスの値が変わったら、選択した組織でスケジュールを表示
    $('#selorgs').change(() => {
      getScheduleData($('#selorgs').val());
    });
    // 更新ボタンがおされたら、スケジュールデータの再読み込みを行う
    $('.schedule_board_update_button_base_grn').click(() => {
      getScheduleData($('#selorgs').val());
    });
    // 組織情報を取得する
    return garoon.api('/api/v1/base/organizations', 'GET', {}).then((resorgs) => {
    // 組織情報
      const orgs = resorgs.data.organizations;
      // 組織の登録がない場合
      if (!orgs || orgs.length === 0) {
        throw new Error('組織が登録されていません');
      }
      // 組織セレクトボックスのオプション
      const compopt = _.template('<option value="<%- orgid %>"><%- orgname %></option>');
      // 組織選択セレクトボックスに組織をセットする
      for (let i = 0; i < orgs.length; i += 1) {
        $('#selorgs').append(compopt({orgid: orgs[i].id, orgname: orgs[i].name}));
      }

      // ログインユーザーの情報を取得する
      return getBaseGetUsersById();

    }).then((resUsers) => {
      // 優先する組織の id を取得する
      // 優先する組織がない場合は、組織リストボックスの一番上の組織とする
      const porg = $(resUsers).find('user').attr('primary_organization') ||
        $('#selorgs option')[0].value;

      // 組織選択リストボックスを、表示する組織が選択された状態にする
      $('#selorgs').val(porg);

      // スケジュールデータを取得する
      return getScheduleData(porg);

    }).then((resresult) => {

      // 処理がすべて終わってから、予定表を表示する
      $('.schedule_board_grn').css('display', 'block');

    }).catch((err) => {

      alert(err);

    });
  }

  init();

})(jQuery.noConflict(true));

whiteBoard.css

以下のサンプルコードを作成し、ファイル名を「whiteBoard.css」、文字コードを「UTF-8」で保存します。

  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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/* Garoon white board sample css */
/* Copyright (c) 2019 Cybozu */
/* Licensed under the MIT License */ 

.schedule_board_grn {
  font-size: 110%;
  background: #fff;
}

.schedule_board_title_grn {
  display: flex;
  align-items: center;
  padding: 15px 20px;
  border: 2px solid #666;
  border-bottom: 0;
}

.schedule_board_grn h3 {
  display: inline-block;
  margin: 0 50px 0 0;
  font-size: 32px;
  font-weight: 700;
  line-height: 1.4;
}

.schedule_board_icon_grn {
  width: 24px;
  height: 24px;
  vertical-align: middle;
  position: relative;
  top: -2px;
}

.schedule_board_date_time_grn {
  margin-right: 10px;
  font-size: 24px;
  font-weight: 700;
  vertical-align: middle;
}

.schedule_board_update_button_base_grn,
.schedule_board_update_button_base_grn:hover,
.schedule_board_update_button_base_grn:active,
.schedule_board_update_button_base_grn:visited {
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
}

.schedule_board_update_button_base_grn:active svg {
  position: relative;
  top: 0;
  right: 0;
  opacity: 0.5;
}

.schedule_board_update_button_base_grn,
.schedule_board_update_button_grn {
  display: inline-block;
  width: 32px;
  height: 32px;
  box-sizing: border-box;
  vertical-align: middle;
}

.schedule_board_grn .list_table_style1_grn_kit th,
.schedule_board_grn .list_table_style1_grn_kit td {
  padding: 10px;
  border: 2px solid #666;
  line-height: 1.2;
  text-align: left;
  vertical-align: top;
}

.schedule_board_grn .list_table_style1_grn_kit th {
  background-color: #fff;
  color: #333;
  font-weight: 700;
  white-space: nowrap;
  text-align: center;
}

.schedule_board_grn .list_table_style1_grn_kit td {
  background-color: #fff;
}

.schedule_board_grn .list_table_style1_grn_kit {
  border-collapse: collapse;
}

.schedule_board_grn .list_table_style1_grn_kit th,
.schedule_board_grn .list_table_style1_grn_kit td {
  vertical-align: middle;
}

.schedule_board_item_grn {
  margin: 0 0 0 25px;
  padding: 0;
}

.schedule_board_item_grn li {
  margin: 0 0 12px 0;
  list-style-position: outside;
  font-weight: 700;
}

.schedule_board_item_grn li:last-child {
  margin-bottom: 0;
}

.schedule_board_item_current_grn li {
  font-weight: 400;
}

.schedule_board_event_grn {
  margin: 0 10px;
  padding: 4px 6px;
  line-height: 1.4;
}

.schedule_board_footer_grn {
  border: 2px solid #666;
  border-top: 0;
  line-height: 0;
  text-align: center;
}

.schedule_board_footer_decoration_grn {
  height: 75px;
  padding: 15px 10px 0 20px;
}

.schedule_board_footer_bar_grn {
  height: 15px;
  background: #aaa;
}

.schedule_board_footer_decoration_svg_grn_33{
  fill:#a0a0a0
}

.schedule_board_footer_decoration_svg_grn_3{
  fill:#d2d2d2
}

.schedule_board_footer_decoration_svg_grn_4{
  opacity:.3
}

.schedule_board_footer_decoration_svg_grn_35,
.schedule_board_footer_decoration_svg_grn_5,
.schedule_board_footer_decoration_svg_grn_6,
.schedule_board_footer_decoration_svg_grn_9{
    fill:#fff
}

.schedule_board_footer_decoration_svg_grn_6{
    opacity:.21
}

.schedule_board_footer_decoration_svg_grn_7{
    opacity:.14
}

.schedule_board_footer_decoration_svg_grn_9{
    opacity:.26
}

.schedule_board_footer_decoration_svg_grn_10{
    fill:#8e8e8e
}

.schedule_board_footer_decoration_svg_grn_11{
    fill:#c7c7c7
}

.schedule_board_footer_decoration_svg_grn_11,
.schedule_board_footer_decoration_svg_grn_33{
    opacity:.85
}

.schedule_board_footer_decoration_svg_grn_14{
    fill:#0074ff
}

.schedule_board_footer_decoration_svg_grn_17{
    fill:#ff0017
}

.schedule_board_footer_decoration_svg_grn_22{
    fill:#3ca415
}

.schedule_board_footer_decoration_svg_grn_35{
    opacity:.2
}

行動予定表ポートレットの作成

sample.html

以下のサンプルコードをコピーします。

  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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<!-- 
 Garoon white board sample html
 Copyright (c) 2019 Cybozu

 Licensed under the MIT License
 -->
<div class="portal_frame portal_frame_html_grn">

<div class="schedule_board_grn" style="display: none;">

<div class="schedule_board_title_grn">
<h3>行動予定表</h3>    
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="schedule_board_icon_grn mRight3">
  <defs>
    <style>
      .schedule_board_schedule_icon_svg_grn{fill:#737373}
    </style>
  </defs>
  <g>
    <path fill="none" d="M0 0h20v20H0z"></path>
    <g>
      <rect class="schedule_board_schedule_icon_svg_grn" x="1" y="3" width="18" height="16" rx="1.2"></rect>
      <path fill="#fff" d="M3 7h14v10H3z"></path>
      <path class="schedule_board_schedule_icon_svg_grn" d="M5 9h2v2H5zM9 9h2v2H9zM13 9h2v2h-2zM5 13h2v2H5zM9 13h2v2H9zM13 13h2v2h-2zM4 1h2v3H4zM14 1h2v3h-2z"></path>
    </g>
  </g>
</svg>
<span id="currenttime"  class="schedule_board_date_time_grn"></span>
<button type="button" class="schedule_board_update_button_base_grn" title="更新" aria-label="更新">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 60.9 60.9" class="schedule_board_update_button_grn">
  <defs>
    <style>
      .schedule_board_update_svg_grn_5{fill:#fff}
    </style>
    <linearGradient id="schedule_board_update_gradation_svg_grn_15" x1="30.02" y1="-2.76" x2="30.77" y2="47.28" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#fff"></stop>
      <stop offset="1" stop-color="#fff" stop-opacity="0"></stop>
    </linearGradient>
    <linearGradient id="schedule_board_update_gradation_svg_grn_15-2" x1="35.92" y1="3.9" x2="45.92" y2="28.55" xlink:href="#schedule_board_update_gradation_svg_grn_15"></linearGradient>
  </defs>
  <g>
    <g>
      <circle cx="30.45" cy="30.45" r="30.45" fill="#c7c7c7" opacity=".85"></circle>
      <circle cx="30.45" cy="28.71" r="28.27" fill="#0074ff"></circle>
      <circle cx="30.49" cy="28.73" r="28.08" opacity=".35" fill="url(#schedule_board_update_gradation_svg_grn_15)"></circle>
      <path d="M24 3.89S32.13.54 42.14 5.76s12.77 15.88 12.77 15.88-8.5-9.66-14.33-13C35 5.47 24 3.89 24 3.89z" opacity=".5" fill="url(#schedule_board_update_gradation_svg_grn_15-2)"></path>
      <path class="schedule_board_update_svg_grn_5" d="M40.57 41.67a1.52 1.52 0 0 1-.82-.25 1.47 1.47 0 0 1-.66-1.07L35.39 30a1.49 1.49 0 1 1 3-.35l3.49 8.62 9.29-2.77a1.5 1.5 0 0 1 .83 2.84l-11 3.27a1.59 1.59 0 0 1-.43.06z"></path>
      <path class="schedule_board_update_svg_grn_5" d="M17.33 29.15a13.19 13.19 0 1 1 22.45 9.37l1.61.35.17 2.13a16.18 16.18 0 1 0-11.42 4.3v-3a13.19 13.19 0 0 1-12.81-13.15z"></path>
    </g>
  </g>
</svg>    
</button>
</div>

<table class="list_table_style1_grn_kit" style="width:100%;">
  <tbody id="schtbody">
    <tr>
      <th style="width:20%;"scope="col"><select 
id="selorgs" class="mRight10"></select>名前</th>
      <th style="width:50%;" scope="col">外出予定</th>
      <th style="width:10%;" scope="col">帰社予定時刻</th>
      <th style="width:20%;" scope="col">現在の予定</th>
    </tr>
  </tbody>
</table>

<div class="schedule_board_footer_grn">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 821 92" class="schedule_board_footer_decoration_grn">
  <defs>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15" x1="17.76" y1=".69" x2="18.14" y2="26.41" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#fff"></stop>
      <stop offset="1" stop-color="#fff" stop-opacity="0"></stop>
    </linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-2" x1="20.79" y1="4.11" x2="25.93" y2="16.78" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-3" x1="78.76" y1="1.82" x2="79.14" y2="27.53" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-4" x1="81.79" y1="5.24" x2="86.93" y2="17.91" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-5" x1="106.42" y1="40.34" x2="106.8" y2="66.05" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-6" x1="109.45" y1="43.76" x2="114.59" y2="56.43" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-7" x1="156.42" y1="30.34" x2="156.8" y2="56.05" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-8" x1="159.45" y1="33.76" x2="164.59" y2="46.43" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-9" x1="204.76" y1="3.82" x2="205.14" y2="29.53" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-10" x1="207.79" y1="7.24" x2="212.93" y2="19.91" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-11" x1="295.76" y1="8.82" x2="296.14" y2="34.53" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-12" x1="298.79" y1="12.24" x2="303.93" y2="24.91" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-13" x1="245.76" y1="37.82" x2="246.14" y2="63.54" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
    <linearGradient id="schedule_board_footer_decoration_gradation_svg_grn_15-14" x1="248.79" y1="41.24" x2="253.93" y2="53.91" xlink:href="#schedule_board_footer_decoration_gradation_svg_grn_15"></linearGradient>
  </defs>
  <g>
    <g>
      <path fill="none" d="M0 0h821v92H0z"></path>
      <path d="M480.28 72l1 2.54 24 .89.31 1.2h7.45l.34-1h97.43l.25-2.13h7.45c.72 0 1.14-.9 1.37-2.32v-1.56z" opacity=".76" fill="#a0a0a0"></path>
      <path d="M620.11 70.72a2 2 0 0 1-2 1.77h-8.65a1.79 1.79 0 0 1-1.79-1.79v-6.55a1.79 1.79 0 0 1 1.79-1.79h8.65a2 2 0 0 1 2 1.78l.17 1.59a19.24 19.24 0 0 1 0 3.56zM510.23 73.68a.86.86 0 0 1-.89.87l-28-.84a1.07 1.07 0 0 1-1-.91l-.56-4.29a9.52 9.52 0 0 1 0-1.77l.57-4.69a1.08 1.08 0 0 1 1-.91l28-.84a.86.86 0 0 1 .89.87z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_3" d="M610.97 74.58h-98.95V60.27h98.95l.52 7.69-.52 6.62z"></path>
      <path d="M513.21 75.4h-7.8V59.45h7.8l.16 8.37-.16 7.58z"></path>
      <g class="schedule_board_footer_decoration_svg_grn_4">
        <path class="schedule_board_footer_decoration_svg_grn_5" d="M506.62 60.55H512v.92h-5.38zM506.62 63.03H512v.92h-5.38zM506.62 65.52H512v.92h-5.38zM506.62 68H512v.92h-5.38zM506.62 70.48H512v.92h-5.38zM506.62 72.97H512v.92h-5.38z"></path>
      </g>
      <path class="schedule_board_footer_decoration_svg_grn_6" d="M610.64 64.43l-96.68-1.97v-1.99h96.68v3.96z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_7" d="M504.91 60.41h.46v14.01h-.46zM513.66 74.57h-.45v-14.3h.45l.16 7.21-.16 7.09z"></path>
      <path opacity=".25" fill="#fff" d="M481.46 61.83l22.68-.69v3.54l-22.68-2.85z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_9" d="M611.23 63.77v-1h6.89a1.81 1.81 0 0 1 1.4 1.08z"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="588.26" cy="67.83" r="1.28"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="593.57" cy="67.83" r="1.28"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="598.87" cy="67.83" r="1.28"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_11" cx="17.98" cy="17.76" r="15.65"></circle>
      <circle cx="17.98" cy="16.86" r="14.53"></circle>
      <circle cx="18" cy="16.88" r="14.43" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15)" opacity=".35"></circle>
      <path d="M14.66 4.11a11.71 11.71 0 0 1 9.32 1 14 14 0 0 1 6.57 8.16s-4.37-5-7.36-6.68-8.53-2.48-8.53-2.48z" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-2)" opacity=".5"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_11" cx="78.98" cy="18.88" r="15.65"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_14" cx="78.98" cy="17.99" r="14.53"></circle>
      <circle cx="79" cy="18" r="14.43" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-3)" opacity=".35"></circle>
      <path d="M75.66 5.23a11.71 11.71 0 0 1 9.32 1 14 14 0 0 1 6.57 8.17s-4.37-5-7.36-6.68-8.53-2.49-8.53-2.49z" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-4)" opacity=".5"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_11" cx="106.64" cy="57.4" r="15.65"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_17" cx="106.64" cy="56.51" r="14.53"></circle>
      <circle cx="106.66" cy="56.52" r="14.43" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-5)" opacity=".35"></circle>
      <path d="M103.33 43.76a11.66 11.66 0 0 1 9.32 1 14 14 0 0 1 6.56 8.16s-4.37-5-7.36-6.68-8.52-2.48-8.52-2.48z" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-6)" opacity=".5"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_11" cx="156.64" cy="47.4" r="15.65"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_17" cx="156.64" cy="46.51" r="14.53"></circle>
      <circle cx="156.66" cy="46.52" r="14.43" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-7)" opacity=".35"></circle>
      <path d="M153.33 33.76a11.66 11.66 0 0 1 9.32 1 14 14 0 0 1 6.56 8.16s-4.37-5-7.36-6.68-8.52-2.48-8.52-2.48z" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-8)" opacity=".5"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_11" cx="204.98" cy="20.88" r="15.65"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_22" cx="204.98" cy="19.99" r="14.53"></circle>
      <circle cx="205" cy="20" r="14.43" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-9)" opacity=".35"></circle>
      <path d="M201.66 7.23a11.71 11.71 0 0 1 9.32 1 14 14 0 0 1 6.57 8.17s-4.37-5-7.36-6.68-8.53-2.49-8.53-2.49z" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-10)" opacity=".5"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_11" cx="295.98" cy="25.88" r="15.65"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_22" cx="295.98" cy="24.99" r="14.53"></circle>
      <circle cx="296" cy="25" r="14.43" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-11)" opacity=".35"></circle>
      <path d="M292.66 12.23a11.71 11.71 0 0 1 9.32 1 14 14 0 0 1 6.57 8.17s-4.37-5-7.36-6.68-8.53-2.49-8.53-2.49z" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-12)" opacity=".5"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_11" cx="245.98" cy="54.89" r="15.65"></circle>
      <circle cx="245.98" cy="53.99" r="14.53" fill="#ffff15"></circle>
      <circle cx="246" cy="54" r="14.43" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-13)" opacity=".35"></circle>
      <path d="M242.66 41.24a11.71 11.71 0 0 1 9.32 1 14 14 0 0 1 6.57 8.16s-4.37-5-7.36-6.68-8.53-2.48-8.53-2.48z" fill="url(#schedule_board_footer_decoration_gradation_svg_grn_15-14)"></path>
      <path class="schedule_board_footer_decoration_svg_grn_14" d="M684.64 87.06a2.05 2.05 0 0 1-2 1.78H674a1.8 1.8 0 0 1-1.79-1.79v-6.56a1.79 1.79 0 0 1 1.79-1.78h8.64a2 2 0 0 1 2 1.77l.18 1.6a20.16 20.16 0 0 1 0 3.56zM574.76 90a.87.87 0 0 1-.89.87l-28-.84a1.11 1.11 0 0 1-1-.92l-.55-4.28a8.82 8.82 0 0 1 0-1.78l.57-4.68a1.07 1.07 0 0 1 1-.91l28-.84a.85.85 0 0 1 .89.86z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_3" d="M675.5 90.92h-98.95v-14.3h98.95l.52 7.69-.52 6.61z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_14" d="M577.74 91.75h-7.8V75.8h7.8l.15 8.37-.15 7.58z"></path>
      <g class="schedule_board_footer_decoration_svg_grn_4">
        <path class="schedule_board_footer_decoration_svg_grn_5" d="M571.15 76.9h5.38v.92h-5.38zM571.15 79.38h5.38v.92h-5.38zM571.15 81.86h5.38v.92h-5.38zM571.15 84.35h5.38v.92h-5.38zM571.15 86.83h5.38v.92h-5.38zM571.15 89.31h5.38v.92h-5.38z"></path>
      </g>
      <path class="schedule_board_footer_decoration_svg_grn_6" d="M675.16 80.78l-96.67-1.97v-1.99h96.67v3.96z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_7" d="M569.43 76.76h.46v14.01h-.46zM578.19 90.92h-.45V76.61h.45l.16 7.21-.16 7.1z"></path>
      <path opacity=".31" fill="#fff" d="M545.99 78.17l22.67-.68v3.54l-22.67-2.86z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_9" d="M675.76 80.11v-1h6.88a1.81 1.81 0 0 1 1.41 1.08z"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="652.79" cy="84.18" r="1.28"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="658.09" cy="84.18" r="1.28"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="663.4" cy="84.18" r="1.28"></circle>
      <path class="schedule_board_footer_decoration_svg_grn_17" d="M532.49 87.06a2.06 2.06 0 0 1-2 1.78h-8.64a1.79 1.79 0 0 1-1.79-1.79v-6.56a1.79 1.79 0 0 1 1.79-1.78h8.64a2 2 0 0 1 2 1.77l.17 1.6a19.24 19.24 0 0 1 0 3.56zM422.61 90a.88.88 0 0 1-.9.87l-28-.84a1.09 1.09 0 0 1-1-.92l-.56-4.28a8.82 8.82 0 0 1 0-1.78l.57-4.68a1 1 0 0 1 1-.91l28-.84a.86.86 0 0 1 .9.86z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_3" d="M523.35 90.92h-98.96v-14.3h98.96l.52 7.69-.52 6.61z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_17" d="M425.59 91.75h-7.81V75.8h7.81l.15 8.37-.15 7.58z"></path>
      <g class="schedule_board_footer_decoration_svg_grn_4">
        <path class="schedule_board_footer_decoration_svg_grn_5" d="M418.99 76.9h5.38v.92h-5.38zM418.99 79.38h5.38v.92h-5.38zM418.99 81.86h5.38v.92h-5.38zM418.99 84.35h5.38v.92h-5.38zM418.99 86.83h5.38v.92h-5.38zM418.99 89.31h5.38v.92h-5.38z"></path>
      </g>
      <path class="schedule_board_footer_decoration_svg_grn_6" d="M523.01 80.78l-96.68-1.97v-1.99h96.68v3.96z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_7" d="M417.28 76.76h.46v14.01h-.46zM426.04 90.92h-.46V76.61h.46l.15 7.21-.15 7.1z"></path>
      <path opacity=".36" fill="#fff" d="M393.83 78.17l22.68-.68v3.54l-22.68-2.86z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_9" d="M523.6 80.11v-1h6.89a1.81 1.81 0 0 1 1.4 1.08z"></path>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="500.63" cy="84.18" r="1.28"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="505.94" cy="84.18" r="1.28"></circle>
      <circle class="schedule_board_footer_decoration_svg_grn_10" cx="511.25" cy="84.18" r="1.28"></circle>
      <g opacity=".85">
        <path class="schedule_board_footer_decoration_svg_grn_33" d="M730 75.29h91V82h-91z"></path>
        <path class="schedule_board_footer_decoration_svg_grn_33" d="M817 89l-83-.19v-7.85l83 .19V89z"></path>
      </g>
      <path d="M734 81.1h83V92h-83z"></path>
      <path fill="#9cc3e7" d="M730 45h91v36h-91z"></path>
      <path class="schedule_board_footer_decoration_svg_grn_35" d="M784.64 47H819v27.41L784.64 47zM730 45h91v1h-91z"></path>
    </g>
  </g>
</svg>
<div class="schedule_board_footer_bar_grn"></div>
</div>
</div>

</div>

システム管理(各アプリケーション)> ポータル > HTML ポートレット > HTML ポートレットの追加。

上記のように遷移し、「ポートレットの内容」に先ほどコピーした内容を入力してください。

JavaScript / CSSによるカスタマイズ

2019 年 2 月版の Garoon より、ポートレット毎に JavaScript / CSS によるカスタマイズが可能になりました。

ポートレットの詳細から下図のように JavaScript / CSS によるカスタマイズを適用してください。

「カスタマイズ」は「適用する」を選択してください。

JavaScriptカスタマイズ
  • moment.js( Cybozu CDN
    https://js.cybozu.com/momentjs/2.24.0/moment-with-locales.min.js
  • lodash.js( Cybozu CDN
    https://js.cybozu.com/lodash/4.17.11/lodash.min.js
  • jQuery( Cybozu CDN
    https://js.cybozu.com/jquery/3.4.1/jquery.min.js
  • whiteBoard.js
CSSカスタマイズ
  • whiteBoard.css

以上でポートレットの作成は終了です。

作成したポートレットを使用して、ポータルを作成してください。
ポータルの作成については、Garoon ヘルプサイトの ポータル作成の流れ (External link) を確認してください。

おわりに

ホワイトボードなどで管理している行動予定表を Garoon 上に表示させてみました。
閲覧や予定の編集など、Garoon 上で可能なので、スピーディかつ簡単に予定の共有ができようになりました。

2019 年 2 月版アップデートにより一層カスタマイズしやすくなった HTML ポートレットで、さまざまな業務改善ポータルを作成できるかと思います。

2019 年 2 月アップデート。

information

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