kintoneポータルにGoogle Workspace新着メール一覧とスケジュール情報を表示しよう

目次

caution
警告

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

はじめに

この記事では、Google Workspaceに紐づくGoogleアカウントの直近のスケジュール情報やGmailで受信した新着メールの一覧を表示するポータルカスタマイズを紹介します。

GmailやGoogleカレンダーのページを開いて確認するところを、kintoneを開くだけで最新情報を確認できて便利なカスタマイズです。

必要なもの

完成イメージ

直近のスケジュール表示

  • ポータルの左側に、直近のスケジュールをカレンダーごとに最大10件表示します。 *1
  • カレンダーの切り替えボタンで、複数のカレンダーを切り替え表示できます。
  • スケジュール一覧をクリックすると、Googleカレンダーのページに遷移します。 *2

新着メール一覧表示

  • ポータルの右側に、受信トレイから取得した直近の新着メールを5件表示します。
  • 未読メールは太字で表示されます。
  • メール一覧をクリックすると、Gmailでクリックしたメールの詳細画面を表示します *2

*1 スケジュール表示のみをサポートしています。
スケジュールを修正したい場合は、スケジュール一覧をクリックし、表示されるGoogleカレンダーの画面で編集してください。 ^

*2 Google Workspaceの仕様上、複数アカウントにログインしている場合、一番上のアカウントが使用されます。
kintoneポータルにはポータル表示時にログインしたアカウントのGmailやスケジュール情報を表示しますが、一覧をクリックしたときの遷移先のページは、一番上のアカウントとして開かれます。
Gmailの場合は、メールの詳細画面ではなくそのアカウントの受信トレイを表示します。 ^

システム構成

  • kintoneポータルからGoogle APIを実行し、GmailやGoogleカレンダーの情報を取得します。

注意事項

  • タイトル部分のアイコン(メールやカレンダー)は、kintone製品のアイコンを使用しています。
    製品のアップデートにより、通知アイコンが正常に表示されなくなる場合があります。

Google Cloud Platformの設定

Google APIを利用できるよう、Google Cloud Platformでプロジェクトを作成します。

  1. Google API Console (External link) にGoogle Workspace管理者アカウトでログインします。

  2. [プロジェクトの選択]をクリックします。

  3. [新しいプロジェクト]をクリックします。

  4. プロジェクト名を入力し、[作成]ボタンをクリックします。この記事では「kintone-portal」としています。
    プロジェクトが作成されると、作成したプロジェクトのダッシュボードに自動で移動します。

  5. [APIとサービスを有効化]をクリックします。

  6. 「APIとサービスを検索」ボックスに「Gmail」と入力します。「Gmail API」が表示されるので選択します。

  7. [有効にする]ボタンをクリックします。サービスが有効化されると、Gmail APIの概要ページに自動で遷移します。

  8. [認証情報を作成]ボタンをクリックします。

  9. 次の内容を入力します。入力が終わったら、[必要な認証情報]ボタンをクリックします。

    項目
    使用するAPI 「Gmail API」を選択します。
    APIを呼び出す場所 「Webブラウザー(JavaScript)」を選択します。
    アクセスするデータの種類 「ユーザー データ」を選択します。
  10. 「OAuth同意画面の表示」モーダルが表示されます[同意画面を設定]をクリックします。

  11. 次の内容を入力します。入力が終わったら、[保存]ボタンをクリックします。


    必要最低限の設定項目を記載しています。その他の設定項目は、任意に設定してください。

    項目
    アプリケーション名 任意の値を入力します。この記事では「kintone-portal」としています。
    サポートメール メールアドレスを選択します。
    承認済みドメイン 「cybozu.com」を入力します。
  12. 認証情報ページに自動で遷移します[認証情報を作成]ボタンをクリックし、「OAuthクライアントID」を選択します。

  13. 次の内容を入力します。入力が終わったら、[作成]ボタンをクリックします。

    項目
    アプリケーションの種類 「Webアプリケーション」を選択します。
    名前 任意の値を入力します。この記事では「kintone-portal」としています。
    承認済みのJavaScript生成元 「https://SUBDOMAIN.cybozu.com」を入力します。
    SUBDOMAINはご利用のkintone環境に合わせて変更してください。
  14. OAuthクライアントIDが生成されます。
    クライアントIDはkintoneに適用するカスタマイズファイルに利用します。メモして控えておいてください。
    [完了]ボタンをクリックします。


    クライアントIDは、「APIとサービス」で作成プロジェクトを選択し、左ペインから「認証情報」を選択すると、再確認できます。

  15. 同様の手順で、Google Calendar APIも有効化します。左ペインから「ダッシュボード」を選択します[APIとサービスを有効化]をクリックします。

  16. 「APIとサービスを検索」ボックスに「Calendar」と入力します。「Google Calendar API」が表示されるので選択します。

  17. [有効にする]ボタンをクリックします。

  18. [認証情報を作成]ボタンをクリックします。

  19. 次の内容を入力します。入力が終わったら、[必要な認証情報]ボタンをクリックします。

    項目
    使用するAPI 「Google Calendar API」を選択します。
    APIを呼び出す場所 「Webブラウザー(JavaScript)」を選択します。
    アクセスするデータの種類 「ユーザー データ」を選択します。
  20. 認証情報は、Gmail APIの有効化で作成したものと同じ認証情報を使用します[完了]ボタンをクリックします。

