共有したスプレッドシートで編集している人の名前を取得したい

## 概要

GSuiteで複数ドメインをまたいで共有したスプレッドシートや、Googleアカウントで作成したスプレッドシートで、編集している人の名前を設定・取得するための方法です。


## 背景

スプレッドシートで、編集している人の情報を取得する方法として、Class Session のSession.getActiveUser().getEmail()や Session.getEffectiveUser().getEmail() を利用する方法が一般的です。

例えば、

- [GoogleSpreadsheet‎で入力したセルへ自動的に更新者情報のメモを入れる](https://qiita.com/tryforth/items/d2865a92f23e790b5d53)

- [共有したスプレッドシートを編集しているユーザ情報を取得したい](https://qiita.com/tanaike/items/9a50390ddfe5206bc07e)

などがあります。

しかし、Session.getActiveUser().getEmail() は、GSuiteの同じドメインでないと、正しく取得できません。

https://developers.google.com/apps-script/reference/base/session#getactiveuser
> However, these restrictions generally do not apply if the developer runs the script themselves or belongs to the same G Suite domain as the user.

Session.getEffectiveUser().getEmail() は挙動がわかりづらいですが、スクリプトを実行した人の情報を取得します。onEdit であれば、トリガーの設定は各自という扱いになるのでうまく取得できます。しかし、トリガーを開発者が設定した場合は、開発者の情報になってしまいます。他の人に共有していた場合でも、開発者になってしまう。

https://developers.google.com/apps-script/reference/base/session#geteffectiveuser
> Gets information about the user under whose authority the script is running. If the script is a web app set to "execute as me" (the developer), this returns the developer's user account. 

というわけで、複数ドメインの人たちとスプレッドシートを共有している場合や、そもそもGSuiteではなく、個人のGoogleアカウントを使っている場合でも、編集している人の情報を設定・取得するためのスクリプトを作ってみました。


## 実際の動き

アカウントの情報を取得できないのであれば、入力してもらおうという作戦です。
<img width="672" alt="スクリーンショット 2020-03-31 20.54.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83720/d961df98-5ba4-53e2-7c51-b10a52c13595.png">

自己申告で名前を入れてもらいます。
<img width="671" alt="スクリーンショット 2020-03-31 20.54.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83720/36fa0aaa-b00b-93fa-8d0c-2c8f93b73e9f.png">

編集すると、ノートに入力した内容と名前を残します。
<img width="672" alt="スクリーンショット 2020-03-31 20.55.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83720/2b5557ec-4933-5a50-6caa-220a92c996f9.png">

メニューから、名前を入力し直すこともできます。
<img width="672" alt="スクリーンショット 2020-03-31 20.57.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83720/9b6d3f13-b6e9-4d7a-f861-41aeac47883d.png">

名前を変更すると、その名前で履歴が残ります。
<img width="673" alt="スクリーンショット 2020-03-31 20.57.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83720/eaa3132e-84b3-7043-1962-1ee6211d43a8.png">

スプレッドシートを共有すると、他の人も同じ動きになります。名前を入力すると、その名前で履歴が残ります。
<img width="622" alt="スクリーンショット 2020-03-31 21.04.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83720/97a5c761-2640-221f-fc67-35e824601a8c.png">


## ソースコード

```{javascript}
/*
 * ユーザー名を設定・取得する
 * PropertiesService.getUserProperties(); を利用
 */
function getUser() {
  var properties = PropertiesService.getUserProperties();
  var user = properties.getProperty('user');
  
  // 既にユーザー名が保存されていたらそれを返す
  if (user) { return user; }

  // ユーザー名が設定されていなければ、入力してもらう
  while (user == null || user == 'cancel' || user == '') { // 文字が入力されるまで表示
    user = Browser.inputBox("あなたの名前を入力してください");
  }
  
  properties.setProperty("user", user); // ユーザー名を保存
  return user;
}

/*
 * ユーザー名を再設定する
 */
function setUser() {
  var properties = PropertiesService.getUserProperties();
  var user = properties.getProperty('user');
  
  user = Browser.inputBox('あなたの名前を入力してください' + '(現在の名前： ' + user + ')');
  if (user != null && user != 'cancel' && user != '') {
    properties.setProperty("user", user); // ユーザー名を保存
  }
  return user;
}

/*
 * スプレッドシートを開いたときにイベントを発動
 */
function onOpen(e) {
  // スプレッドシート開いた時に名前が入力されているか確認
  getUser();

  // メニューに追加
  SpreadsheetApp
  .getActiveSpreadsheet()
  .addMenu("設定", [
    {name: "名前を変更", functionName: "setUser"}
  ]);
  
}

/*
 * 編集時にイベントを発動
 */
function onEdit(e) {
  var range = e.range;
  var value = e.value;
  
  // セルの値が空欄の場合、replace()するとエラーになるので文字列に変えておく
  if (value == undefined) { value = ''; }
  
  range.setNote(
    range.getNote() + '\n' // これまでのノートを先頭につける
    + Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yy/MM/dd HH:mm') // 編集日付
    + ': '
    + value.replace(/\r?\n/g, ' ') // 改行を削除して現在のセルの値
    + ' (' + getUser() + ')' // 編集した人の名前
  );
}
```

## 雑な解説

名前は自己申告で入力なので、嘘つくこともできるので、あくまでも性善説で運用する必要があります。しかし、これが故に匿名ユーザーでも利用することができるので、わりと便利です。

人ごとに入力値を保存するのに、PropertiesService.getUserProperties() を利用しています。[ひこざるさん: Google Apps Scriptで設定ファイルの保存&#12539;読込](https://blog.hikozaru.com/2018/03/google-apps-script_31.html)　にわかりやすいサンプルがあります。

ここでは、onEdit()を使っていますが、トリガーを自分で設定しても正しく動きます。