Google カレンダーを不特定多数と安全に共有（共同編集）するための Apps Script

# はじめに

パブリックなイベントカレンダーを不特定多数の人と共同編集したいニーズをよく見かけます。
Google カレンダーで匿名ユーザーに編集権限を与えれば不特定多数と共同編集できるようになりますが、これだと自分が登録したイベントを削除されたりしてしまいます。
そのため多くの場合、編集権限はパブリックにせず、編集権限を持った特定の管理者が依頼を受けてイベントの登録を行う運用にするケースが多いと思います。
しかし、この運用だと対応できる件数に限りがあります。
そこでこの記事では、Google フォームと Google Apps Script を使い、不特定多数の人がイベントを登録でき、かつ、自分が登録したイベントだけを削除できるカレンダーの作成方法をご紹介します。

# 用意するもの

- Google カレンダー（一般公開済み）
- イベント登録用 Google フォーム（項目は適当）
- イベント削除用 Google フォーム（項目は適当）

Google フォームの回答は Google スプレットシートにリンクしておいてください。

# Apps Script の有効化

Google フォームとリンクしているスプレットシートのそれぞれで `スクリプト エディタ` を起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113765/19dbc514-303d-c4e0-8368-aedc49d6d326.png)

とりあえずコードに `function myFunction(e) {}` と入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113765/cf4d2dd7-b074-bc35-331d-484e5518fecb.png)

トリガーも設定しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113765/f0eec2e4-f83d-c0cc-43b2-c19d47c479e6.png)

# イベント登録用 Google フォームの項目

型に気を付けつつ、カレンダーの登録に必要な項目を設定します。
ポイントは `削除キー` です。使い方は後々説明します。
なお `削除キー` にはシングルクオーテーションやダブルクオーテーションは使えないので、その旨を説明に記載しておくとよいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113765/6632d1c7-addd-9bcd-867e-3460d28bd9b2.png)

# イベント登録用 Apps Script のコード

イベント登録用 Apps Script の `コード.gs` に下記を貼り付けてください。

```コード.gs
function myFunction(e) {

  // トリガーされたスプレットシート（登録用）を取得
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  // 新規登録された行番号を取得
  var rowNum = sheet.getLastRow();

  // 各回答を取得
  var eventName     = sheet.getRange(rowNum, 2).getValue();           // イベント名
  var eventDate     = new Date(sheet.getRange(rowNum, 3).getValue()); // 開催日
  var startTime     = new Date(sheet.getRange(rowNum, 4).getValue()); // 開始時間
  var endTime       = new Date(sheet.getRange(rowNum, 5).getValue()); // 終了時間
  var eventLocation = sheet.getRange(rowNum, 6).getValue();           // 開催場所
  var eventDetails  = sheet.getRange(rowNum, 7).getValue();           // イベント詳細
  var deleteKey     = sheet.getRange(rowNum, 8).getValue();           // 削除キー

  // 開始日時を作成
  var startDateTime = new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate(), startTime.getHours(), startTime.getMinutes(), 0);
  // 終了日時を作成
  var endDateTime= new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate(), endTime.getHours(), endTime.getMinutes(), 0);

  // イベントIDを作成
  var eventID = ("00000000" + rowNum.toString()).slice(-8) + "-" + Utilities.getUuid().slice(-8);
  // イベントIDをイベント詳細の一番下に追記
  eventDetails = eventDetails + "\n\n" + "イベントID: " + eventID;

  // カレンダーを取得
  var calendar = CalendarApp.getCalendarById("xxxxxxxxxxxxxxxxxxxxxxxxxx@group.calendar.google.com");

  // カレンダーイベントを作成
  var rtn = calendar.createEvent(
    eventName,
    startDateTime,
    endDateTime,
    {
      location:    eventLocation,
      description: eventDetails
    }
  );

  // 「イベントID-削除キー」をスプレッドシートに記録
  var deleteToken = eventID + "-" + deleteKey;
  sheet.getRange(rowNum, 9).setValue(deleteToken);

  // 「カレンダーのID,イベントのID」をスプレッドシートに記録
  var calendarEventKeys = calendar.getId() + "," + rtn.getId();
  sheet.getRange(rowNum, 10).setValue(calendarEventKeys);
}
```

