ガルーンポータル活用 Tips #1 「行き先案内板」

目次

はじめに

今回は、大企業向けグループウェア「サイボウズ Garoon」のポータル活用企画として、「行き先案内板」を作成していきたいと思います。

完成図

自由に必要な情報を配置できる「HTML ポートレット」ですが、配置しているリンクが年度ごとに変わったりする場合 html を直接編集する手動のメンテナンスが発生します。

担当者が気付かずに放置してしまい、しまいには使われないゾンビポータルと化す前に、サクッと情報を更新できるポートレットを今回は作っていきます。

HTML ポートレット

バックエンドで使うのは…そう、kintone です。
kintone でリソースを管理することで、Garoon のシステム管理権限をもっていなくても配置するリソースを変えることができます。
つまり、ポータルで発信したい情報を担当部署の担当者の作業だけで変更でき、メンテナンスのコストを下げることができます。

それでは kintone に表示するリソースを保管して、Garoon のポートレットに表示させる小技を披露していきたいと思います。

information

kintone を介せず Garoon 完結で同様のポータルを作る方法については Garoon ポータル 行き先案内板(簡易版) を参照してください。

リソースの準備

ポートレットに動的に表示するコンテンツは、kintone 側でレコードとして保存して使いますが、今回作成するサンプルでは、各パーツで表示する画像ファイルがいくつかあります。
今回画像ファイルの置き場として、Garoon の「ファイル管理」にファイルを保存して使っていきたいと思います。

では、ファイル管理を使ってリソースの準備をしていきたいと思います。

画像の準備

  1. 今回使う画像ファイル類は以下の zip ファイルよりダウンロードしてください。
    garoon-portal1.zip
  2. 分かりやすくするため、ファイル管理にポートレット用のフォルダーを作成します。今回は「kin ポータル用画像」とします。
  3. ページのヘッダー用画像、各 box の吹き出し用画像、各 box のフッター用画像、ページのフッター用画像を「kin ポータル用画像」に保存します。

CSS ファイルの準備

次にレイアウト調整用ファイル「main.css」を作成します。

  1. CSS サンプルコード を参考に、「main.css」を作成してください。 文字コードは「UTF-8」で保存します。
  2. ファイル管理の一覧画面で、さきほど保存した以下のファイルのダウンロードアイコンから画像のアドレス情報をひとつずつコピーします。
    • 「bg_header.png」
    • 「bg_box_footer.png」
    • 「bg_lead_red.png」
    • 「bg_lead_yellow.png」
    • 「bg_lead_green.png」
    • 「bg_lead_purple.png」
    • 「bg_lead_turquoise.png」
    • 「bg_lead_blue.png」
  3. 「main.css」の /*画像パスの変更*/ 部分の XXX を含むリンクをコピーしたリンク(「/g」以降の部分)で書き換えます。

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
 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
308
309
/*
* Garoon Portal sample program
* Copyright (c) 2016 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
*/

@charset "UTF-8";
/*
*
* Title: Calomama Graph
* Last Modified: 2015-05-19
* Description: Pages Style
*
*/
/* =========== INDEX LIST ============
  1: RESET
  2: COMMON
  3: LAYOUT
  4: MODULE
====================================== */
/* ===================================
  1: RESET
====================================== */
/*@import "normalize";*/
/* ===================================
  2: COMMON
====================================== */
.template-portal01 h1, .template-portal01 h2, .template-portal01 h3, .template-portal01 h4, .template-portal01 h5, .template-portal01 h6 {
  margin: 0;
  font-weight: normal;
  line-height: 1.5;
}
.template-portal01 p,
.template-portal01 ul,
.template-portal01 ol,
.template-portal01 dl {
  list-style: none;
  margin: 0;
  line-height: 1.4;
  font-size: 11px;
  font-size: 1.1rem;
}
.template-portal01 img {
  line-height: 1;
  vertical-align: top;
}
.template-portal01 table {
  width: 100%;
  border-collapse: collapse;
}
.template-portal01 th,
.template-portal01 td {
  text-align: left;
}