Google Cloud Platformの設定は以上です。

kintoneの設定

このカスタマイズでは、次の外部ライブラリを利用しています。

  • jQuery v3.3.1, ドキュメント (External link)
    • https://js.cybozu.com/jquery/3.3.1/jquery.min.js
  • Google JavaScriptクライアントライブラリ ドキュメント (External link)
    Google APIを扱うためのライブラリです。
    • https://apis.google.com/js/client.js
    • https://apis.google.com/js/platform.js
  • Moment.js v2.24.0, ドキュメント (External link)
    • https://js.cybozu.com/momentjs/2.24.0/moment.min.js
  • Loaders.css v0.1.2, ドキュメント (External link)
    • loaders.css.js
    • loaders.min.css

ポータルカスタマイズをするには、kintone全体にカスタマイズを適用します。
手順の詳細は「 JavaScriptやCSSを使用したカスタマイズ (External link) 」を参照してください。

  1. システム管理画面を開きます。
  2. [JavaScriptやCSSでkintone全体をカスタマイズする]をクリックします。
  3. 次の内容を入力します。入力が終わったら、[保存]ボタンをクリックします。

    項目
    適用範囲 ポータルを表示させたいユーザーの範囲を選択します。
    PC用のJavaScriptファイル 次の順で指定します。
    • https://js.cybozu.com/jquery/3.1.1/jquery.min.js
    • https://apis.google.com/js/client.js
    • https://apis.google.com/js/platform.js
    • https://js.cybozu.com/momentjs/2.24.0/moment.min.js
    • loaders.css.js
    • カスタマイズファイル(kintone-portal-gsuite.js)
      詳細は、後述の サンプルコード(kintone-portal-gsuite.js) を参照してください。
    PC用のCSSファイル 次の順で指定します。

loaders.cssの入手方法

  1. https://github.com/ConnorAtherton/loaders.css/releases/tag/0.1.2 (External link) にアクセスします。
  2. Assetsの「Source code(zip)」をクリックし、Zipファイルをダウンロードします。
  3. ダウンロードしたZipファイルを解凍します。解凍したフォルダーの以下のファイルを利用します。
    • loaders.css.js
    • loaders.min.css

kintoneの設定は以上です。

サンプルコード

kintone-portal-gsuite.js

12行目GOOGLE_CLIENT_IDの値: Google Cloud Platformの設定 でメモしておいたクライアントIDに置き換えてください。

  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
/*
 * kintone x Gsuite Portal Sample program
 * Copyright (c) 2019 Cybozu
 *
 * Licensed under the MIT License
*/

