Outlook連携 - kintoneからOutlookスケジュールを登録しよう

目次

caution
警告

はじめに

kintoneからOutlookのスケジュールを登録できるサンプルです。

Outlookスケジュールの「イベントタイトル」「開始日時」「終了日時」「詳細内容」「添付ファイル」をkintoneから登録できます。
また、kintoneのレコードを変更した場合や、レコードを削除した場合にも、Outlookのイベントも更新されます。

Outlookのイベントを変更した場合は、kintoneのレコードには反映されません。

メリットとしては、kintoneに登録したレコードの内容をイベントとしてOutlookのスケジュールに登録し共有できることです。
また、kintoneのプロセス管理機能を使用してステータスを管理できます。

Outlook側への認証にOAuth2.0を利用しています。
少し敷居が高く感じられるかもしれませんが、詳しい手順の解説とサンプルコードを公開していますのでぜひ参考にしてください。

サンプルコードは SAMPLE-kintone-connect-azure (External link) に公開しています。

概要

Microsoftが提供している MSAL.js (外部サイト) (External link) を利用することで、Outlook(Microsoftアカウント)とのOAuth2.0を利用した認証をkintone上で行うことができます。

連携の流れは以下となっています。

  • kintoneからOAuth認証を用いてEntra ID V2 Endpointへサインイン
  • kintoneからAzureへアクセストークンを取得
  • アクセストークンを使ってGraph APIをたたく。
  • kintoneからデータをOutlookスケジュールに登録

Graph APIはMicrosoft Cloudサービスリソースへのアクセスを可能にするAPIです。

kintoneアプリの作成

kintoneアプリストア (External link) にある「 To Do (External link) 」アプリを使います。
アプリに以下のフィールドを追加してください。

フィールド名 フィールドタイプ フィールドコード
イベントID 文字列(1行) EventId

また、アプリストアの「To Do」アプリは「開始日」「終了日」が「日付」フィールドとなっているため、「日時」フィールドに変更してください。

フィールド名 フィールドタイプ フィールドコード
開始日時 日時 From
終了日時 日時 To

こちらがアプリの配置したフィールドのフォーム画面です。

Microsoft Entra IDアプリケーションの登録

下準備

Outlookとkintoneを連携させるために、Microsoft Entra IDへアプリケーションを登録する必要があります。
事前に以下を行ってください。

  1. Microsoftアカウントを取得します。
  2. 1で取得したアカウントを使って Azure Portal (External link) へログインします。

アプリケーションの登録

Outlook連携 - kintoneからOutlookメールの送受信をしよう Microsoft Entra IDアプリケーションの登録 を参考に作成します。

今回使用するプログラムの準備

サンプルプログラム一式

SAMPLE-kintone-connect-azure (External link) にプログラムを置いています。

入手方法
  1. https://github.com/kintone-samples/SAMPLE-kintone-connect-azure/releases/tag/1.1.0 (External link) へアクセスします。
  2. [Assets]の「Source code(zip)」をクリックし、zipファイルをダウンロードします。
  3. ファイルを解凍します。
利用するプログラムファイル
common-js-functions.min.js

共通処理を記述するプログラムです。
commonディレクトリ内にあります。

kintone-connect-outlook-schedule-common.js

kintoneアプリ/Microsoftアプリの設定を記述するプログラムです。
環境に合わせて修正する部分があります(後述)。
outlook-scheduleディレクトリ内にあります。

oauth.js

OAuth2.0を利用してAzureへの認証処理を行うプログラムです。
commonディレクトリ内にあります。

kintone-connect-outlook-schedule.js

アクセストークンを用いてOutlookへスケジュールデータの登録/更新/削除するプログラムです。
outlook-scheduleディレクトリ内にあります。

kintone JS SDK v0.7.0

kintone REST APIを便利に扱うことができるライブラリです。
詳しくは kintone JS SDK を参照してください。

入手方法
  1. kintone-labs/kintone-js-sdk (External link) にアクセスします。
  2. [Clone or download]ボタンをクリックし、zipファイルをダウンロードします。
  3. ファイルを解凍し、dist下の「kintone-js-sdk.min.js」を利用します。

kintone UI Component (External link) v0.4.2

kintoneライクなUIパーツを簡単に作成できるライブラリです。
詳しくは以下の記事を参照してください。 「kintone UI Component v0」を使って簡単にkintoneライクなUIを設置する