/* ===================================
  4: MODULE
====================================== */
.template-portal01 {
  background: #f2f2f2;
  font-size: 12px;
  font-size: 1.2rem;
}
.template-portal01-header {
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_header.png?hid=XXX&fid=XXX&ticket=&.png) repeat top left;
  margin-bottom: 20px;
  padding: 35px 10px 29px;
  text-align: center;
}
.template-portal01-header h1 {
  font-size: 30px;
  font-size: 3rem;
  font-weight: bold;
  color: #fff;
  text-shadow: 0px 2px 0px rgba(0, 0, 0, 0.2);
}
.template-portal01-header .lead {
  font-size: 14px;
  font-size: 1.4rem;
  color: #fff;
}
.template-portal01-footer {
  background: #666;
  padding: 30px;
  color: #fff;
}
.template-portal01-footer p {
  font-size: 9px;
  font-size: 0.9rem;
}
.template-portal01-footer .footer-nav-wrap {
  display: table;
  width: 100%;
}
.template-portal01-footer .footer-nav-wrap .nav-box {
  width: 33.33333333%;
  display: table-cell;
  box-sizing: border-box;
  padding: 20px 20px 0 0;
}
.template-portal01-footer .footer-nav-wrap .nav-box li {
  margin-bottom: 7px;
}
.template-portal01-footer .footer-nav-wrap .nav-box a {
  display: inline-block;
  padding-left: 15px;
  color: #fff;
  font-size: 9px;
  font-size: 0.9rem;
}
.template-portal01-footer .footer-nav-wrap .nav-box a:before {
  content: "";
  display: inline-block;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 4px 0 4px 6px;
  border-color: transparent transparent transparent #ffffff;
  margin-right: 5px;
  margin-left: -15px;
}
.template-portal01-footer .footer-nav-wrap .nav-box a:hover {
  color: #ffff66;
}
.template-portal01-contents {
  padding: 0 10px;
}
.template-portal01-contents:after {
  content: '';
  display: block;
  clear: both;
}
.template-portal01-box {
  width: 33.333333%;
  margin-bottom: 20px;
  float: left;
}
.template-portal01-box > a {
  display: block;
  text-decoration: none;
  color: #fff;
}
.template-portal01-box > a:hover {
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=$trans*100)";
  filter: alpha(opacity=80);
  -moz-opacity: 0.8;
  -khtml-opacity: 0.8;
  opacity: 0.8;
}
.template-portal01-box .box-wrap {
  padding-left: 20px;
}
.template-portal01-box .box-wrap > div {
  background: #999;
}
.template-portal01-box .box-wrap .box-header {
  padding: 15px 10px 0;
}
.template-portal01-box .box-wrap .box-header:after {
  content: '';
  display: block;
  clear: both;
}
.template-portal01-box .box-wrap .box-header .lead {
  width: 120px;
  height: 78px;
  box-sizing: border-box;
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_lead_red.png?hid=XXX&fid=XXX&ticket=&.png) no-repeat top left;
  -moz-background-size: 120px 78px;
  -webkit-background-size: 120px 78px;
  -o-background-size: 120px 78px;
  background-size: 120px 78px;
  padding: 20px 23px;
  margin-top: -25px;
  margin-left: -20px;
  margin-right: 10px;
  line-height: 1.2;
  color: #666;
  float: left;
  font-size: 9px;
  font-size: 0.9rem;
}
.template-portal01-box .box-wrap .box-header h2 {
  margin-bottom: 15px;
  font-size: 25px;
  font-size: 2.5rem;
  text-shadow: 0px 2px 0px rgba(0, 0, 0, 0.2);
  line-height: 1;
  white-space: nowrap;
  font-weight: bold;
  color: #fff;
}
.template-portal01-box .box-wrap .box-content {
  display: table;
  width: 100%;
}
.template-portal01-box .box-wrap .box-content > div {
  display: table-cell;
  padding: 5px 20px 20px;
  vertical-align: top;
}
.template-portal01-box .box-wrap .box-content .content-text {
  padding-left: 0px;
  color: #fff;
  text-shadow: 0px 2px 0px rgba(0, 0, 0, 0.2);
}
.template-portal01-box .box-wrap .box-footer {
  padding: 10px;
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_box_footer.png?hid=XXX&fid=XXX&ticket=&.png);
  position: relative;
}
.template-portal01-box .box-wrap .box-footer:after {
  content: "";
  display: block;
  position: absolute;
  right: 0;
  bottom: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 0 25px 25px;
  border-color: transparent transparent #f2f2f2 transparent;
}
.template-portal01-box .box-wrap .box-red {
  background: #ff6666;
}
.template-portal01-box .box-wrap .box-red .box-header .lead {
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_lead_red.png?hid=XXX&fid=XXX&ticket=&.png) no-repeat top left;
  -moz-background-size: 120px 78px;
  -webkit-background-size: 120px 78px;
  -o-background-size: 120px 78px;
  background-size: 120px 78px;
}
.template-portal01-box .box-wrap .box-yellow {
  background: #ebb218;
}
.template-portal01-box .box-wrap .box-yellow .box-header .lead {
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_lead_yellow.png?hid=XXX&fid=XXX&ticket=&.png) no-repeat top left;
  -moz-background-size: 120px 78px;
  -webkit-background-size: 120px 78px;
  -o-background-size: 120px 78px;
  background-size: 120px 78px;
}
.template-portal01-box .box-wrap .box-green {
  background: #91cd5c;
}
.template-portal01-box .box-wrap .box-green .box-header .lead {
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_lead_green.png?hid=XXX&fid=XXX&ticket=&.png) no-repeat top left;
  -moz-background-size: 120px 78px;
  -webkit-background-size: 120px 78px;
  -o-background-size: 120px 78px;
  background-size: 120px 78px;
}
.template-portal01-box .box-wrap .box-purple {
  background: #c684ca;
}
.template-portal01-box .box-wrap .box-purple .box-header .lead {
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_lead_purple.png?hid=XXX&fid=XXX&ticket=&.png) no-repeat top left;
  -moz-background-size: 120px 78px;
  -webkit-background-size: 120px 78px;
  -o-background-size: 120px 78px;
  background-size: 120px 78px;
}
.template-portal01-box .box-wrap .box-blue {
  background: #61aee4;
}
.template-portal01-box .box-wrap .box-blue .box-header .lead {
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_lead_blue.png?hid=XXX&fid=XXX&ticket=&.png) no-repeat top left;
  -moz-background-size: 120px 78px;
  -webkit-background-size: 120px 78px;
  -o-background-size: 120px 78px;
  background-size: 120px 78px;
}
.template-portal01-box .box-wrap .box-turquoise {
  background: #25d3c4;
}
.template-portal01-box .box-wrap .box-turquoise .box-header .lead {
  /* 画像パスの変更 */
  background: url(/g/cabinet/download.csp/-/bg_lead_turquoise.png?hid=XXX&fid=XXX&ticket=&.png) no-repeat top left;
  -moz-background-size: 120px 78px;
  -webkit-background-size: 120px 78px;
  -o-background-size: 120px 78px;
  background-size: 120px 78px;
}
.template-portal01 .btn-text {
  text-align: center;
  font-size: 9px;
  font-size: 0.9rem;
}
.template-portal01 .btn-text:after {
  content: "";
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: 5px;
  border-style: solid;
  border-width: 5px 0 5px 10px;
  border-color: transparent transparent transparent #ffffff;
}