(($) => {
  'use strict';

  // Google API設定で発行したクライアントID
  const GOOGLE_CLIENT_ID = 'GOOGLE_API_CLIENT_ID';

  // kintoneポータルに表示する要素を作る
  const createElement = () => {
    let space = null;
    const $portal = $('<div class="main-content">');
    $portal.append(
      '<div class="left-content">'
      + '  <div class="left-container">'
      + '    <div class="custom-container-header-calender"><h3>カレンダー</h3></div>'
      + '    <div class="custom-container-body-calender">'
      + '      <div id="calendars"></div>'
      + '      <div id="events"></div>'
      + '    </div>'
      + '  </div>'
      + '</div>');
    $portal.append(
      '<div class="right-content">'
      + '  <div class="right-container">'
      + '    <div class="custom-container-header-mail"><h3>メール</h3></div>'
      + '    <div class="custom-container-body-mail">'
      + '      <div class="loader-inner ball-grid-pulse"></div>'
      + '    </div>'
      + '  </div>'
      + '</div>');
    space = kintone.portal.getContentSpaceElement();
    $(space).append($portal);
  };

  // Google APIを利用するために認証する
  const getAuth = () => {
    return new kintone.Promise((resolve, reject) => {
      $('.loader-inner').loaders();
      gapi.load('auth2', () => {
        gapi.auth2.init({
          client_id: GOOGLE_CLIENT_ID,
          fetch_basic_profile: true,
          scope: 'https://www.googleapis.com/auth/gmail.readonly '
              + 'https://www.googleapis.com/auth/calendar.readonly '
              + 'https://www.googleapis.com/auth/calendar.events.readonly '
              + 'https://www.googleapis.com/auth/calendar.settings.readonly'
        }).then(() => {
          if (gapi.auth2.getAuthInstance().isSignedIn.get() === true) {
            resolve();
          } else {
            gapi.auth2.getAuthInstance().signIn().then(resolve);
          }
        });
      });
    });
  };

  // Google Calendarからカレンダー一覧を取得する
  const getCalendar = () => {
    moment.locale('ja', {
      weekdaysShort: ['日', '月', '火', '水', '木', '金', '土']
    });

    gapi.client.load('calendar', 'v3', () => {
      gapi.client.calendar.calendarList.list().execute((resp) => {
        resp.items.forEach((calendar) => {
          $('#calendars').append(
            $('<div>')
              .addClass('calendar-item')
              .attr('data-calendar-id', calendar.id)
              .css('color', calendar.backgroundColor)
              .css('border', '1px solid ' + calendar.backgroundColor)
              .on('click', handleCalendarClick)
              .text(calendar.summary));
        });
        getEvents(resp.items[0].id);
      });
    });
  };

  // ポータルのカレンダー一覧をクリックした時のイベントを作成する
  const handleCalendarClick = (event) => {
    const calendarId = $(event.target).attr('data-calendar-id');
    getEvents(calendarId);
  };

  // カレンダーIDからカレンダーの情報(最大10件)を取得する
  const getEvents = (calendarId) => {
    gapi.client.load('calendar', 'v3', () => {
      gapi.client.calendar.events.list({
        calendarId: calendarId,
        timeMin: (new Date()).toISOString(),
        showDeleted: false,
        singleEvents: true,
        maxResults: 10,
        orderBy: 'startTime'
      }).execute((resp) => {
        let date = '';
        $('#events').html('');
        resp.items.forEach((event) => {
          let dateLabel = '';
          let startDate = '';

          if (event.start.dateTime) {
            startDate = moment(event.start.dateTime).format('YYYY/MM/DD');
            dateLabel = moment(event.start.dateTime).format('HH:mm') + ' - ' + moment(event.end.dateTime).format('HH:mm');
          } else if (event.start.date) {
            startDate = event.start.date;
            dateLabel = '[終日]';
          }

          if (date !== startDate) {
            $('#events').append(
              $('<div>')
                .addClass('title')
                .text(moment(startDate).format('YYYY/MM/DD(ddd)')));
            date = moment(startDate).format('YYYY/MM/DD');
          }

          $('#events').append(
            $('<a>')
              .addClass('event')
              .attr('href', event.htmlLink)
              .attr('target', '_blank')
              .text(dateLabel + ' ' + event.summary));
        });
      });
    });
  };

  // Gmailの受信トレイから新着メール5件を取得する
  const getMail = () => {
    gapi.client.load('gmail', 'v1', () => {
      const option = {
        includeSpamTrash: false,
        userId: 'me',
        labelIds: ['INBOX'],
        maxResults: 5
      };
      const request = gapi.client.gmail.users.messages.list(option);
      request.execute((resp) => {
        const requests = {};
        const httpBatch = gapi.client.newHttpBatch();
        $.each(resp.messages, (i, message) => {
          requests[message.id] = gapi.client.request({
            path: 'gmail/v1/users/me/messages/' + message.id,
            method: 'GET',
            params: {format: 'full'}
          });
          httpBatch.add(requests[message.id], {id: message.id});
        });
        httpBatch.execute((response) => {
          const $table = $('<table>');
          const $tbody = $('<tbody>');
          $table.append(
            '<thead>'
            + '  <tr>'
            + '    <th>日時</th>'
            + '    <th>From</th>'
            + '    <th>件名</th>'
            + '  </tr>'
            + '</thead>'
          );

          $.each(response, (i, mail) => {
            const $tr = $(
              '<tr class="tr-row" link="' + mail.id + '">'
              + '  <td class="td-date"></td>'
              + '  <td class="td-from"></td>'
              + '  <td class="td-title"></td>'
              + '</tr>'
            );
            $.each(mail.result.payload.headers, (index, header) => {
              if (header.name === 'Date') $tr.children().eq(0).text(moment(header.value).format('YYYY/MM/DD HH:mm'));
              if (header.name === 'From') $tr.children().eq(1).text(header.value);
              if (header.name === 'Subject') $tr.children().eq(2).html(header.value);
            });
            if (mail.result.labelIds.indexOf('UNREAD') !== -1) {
              $tr.css('font-weight', 'bold');
            }
            $tbody.append($tr);
          });
          $table.append($tbody);
          $('.loader-inner').css('display', 'none');
          $('.custom-container-body-mail').append($table);
          $('.tr-row').on('click', (event) => {
            window.open('https://mail.google.com/mail/u/0/#inbox/' + $(event.currentTarget).attr('link'));
          });
        });
      });
    });
  };

  // kintoneポータルにスケジュール一覧とメール一覧を表示する
  kintone.events.on('portal.show', (event) => {
    createElement();
    getAuth().then((resp) => {
      getMail(resp);
      getCalendar(resp);
    });
  });
})(jQuery);

