kintoneとOutlookを連携してスケジュール管理をする#01

## 経緯
kintoneのOutlookを連携してスケジュール管理をしたい。
けど検索して出てくる記事が古いため、アレンジしてなるべく最新のものに対応する。

要するに「[Outlook連携 - kintoneからOutlookスケジュールを登録しよう！！](https://developer.cybozu.io/hc/ja/articles/360022249111-Outlook%E9%80%A3%E6%90%BA-kintone%E3%81%8B%E3%82%89Outlook%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E7%99%BB%E9%8C%B2%E3%81%97%E3%82%88%E3%81%86-)」のOutlook連携をほぼサンプルのまま使っていたのだけど、先日急に動かなくなったので最新化対応したいということ。

### やりたいこと
- MSAL v2 に対応してkintoneとOutlookのスケジュールを連携させたい。
- kintone UI Component v1 対応したUIを構築したい。
- FullCalendar.js を導入してカレンダー表示させたい。

## 参考サンプル
こちらの２つのサンプルを参考にOutlook連携カスタマイズを行います。
1. [Outlook連携 - kintoneからOutlookスケジュールを登録しよう！！](https://developer.cybozu.io/hc/ja/articles/360022249111-Outlook%E9%80%A3%E6%90%BA-kintone%E3%81%8B%E3%82%89Outlook%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E7%99%BB%E9%8C%B2%E3%81%97%E3%82%88%E3%81%86-)
2. [クイックスタート: PKCE 対応の承認コード フローを使用して JavaScript SPA 内でユーザーをサインインさせ、アクセス トークンを取得する](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/quickstart-v2-javascript-auth-code)

## 今回（本記事）のゴール
一覧画面にkintone UI Component v1 対応したボタンを表示する。

## 記事一覧
1. #01.ボタンを表示 ← 当記事
2. [#02.サインイン](https://qiita.com/iNaoki04/items/207666c8daf3dcbd9ac8)
3. [#03.サインアウトとボタン表示制御](https://qiita.com/iNaoki04/items/059ef1d51f567416eb25)

## フォームの設定
kintoneサンプルに記載の内容で構築します。
アプリストアからToDoアプリを作成し、イベントIDを文字列で追加、開始日と終了日を日付から日時に変更してください。

|フィールド名|フィールドタイプ|フィールドコード|
|-|-|-|
|イベントID|文字列（1行）|EventId|
|ToDo名|文字列（１行）|To_Do|
|担当者|ユーザー選択|Assignees|
|優先度|ドロップダウン|Priority|
|締切日|日付|Duedate|
|開始日時|日時|From|
|終了日時|日時|To|
|詳細内容|文字列（複数行）|Details|
|添付ファイル|添付ファイル|Attachments|

## 登録するカスタマイズJS/CSS
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126147/fc4d64d7-3707-922b-3f11-577d8fbf0302.png)

### javascript
1. https://unpkg.com/kintone-ui-component/umd/kuc.min.js
2. https://js.cybozu.com/jquery/3.3.1/jquery.min.js
3. buttonSettings.js
4. ui.js

### css
1. buttons.css

## ソース

ボタン表示用の設定情報です。
内容はkintoneのサンプルのままです。
変更点は表示するボタンに対してclass指定をしています。

```butttonSettings.js
window.buttonSettings = {
    lang: {
        en: {
            button: {
                signIn: 'Sign in Outlook',
                signOut: 'Sign out of Outlook',
                addEvent: 'Add Event',
                getEvent: 'Get Event',
                setToday: 'Set Today',
                registerExec: 'Add',
                updateExec: 'Update',
                deleteExec: 'Delete',
                cancelExec: 'Cancel'
            },
            message: {
                info: {
                    confirmRegister: 'Do you want to add this event to Outlook?',
                    confirmUpdate: 'Do you want to update this event in Outlook?\n (kintone Record will be registered.)',
                    confirmDelete: 'Do you want to delete this event from Outlook?'
                },
                warning: {
                    noEvent: 'The target event does not exist in Outlook.'
                },
                success: {
                    registerExec: 'Your event has been added successfully.',
                    updateExec: 'Your event has been updated successfully.',
                    deleteExec: 'Your event has been deleted successfully.'
                },
                error: {
                    registerFailure: 'Failed to add your event.',
                    updateFailure: 'Failed to update your event.',
                    deleteFailure: 'Failed to delete your event.',
                    signInFailure: 'Failed to sign in Outlook.',
                    getAccessTokenFailure: 'Failed to get access token.',
                    fromtoDatetimeNodata: 'Start date/time or end date/time has not been entered.',
                    fromDatetimeFuture: 'Start date/time is not earlier than end date/time.',
                    addAttachFileFailure: 'Failed to add attachments.',
                    updateAttachFileFailure: 'Failed to update attachments.'
                }
            }
        },
        ja: {
            button: {
                signIn: 'Outlookにログイン',
                signOut: 'Outlookからログアウト',
                addEvent: '予定を登録',
                registerExec: '登録',
                updateExec: '更新',
                deleteExec: '削除',
                cancelExec: 'キャンセル'
            },
            message: {
                info: {
                    confirmRegister: 'Outlookに登録しますか?',
                    confirmUpdate: 'Outlookを更新しますか?\n (kintoneにはレコードが登録されます)',
                    confirmDelete: 'Outlookから削除しますか?'
                },
                warning: {
                    noEvent: '対象の予定がOutlookに存在しません'
                },
                success: {
                    registerExec: 'Outlookに登録しました',
                    updateExec: 'Outlookを更新しました',
                    deleteExec: 'Outlookから削除しました'
                },
                error: {
                    registerFailure: 'Outlookの登録に失敗しました',
                    updateFailure: 'Outlookの更新に失敗しました',
                    deleteFailure: 'Outlookの削除に失敗しました ',
                    signInFailure: 'サインインできませんでした',
                    getAccessTokenFailure: 'アクセストークンが取得できませんでした',
                    fromtoDatetimeNodata: '開始日時または終了日時が未入力です',
                    fromDatetimeFuture: '開始日時が未来日時になっています',
                    addAttachFileFailure: '添付ファイルの登録に失敗しました',
                    updateAttachFileFailure: '添付ファイルの更新に失敗しました'
                }
            }
        }
    },

    setting: {
        lang: 'ja',
        i18n: {},
        ui: {
            buttons: {
                signIn: {
                    text: 'signIn',
                    type: 'submit',
                    className: 'header-not-signed'
                },
                signOut: {
                    text: 'signOut',
                    type: 'normal',
                    className: 'header-signed'
                },
                sendEvent: {
                    text: 'addEvent',
                    type: 'submit',
                    className: 'header-signed'
                }
            }
        }
    },
};
```

ボタン表示の処理をします。
表示用の定義ファイルを読込み、ログインユーザーの言語設定に合わせてボタンの表示を行います。

```ui.js
// ボタンの表示情報を取得
let BS = window.buttonSettings;

// カスタマイズのメイン処理
let uiService = {
    setting: {
        lang: {},
        i18n: {},
        ui: {}
    },
    data: {
        ui: {}
    },

    // 初期処理
    init: function () {
        // ログインユーザーの言語設定を取得
        this.setting.lang = kintone.getLoginUser().language || 'ja';
        this.setting.i18n = this.setting.lang in BS.lang ? BS.lang[this.setting.lang] : BS.lang.en;
        this.setting.ui = BS.setting.ui;
    },

    // ボタン表示設定
    uiNotSignCreateForIndex: function (kintoneHeaderSpace) {
        if (typeof kintoneHeaderSpace === 'undefined') {
            return;
        }
        
        // サインインボタン表示
        this.data.ui.HeaderNotSigned = document.createElement('div');
        this.data.ui.btnSignIn = this.createButton(this.setting.ui.buttons.signIn, this.setting.i18n.button);
        this.data.ui.HeaderNotSigned.appendChild(this.data.ui.btnSignIn);
        kintoneHeaderSpace.appendChild(this.data.ui.HeaderNotSigned);
    },

    // ボタンを作成
    createButton: function (setting, lang) {
        if (typeof setting === 'undefined' || !setting) {
            return null;
        }

        let uiButton;
        let text = lang ? lang[setting.text] || setting.text || '' : setting.text || '';
        let type = setting.type;
        let className = setting.className ? setting.className : '' ;

        uiButton = new Kuc.Button({ text: text, type: type, className: className });
        return uiButton;
    },
};

// 画面表示時イベント
kintone.events.on('app.record.index.show', function (event) {
    // 初期処理
    uiService .init();
    // サインインボタンを表示
    uiService .uiNotSignCreateForIndex(kintone.app.getHeaderSpaceElement());
});
```

kintoneサンプルではjsでstyleを追加していましたが、Kucではclassまたはidを付与して設定します。
classの付与はjsのボタン生成時に行います。

```buttons.css
.header-not-signed > button {
  display: inline-block;
  margin: 0 15px 15px 15px;
}
```

### ボタンが表示されました
まだ何も動きません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126147/813304cc-d475-297a-fa05-5113ddd3c520.png)

## 次回
ボタンを押下した際にログインするようにします。