JavaScript ファイル

最後に kintone との連携用の JavaScript ファイル「kinGrIntegration.js」を作成します。

JavaScript サンプルコード を参考に、「kinGrIntegration.js」を作成します。
文字コードは「UTF-8」でファイル管理に保存します。
JavaScript サンプルコード の以下の項目は環境に応じて書き換えてください。

JavaScript サンプルコード
  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
/*
* Garoon Portal sample program
* Copyright (c) 2016 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
*/

(()=> {
  'use strict';
  const APP_ID = 101;
  const REC_ID = 1;
  let count = 0;
  fetch(`/k/v1/record.json?app=${APP_ID}&id=${REC_ID}`, {
    method: 'GET',
    headers: {
      'X-Requested-With': 'XMLHttpRequest',
    }
  })
    .then(response => response.json())
    .then(resp => {
      const record = resp.record;
      const fileKeyArray = [];
      const mainTitle = escapeHtml(record.gr_title.value);
      const subTitle = escapeHtml(record.sub_title.value);
      document.getElementById('main_title').innerHTML = mainTitle;
      document.getElementById('main_title_sub').innerHTML = subTitle;
      if (record.S_TABLE.value) {
        const divComment = document.getElementById('comment');
        for (let i = 0; i < record.S_TABLE.value.length; i++) {
          const tableObj = record.S_TABLE.value[i].value;
          if (tableObj.img.value.length > 0) {
            fileKeyArray.push(tableObj.img.value[0].fileKey);
          }
          const color = escapeHtml(tableObj.color.value);
          const comment = escapeHtml(tableObj.comment.value);
          const title = escapeHtml(tableObj.title.value);
          const titleSub = escapeHtml(tableObj.title_sub.value);
          const footer = escapeHtml(tableObj.footer.value);
          const link = escapeHtml(tableObj.link.value);

          const parentDiv = document.createElement('div');
          parentDiv.classList.add('template-portal01-box');
          const linkDiv = document.createElement('a');
          linkDiv.href = link;
          const wrapDiv = document.createElement('div');
          wrapDiv.classList.add('box-wrap');
          const colorDiv = document.createElement('div');
          colorDiv.classList.add(color);
          const headerDiv = document.createElement('div');
          headerDiv.classList.add('box-header');
          headerDiv.innerHTML = `<p class="lead">${comment}</p><h2>${title}</h2>`;
          colorDiv.appendChild(headerDiv);

          const boxContentDiv = document.createElement('div');
          boxContentDiv.classList.add('box-content');
          const contentIconDiv = document.createElement('div');
          contentIconDiv.classList.add('content-icon');
          const img = document.createElement('img');
          img.id = 'capture_' + i;
          img.width = 55;
          img.height = 55;
          contentIconDiv.appendChild(img);
          boxContentDiv.appendChild(contentIconDiv);

          const contentTextDiv = document.createElement('div');
          contentTextDiv.classList.add('content-text');
          contentTextDiv.innerHTML = `<p>${titleSub}</p>`;
          boxContentDiv.appendChild(contentTextDiv);
          colorDiv.appendChild(boxContentDiv);

          const footerDiv = document.createElement('div');
          footerDiv.classList.add('box-footer');
          footerDiv.innerHTML = `<p class="btn-text">${footer}</p>`;
          colorDiv.appendChild(footerDiv);

          wrapDiv.appendChild(colorDiv);
          linkDiv.appendChild(wrapDiv);
          parentDiv.appendChild(linkDiv);
          divComment.appendChild(parentDiv);
        }
      }
      if (fileKeyArray.length > 0) {
        loopFetchFile(fileKeyArray[count]);
      }
      function loopFetchFile(key) {
        fetch(`/k/v1/file.json?fileKey=${key}`, {
          method: 'GET',
          headers: {
            'X-Requested-With': 'XMLHttpRequest',
          }
        })
          .then(response => response.blob())
          .then(blob => {
            const url = URL.createObjectURL(blob);
            document.getElementById('capture_' + count).src = url;
            if (count < fileKeyArray.length - 1) {
              count++;
              loopFetchFile(fileKeyArray[count]);
            }
          });
      }
    });
  const escapeHtml = (str) => {
    return str
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  };
})();