入手方法
  1. kintone-labs/kintone-ui-component v0.4.2 (External link) にアクセスします。
  2. 「Assets」の[Source code]をクリックし、zipファイルをダウンロードします。
  3. ファイルを解凍し、dist下の「kintone-ui-component.min.js」「kintone-ui-component.min.css」を利用します。

その他のライブラリ

また、今回は上記プログラム以外に次のライブラリを利用します。

  • MSAL.js
    AzureからOAuth認証でアクセストークンを取得するプログラムです。
    https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/msal.js
  • jQuery v3.4.1
    https://js.cybozu.com/jquery/3.4.1/jquery.min.js
  • Sweet Alert2 v8.17.6
    • https://js.cybozu.com/sweetalert2/v8.17.6/sweetalert2.min.js
    • https://js.cybozu.com/sweetalert2/v8.17.6/sweetalert2.min.css

プログラムの修正

kintone-connect-outlook-schedule-common.jsを修正します。

プログラムの7行目を アプリケーションの登録 でメモしておいたアプリケーションIDに変更してください。
kintoneアプリのフィールドコードが kintoneアプリの作成 に記載したフィールドコードと異なる場合には、25行目〜41行目を修正してください。

 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
// kintone-connect-outlook-schedule-common.js

window.kintoneAzureConnect = {

  config: {
    auth: {
      clientId: '####################',
      authority: 'https://login.microsoftonline.com/common'
    },
    cache: {
      cacheLocation: 'localStorage',
      storeAuthStateInCookie: true
    }
  },

  graphApiScorp: {
    scopes: ['calendars.readwrite'],
  },

  eventUrl: 'https://graph.microsoft.com/v1.0/me/events',

  kintone: {
    fieldCode: {

      // Field code of subject
      subject: 'To_Do',

      // Field code of body
      body: 'Details',

      // Field code of start
      startDate: 'From',

      // Field code of end
      endDate: 'To',

      // Field code of eventId
      eventId: 'EventId',

      // Field code of attachFile
      attachFile: 'Attachments'
    }
  }
};

プログラムの配置

これらのプログラムを「アプリの設定 > JavaScript/CSSでカスタマイズ」下に配置します。

プログラムの解説