kintone-portal-gsuite.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
/*
 * kintone x Gsuite Portal Sample css
 * Copyright (c) 2019 Cybozu
 *
 * Licensed under the MIT License
*/

.main-content {
  width: 100%;
  height: auto;
}

.left-content {
  width: 50%;
  float: left;
}

.right-content {
  width: 50%;
  float: left;
}

.left-container {
  width: auto;
  height: auto;
  background-color: #ffffff;
  margin: 16px 8px 16px 16px;
  letter-spacing: .04em;
}

.right-container {
  width: auto;
  height: auto;
  background-color: #ffffff;
  margin: 16px 16px 16px 8px;
  letter-spacing: .04em;
}

.custom-container-header-calender {
  position: relative;
  background-image: url(https://static.cybozu.com/contents/k/image/icon/app/calendar.png);
  background-position: left top;
  background-repeat: no-repeat;
  height: 56px;
  line-height: 56px;
  font-size: 20px;
  padding: 0 72px 0 80px;
  border-bottom: 1px solid #e3e7e8;
}

.custom-container-header-mail {
  background-image: url(https://static.cybozu.com/contents/k/image/icon/app/mail.png);
  background-position: left top;
  background-repeat: no-repeat;
  height: 56px;
  line-height: 56px;
  font-size: 20px;
  padding: 0 72px 0 80px;
  border-bottom: 1px solid #e3e7e8;
}

.custom-container-body-calender {
  text-align: center;
  height: 300px;
  /* line-height: 300px; */
  font-size: 20px;
  background-color: #f3f3f3;
  overflow-y: scroll;
}

.custom-container-body-mail {
  height: 300px;
  text-align: center;
  font-size: 12px;
}

.custom-container-body-mail table{
  width: 100%;
  height: 300px;
}

.custom-container-body-mail table thead tr {
  height: 40px;
  background-color: #f7f9fa;
  border-bottom: 1px solid #e3e7e8;
}

.custom-container-body-mail table tbody tr {
  height: 52px;
  cursor: pointer;
}

.custom-container-body-mail table tbody tr:nth-child(even) {
  background-color: #f2f9fe;
}

.td-date {
  width: 20%;
  padding: 0 5px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.td-from {
  width: 30%;
  padding: 0 5px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.td-title {
  width: 50%;
  padding: 0 5px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ball-grid-pulse {
  padding: 50px;
  padding: 100px 0 0 46%;
}

.ball-grid-pulse > div {
  background-color: #e3e7e8;
}

#calendars {
  border-bottom: 1px solid #dddddd;
  background-color: #ffffff;
  padding: 6px;
  text-align: left;
  line-height: initial;
  height: initial;
}

#calendars .calendar-item {
  display: inline-block;
  background-color: #ffffff;
  padding: 6px;
  font-size: 12px;
  font-weight: 100;
  border-radius: 3px;
  margin: 6px;
  line-height: initial;
  height: initial;
  cursor: pointer;
  transition: 0.2s;
}

#calendars .calendar-item:hover {
  opacity: 0.5;
}

#events .title {
  display: block;
  border-bottom: 1px solid #dddddd;
  line-height: initial;
  height: initial;
  padding: 4px 12px;
  text-align: left;
  font-size: 12px;
  background-color: #f0f0f0;
  text-decoration: none;
  color: #333333;
  font-weight: 700;
}

#events .event {
  display: block;
  border-bottom: 1px solid #dddddd;
  line-height: initial;
  height: initial;
  padding: 8px 12px;
  text-align: left;
  cursor: pointer;
  font-size: 14px;
  background-color: #ffffff;
  text-decoration: none;
  color: #666666;
  transition: 0.2s;
}