Garoon ポートレットの準備

ポートレットの HTML

今回使うポートレットを作成していきます。
下記は、ヘッダー部分、kintone からのデータ表示部分とフッター部分を作成するポートレットです。

 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
<!--
* Garoon Portal sample program
* Copyright (c) 2016 Cybozu
*
* Licensed under the MIT License
* https://opensource.org/license/mit/
-->

<div class="template-portal01">
  <header class="template-portal01-header">
  <h1 id='main_title'></h1>
  <p id='main_title_sub' class="lead"></p>
  </header>
  <div class='template-portal01-contents'>
    <div id='comment'>
      <!-- ここにkintoneから取得したデータが表示されます。 -->
    </div>
</div>
<footer class="template-portal01-footer">
    <p>必要な書類をダウンロードできます。</p>
    <div class="footer-nav-wrap">
        <ul class="nav-box">
            <li><a href="#">席次表</a></li>
            <li><a href="#">組織図</a></li>
        </ul>
        <ul class="nav-box">
            <li><a href="#">システムマニュアル</a></li>
            <li><a href="#">確定申告用紙</a></li>
        </ul>
        <ul class="nav-box">
            <li><a href="#">オフィス利用マニュアル</a></li>
            <li><a href="#">名刺発注マニュアル</a></li>
        </ul>
    </div>