なお `xxxxxxxxxxxxxxxxxxxxxxxxxx@group.calendar.google.com` はカレンダー ID です。カレンダー ID はカレンダーの設定画面で `@group.calendar.google.com` を探せば見つかります。
また、`(rowNum, 2)` 等の数値（この場合は2）は、その項目がスプレットシートの左から何番目にあるかを意味しています。人によって異なるため、スプレットシートを見ながら調整してください。

ここで1度 `実行` を押してください。ログイン画面が表示されるので、指示に従って進めてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113765/6b64983c-e78e-6b37-a852-d5ea5580d7c3.png)

以上でフォームから回答を送信するとカレンダーにイベントが作成されるようになります。
イベント詳細にイベント ID が記載されていることを確認してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113765/6cc6a7a6-c1b8-29ec-3e35-7f1ce72c8cd0.png)

# イベント削除用 Google フォームの項目

イベント削除用のフォームには「イベントID」と「削除キー」を含めてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113765/53c3bfff-d34a-3b9d-5295-011ea7b6025e.png)

「削除キー」には、登録フォームで入力した「削除キー」と同じ値を入力します。
「イベントID」には、カレンダーに登録されたイベントの詳細の一番下に記載されていた値を入力します。

# イベント削除用 Apps Script のコード

イベント削除用 Apps Script の `コード.gs` に下記を貼り付けてください。

```コード.gs
function myFunction(e) {

  // スプレットシート（登録用）を取得
  var sheetReg = SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").getSheets()[0];
  // スプレットシート（登録用）の左から何番目の列に「イベントID-削除キー」を記録したか
  var deleteTokenColNum = 9
  // スプレットシート（登録用）の左から何番目の列に「カレンダーのID,イベントのID」を記録したか
  var calendarEventKeysColNum = 10
  
  // トリガーされたスプレットシート（削除用）を取得
  var sheetDel = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  // 新規登録された行番号を取得
  var rowNum = sheetDel.getLastRow();

  // 各回答を取得
  var eventID = sheetDel.getRange(rowNum, 2).getValue();   // イベントID
  var deleteKey = sheetDel.getRange(rowNum, 3).getValue(); // 削除キー

  // 「イベントID-削除キー」を作成
  var deleteToken = eventID + "-" + deleteKey;

  // 「イベントID-削除キー」でスプレットシート（登録用）を検索し、得られた「カレンダーのID,イベントのID」を使ってイベントを削除
  var textFinder = sheetReg.createTextFinder(deleteToken);
  var ranges = textFinder.findAll();
  for (var i = 0; i < ranges.length; i++) {
    if(Number(ranges[i].getColumn()) === deleteTokenColNum) {
      var searchedRowNum = ranges[i].getRow();
      var calendarEventKeys = sheetReg.getRange(searchedRowNum, calendarEventKeysColNum).getValue().split(",");
      var event = CalendarApp.getCalendarById(calendarEventKeys[0]).getEventById(calendarEventKeys[1]);
      event.deleteEvent();
    }
  }
}
```

なお `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` はスプレットシート（登録用）の ID です。スプレットシート（登録用）を開いた際のリンクバーから取得できます。
また、`deleteTokenColNum` や `calendarEventKeysColNum` の `=` 後の数値や、`(rowNum, 2)` 等の数値（この場合は2）は、その項目がスプレットシートの左から何番目にあるかを意味しています。人によって異なるため、スプレットシートを見ながら調整してください。

以上で設定は完了です。
イベント削除用 Google フォームから回答を送信し、イベントがカレンダーから削除されるか確認してください。


以上