#events .event:hover {
  background-color: #f5f5f5;
}

動作確認

  1. kintoneにログインし、ポータル画面を開きます。
  2. Googleアカウントへのログインを求めるポップアップが表示されたら、Google Workspaceで利用しているアカウントを使ってログインします。
    有効なセッションがある場合は、ログインは要求されずにカレンダー一覧や新着メール一覧が表示されます。
  3. カスタマイズで追加したカレンダー一覧や新着メール一覧がポータルに表示されます。

初回のみ、Googleアカウントへのログイン後に権限付与を求めるポップアップが表示されます。
次の手順で、許可します。

  1. 「このアプリは確認されていません」ポップアップが表示されます。「詳細」リンクをクリックします。

  2. 「cybozu.com(安全ではないページ)に移動」リンクをクリックします。

  3. 権限の付与を確認するモーダルが表示されます。以下のすべての権限に対し[許可]ボタンをクリックします。

    • メール メッセージと設定の表示
    • カレンダーの設定を表示します。
    • すべてのカレンダーの予定を表示
    • カレンダーを表示
  4. 「選択内容を確認してください」ポップアップで、[許可]ボタンをクリックします。クリックすると、kintoneポータルにスケジュールとメール一覧が表示されます。

おわりに

この記事では、Google Workspaceという外部サービスのデータをkintoneポータルに表示するカスタマイズを紹介しました。

他にもChrome拡張の Kintone Portal Designer を使ってお手軽にポータルをカスタマイズする kintoneポータルカスタマイズ例 もあるので、ぜひ参照してください。

使用しているAPI

information

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