Azureへの認証処理 (oauth.js

Azureとの認証部分の処理をまとめています。

 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
// oauth.js

/* global Msal */
(function() {
  'use strict';

  var KAC = window.kintoneAzureConnect;

  var azureOauth = {

    userAgentApplication: null,

    getUserInfo: function() {
      return this.userAgentApplication.getAccount().userName;
    },

    init: function() {
      var self = this;
      self.userAgentApplication = new Msal.UserAgentApplication(KAC.config);
    },

    signIn: function() {
      var self = this;
      return self.userAgentApplication.loginPopup(KAC.graphApiScorp);
    },

    signOut: function() {
      var self = this;
      self.userAgentApplication.logout();
    },

    callGraphApi: function() {
      var self = this;
      return self.userAgentApplication.acquireTokenSilent(KAC.graphApiScorp);
    }
  };

  window.azureOauth = azureOauth || {};
}());

kintoneイベントの処理 (kintone-connect-outlook-schedule.js

kintoneの各イベント処理は以下となっています。

レコード一覧画面を表示した後のイベント
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
  kintone.events.on('app.record.index.show', function(event) {

    kintoneScheduleService.init();

    /* create kintone ui */
    kintoneScheduleService.uiCreateForIndex(kintone.app.getHeaderSpaceElement());

    // init process
    outlookAPI.init();

    // click sign in button
    kintoneScheduleService.data.ui.btnSignIn.on('click', function() {
      outlookAPI.signIn();
    });

    // click sign out button
    kintoneScheduleService.data.ui.btnSignOut.on('click', function() {
      outlookAPI.signOut();
    });
  });
レコード詳細画面を表示した後のイベント
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
  kintone.events.on('app.record.detail.show', function(event) {
    var record = event.record;

    kintoneScheduleService.init();
    kintoneScheduleService.uicreateForDetail();

    kintoneScheduleService.data.ui.btnSendEvent.on('click', function() {
      // Confirm whether to execute
      Swal.fire({
        title: kintoneScheduleService.setting.i18n.message.info.confirmRegister,
        type: 'warning',
        confirmButtonColor: '#DD6B55',
        confirmButtonText: kintoneScheduleService.setting.i18n.button.registerExec,
        cancelButtonText: kintoneScheduleService.setting.i18n.button.cancelExec,
        showCancelButton: 'true',
        allowOutsideClick: false
      }).then(function(isConfirm) {
        if (isConfirm.value) {
          return kintoneScheduleService.checkDateTime(record).then(function(isAllDay) {
            outlookAPI.setDataForRegistration(record, isAllDay);
          }, function() {
            KC.ui.loading.hide();
          });
        }
      }, function(dismiss) {
        KC.ui.loading.hide();
      });
    });
  });
レコード作成/編集画面を表示した後のイベント
801
802
803
804
805
806
807
808
809
810
  kintone.events.on(['app.record.create.show',
    'app.record.edit.show', 'app.record.index.edit.show'], function(event) {
    var record = event.record;
    if (event.type === 'app.record.create.show') {
      record[EVENT_ID_FIELD_CODE].value = '';
    }
    record[EVENT_ID_FIELD_CODE].disabled = true;

    return event;
  });
レコードの保存に成功した後のイベント
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
  kintone.events.on('app.record.edit.submit.success', function(event) {
    var record, eventId;
    record = event.record;

    kintoneScheduleService.init();

    if (record[EVENT_ID_FIELD_CODE] === undefined) {
      return event;
    }

    eventId = record[EVENT_ID_FIELD_CODE].value;
    if (eventId === '' || !kintoneScheduleService.isExpireAccessToken()) {
      return event;
    }

    return new kintone.Promise(function(resolve, reject) {
      Swal.fire({
        title: kintoneScheduleService.setting.i18n.message.info.confirmUpdate,
        type: 'warning',
        confirmButtonColor: '#DD6B55',
        confirmButtonText: kintoneScheduleService.setting.i18n.button.updateExec,
        cancelButtonText: kintoneScheduleService.setting.i18n.button.cancelExec,
        showCancelButton: 'true',
        allowOutsideClick: false
      }).then(function(isConfirm) {
        if (isConfirm.dismiss === 'cancel') {
          event.error = 'ダメです';
          return event;
        }
        return kintone.Promise.all([kintoneScheduleService.checkDateTime(record), outlookAPI.getToOutlook(record)]);
      }).then(function(respdata) {
        if (respdata.type === 'app.record.edit.submit.success') {
          return resolve(event);
        }
        return outlookAPI.setDataForUpdate(record, respdata[0]);
      }).then(function() {
        resolve(event);
      }).catch(function(error) {
        return Swal.fire({
          title: 'Error!',
          type: 'error',
          text: kintoneScheduleService.setting.i18n.message.error.updateFailure,
          allowOutsideClick: false
        }).then(function() {
          KC.ui.loading.hide();
          resolve(event);
        });
      });
    });
  });
レコードを削除する前のイベント
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
  kintone.events.on('app.record.detail.delete.submit', function(event) {
    var record = event.record;
    var eventId = record[EVENT_ID_FIELD_CODE].value;
    if (!kintoneScheduleService.isExpireAccessToken() || eventId === '') {
      return event;
    }

    return new kintone.Promise(function(resolve, reject) {
      Swal.fire({
        title: kintoneScheduleService.setting.i18n.message.info.confirmDelete,
        type: 'warning',
        confirmButtonColor: '#DD6B55',
        confirmButtonText: kintoneScheduleService.setting.i18n.button.deleteExec,
        cancelButtonText: kintoneScheduleService.setting.i18n.button.cancelExec,
        showCancelButton: 'true',
        allowOutsideClick: false
      }).then(function(isConfirm) {
        if (isConfirm.dismiss === 'cancel') {
          return resolve(false);
        }
        return outlookAPI.getToOutlook(record);
      }).then(function(respdata) {
        if (respdata === undefined) {
          KC.ui.loading.hide();
          return resolve(false);
        }
        return outlookAPI.setDataForDelete(record);
      }).then(function(resp) {
        resolve(event);
      }).catch(function() {
        Swal.fire({
          title: 'Error!',
          type: 'error',
          text: kintoneScheduleService.setting.i18n.message.error.deleteFailure,
          allowOutsideClick: false
        }).then(function() {
          KC.ui.loading.hide();
          resolve(true);
        });
      });
    });
  });

Outlookモジュール (kintone-connect-outlook-schedule.js

kintoneとOutlookのコネクション部分を一部抜粋して説明します。

MSAL.jsのインスタンス化

userAgentApplicationとしてインスタンス化し、以降の処理に利用します。
oauth.jsの処理を呼び出しています。

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
  var outlookAPI = {

    init: function() {

      AO.init();
      if (!kintoneScheduleService.isExpireAccessToken() || !kintoneScheduleService.isSignUserDispInfo()) {
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookHeaderNotSigned.style.display = 'inline-block';
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookHeaderSigned.style.display = 'none';
      } else {
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookUserInfo.setText(storage.getItem('SIGN_USER_DISPINFO'));
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookHeaderNotSigned.style.display = 'none';
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookHeaderSigned.style.display = 'inline-block';
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookUserInfo.element.style.display = 'inline-block';
        kintoneScheduleService.data.isLoginOutlook = true;
      }
    },
Azureからトークンを取得する

サインイン処理では、まずAzureからIDトークンを取得します。
oauth.jsの処理を呼び出しています。

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
    signIn: function() {
      var self = this;
      KC.ui.loading.show();

      AO.signIn().then(function(id_token) {
        self.callGraphApi();
        KC.ui.loading.hide();
      }, function(error) {
        Swal.fire({
          title: 'Error!',
          type: 'error',
          text: kintoneScheduleService.setting.i18n.message.error.signInFailure,
          allowOutsideClick: false
        });
        KC.ui.loading.hide();
      });
    },

    signOut: function() {
      KC.ui.loading.show();
      storage.clear();
      AO.signOut();
      KC.ui.loading.hide();
    },

そして取得したIDトークンを用いてアクセストークンを取得します。

370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
    callGraphApi: function() {
      var self = this;

      AO.callGraphApi().then(function(token) {
        var userInfo = AO.getUserInfo();

        kintoneScheduleService.data.ui.kintoneCustomizeOutlookHeaderNotSigned.style.display = 'none';
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookHeaderSigned.style.display = 'inline-block';
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookUserInfo.setText(userInfo);
        kintoneScheduleService.data.ui.kintoneCustomizeOutlookUserInfo.element.style.display = 'inline-block';
        kintoneScheduleService.data.isLoginOutlook = true;

        storage.setItem('SESSION_KEY_TO_ACCESS_TOKEN', token.accessToken);
        storage.setItem('SIGN_USER_DISPINFO', userInfo);

        KC.ui.loading.hide();
      }, function(error) {
        if (error) {
          Swal.fire({
            title: 'Error!',
            type: 'error',
            text: kintoneScheduleService.setting.i18n.message.error.getAccessTokenFailure,
            allowOutsideClick: false
          });
          self.userAgentApplication = null;
          KC.ui.loading.hide();
        }
      }).then(function() {
        KC.ui.loading.hide();
      }).catch(function() {
        KC.ui.loading.hide();
      });
    },
取得したアクセストークンを用いたスケジュールデータの登録

GraphAPIをkintone上で叩いてOutlookのスケジュールデータを登録しています。
外部APIをkintone上でたたく方法は 外部のAPIを実行する を参照してください。

516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
    registerToOutlookForAttachement: function(index, file, eventId, accessToken) {
      var self = this;
      var url = EVENT_URL + '/' + eventId + '/attachments';
      var header = {
        'Authorization': 'Bearer ' + accessToken,
        'Content-Type': 'application/json'
      };

      return kintone.proxy(url, 'POST', header, file[index]).then(function(respdata) {
        var responseDataJson = window.JSON.parse(!respdata[0] ? '{}' : respdata[0]);
        if (typeof responseDataJson.error !== 'undefined') {
          throw responseDataJson;
        }

        if (index + 1 < file.length) {
          self.registerToOutlookForAttachement(index + 1, file, eventId, accessToken);
        }
      });
    },

動作確認

kintoneアプリのレコード一覧画面を開きます。
「Outlookにログイン」というボタンが表示されるので、ボタンをクリックします。

OAuth認証画面が表示されるので、「Microsoft Entra IDアプリケーションの登録」の 下準備 で準備したMicrosoftアカウントを使ってログインします。

メールアドレスを入力したあとにエラーが表示される場合、Microsoftアプリの設定を確認してください。

ログインに成功した場合、先ほどまで「Outlookにログイン」だったボタンが「Outlookからログアウト」に変わります。

kintoneのレコードを追加します。

レコードを保存すると、レコード詳細画面の上部に「予定を登録」というボタンが表示されるので、ボタンをクリックすればイベントを登録できます。

無事kintoneのレコードの内容がOutlookのスケジュールに登録されました。

変更履歴

  • 2019/11/18
    以下の利用ライブラリ変更に伴い、ソースコードを修正しました。
    • kintone Utility Library for JavaScriptからkintone JS SDKに変更
    • kintone UI Componentを追加
    • jQuery v3.2.1からv3.4.1に変更
    • SweetAlert2 v6.10.1からv8.17.6に変更
    • Microsoft Graph v1からv2に変更
information

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