</footer>
</div><!--//.template-portal01-->

JavaScript ファイルと CSS ファイルのアップロード

JavaScript / CSS によるカスタマイズから「kinGrIntegration.js」と「main.css」をアップロードします。

ポータルに配置

ポートレットを作成したら、ポータルに配置しましょう。

ポータルに配置したら公開設定を行います。

kintone アプリの準備

リソース保管用アプリの作成

Garoon のポータルに表示させるためのリソース保管用アプリを作成します。
kintone アプリのフィールドは以下のように配置します。

リソース用のフィールドはすべてテーブル化していきます。

フィールドタイプ フィールド名 フィールドコード 備考
文字列(1行) ポートレット名 port_name 任意(配置しなくても連携に影響しません)
文字列(1行) タイトル gr_title
文字列(1行) サブタイトル sub_title
テーブル ポートレット用リソース S_TABLE 以下のフィールドをテーブルに含める
  • 吹き出し
  • パーツタイトル
  • パーツフッター
  • パーツサブタイトル
  • リンク
  • 画像
ドロップダウン color S_TABLE に含める
項目と順番
  • box-red
  • box-yellow
  • box-green
  • box-purple
  • box-blue
  • box-turquoise
文字列(1行) 吹き出し comment S_TABLE に含める
文字列(1行) パーツタイトル title S_TABLE に含める
文字列(1行) パーツフッター footer S_TABLE に含める
文字列(複数行) パーツサブタイトル title_sub S_TABLE に含める
リンク リンク link S_TABLE に含める
入力値の種類:Webサイトのアドレス
添付ファイル 画像 img S_TABLE に含める
作成したアプリのキャプチャ

information

2016 年の 5 月のアップデートにより、認証方式の評価順が変更されます。
2016/05/08 の定期メンテナンスにおける kintone API、User API 更新情報 .
cybozu.com 環境内からのアクセスの場合、本記事内のセッション認証よりも API トークン認証が優先されるため、上記のアップデート以降は API トークンを発行して使用してください。

レコードの登録

kintone にレコードを登録します。

動作確認

作成した Garoon ポータルに kintone で登録した情報が表示されたら成功です!

さいごに

今回の Garoon ポートレット活用はいかがでしょうか?ぜひ、自社の環境で使えるかトライしてみてください。
まだまだ活用できるシーンがありそうなので、今後も Tips を定期的に公開していきたいと思います。

ガルーンポータル活用 Tips

更新履歴

  • 2020/02/19
    jQuery の追加手順および jQuery.noConflict(true) を使うようにコードを修正
  • 2024/03/25
    • jQuery を使わないようにコードを修正
    • JavaScript と CSS ファイルをファイル管理にアップロードする方法から、ポータルの「JavaScript / CSS によるカスタマイズ」を使用する方法に変更
information

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