Google Apps Script で Google Drive のフォルダ配下のオーナー権限を一括で譲渡する

## はじめに

Google Drive の Web UI 上で特定のフォルダ配下のファイル群のオーナー権限を譲渡しようとすると、ファイル毎に権限移譲の操作をしなければなりません。他のプロジェクトに移る際など、一括で権限を譲りたいケースがあるので、 Google Apps Script で一括で譲渡できるスクリプトを実装しました。

※ G Suite には、[あるユーザーの全てのファイルを他者に移譲する機能](https://support.google.com/a/answer/1247799?hl=ja)がありますが、これは基本的に離職時に一括移行するためのものです。今回作ったものは、プロジェクト毎にフォルダ管理している場合など、より細かい単位で権限移譲を行うためのものです。

## コード全文

コードは GitHub の [tanabee/chown-drive-folder](https://github.com/tanabee/chown-drive-folder) に上げています。

コード全文は以下で 30 行ほどで実装できます。以下に簡単にコードの説明をしていきます。

```JavaScript
var BASE_FOLDER_ID = PropertiesService.getScriptProperties().getProperty('FOLDER_ID');
var NEW_OWNER = PropertiesService.getScriptProperties().getProperty('NEW_OWNER');
var me = Session.getActiveUser().getEmail();

function main() {
  var folder = DriveApp.getFolderById(BASE_FOLDER_ID);
  chownWithFolder(folder);
}

function chownIfNeeded(fileOrFolder) {
  if (fileOrFolder.getOwner().getEmail() === me) {
    fileOrFolder.setOwner(NEW_OWNER);
  }
}

function chownWithFolder(folder) {
  chownIfNeeded(folder);

  var files = folder.searchFiles('"me" in owners');
  while (files.hasNext()) {
    var file = files.next();
    console.log('file: ', file.getName());
    chownIfNeeded(file);
  }

  var folders = folder.getFolders();
  while (folders.hasNext()) {
    var folder = folders.next();
    console.log('folder: ', folder.getName());
    chownWithFolder(folder);
  }
}
```

## コードの説明

最初の 2 行でフォルダの ID と新しく指定するオーナーを [スクリプトプロパティ](https://developers.google.com/apps-script/reference/properties/properties-service) から取得します。スクリプトプロパティは Google Apps Script エディタの **ファイル > プロジェクトのプロパティ > スクリプトのプロパティ** から指定します。フォルダ ID は Google Drive でフォルダを開いた際の URL でいうと "drive.google.com/drive/u/0/folders/**[ここ]**" の部分に相当します。スクリプトプロパティを使わずに文字列をベタ書きしてもらっても大丈夫です。

```JavaScript
var BASE_FOLDER_ID = PropertiesService.getScriptProperties().getProperty('FOLDER_ID');
var NEW_OWNER = PropertiesService.getScriptProperties().getProperty('NEW_OWNER');
```

自分自身のメールアドレスは Session クラスから参照します。

```JavaScript
var me = Session.getActiveUser().getEmail();
```

実際に実行する関数です。[DriveApp.getFolderById()](https://developers.google.com/apps-script/reference/drive/drive-app#getfolderbyidid) で指定のフォルダを開いて、 chownWithFolder 関数に渡します。

```JavaScript
function main() {
  var folder = DriveApp.getFolderById(BASE_FOLDER_ID);
  chownWithFolder(folder);
}
```

実際に権限譲渡を行う関数です。自分がオーナーの場合に [setOwner](https://developers.google.com/apps-script/reference/drive/file#setowneruser) 関数を実行し、引数に譲渡するオーナーを指定します。

```JavaScript
function chownIfNeeded(fileOrFolder) {
  if (fileOrFolder.getOwner().getEmail() === me) {
    fileOrFolder.setOwner(NEW_OWNER);
  }
}
```

フォルダを再帰的に探索して、引数で指定されたフォルダ配下のファイル群とフォルダ群の権限変更を行います。  [Folder.searchFiles](https://developers.google.com/apps-script/reference/drive/folder#searchfilesparams) 関数と [Folder.getFolders](https://developers.google.com/apps-script/reference/drive/folder#getfolders) 関数の返り値は配列ではなく [イテレーター](https://developers.google.com/apps-script/reference/drive/file-iterator) であり、その後のループ処理の実装が配列の処理と異なるのはそのためです。

```JavaScript
function chownWithFolder(folder) {
  chownIfNeeded(folder);

  var files = folder.searchFiles('"me" in owners');
  while (files.hasNext()) {
    var file = files.next();
    console.log('file: ', file.getName());
    chownIfNeeded(file);
  }

  var folders = folder.getFolders();
  while (folders.hasNext()) {
    var folder = folders.next();
    console.log('folder: ', folder.getName());
    chownWithFolder(folder);
  }
}
```

## 使い方

[README](https://github.com/tanabee/chown-drive-folder/blob/master/README.ja.md) にまとめましたので、そちらを参照してください。

## まとめ

以上、権限一括譲渡のスクリプトを実装できました。手作業でポチポチするのはつらいので使ってみてください。本当は Drive の Add-ons の機能が用意されていればそちらで実装したかったのですが、現状は用意されていないようです。用意されたらそちらで実装してみようかと思います。

## 関連記事

- [GAS ビギナーが GAS を使いこなすために知るべきこと 10 選](https://qiita.com/tanabee/items/2c51681396fe12b6a0e4)
- [3 分で作る無料の翻訳 API with Google Apps Script](https://qiita.com/tanabee/items/c79c5c28ba0537112922)
- [スプレッドシートに OKR を書くとツリー構造で可視化されるツールを作ってみた](https://qiita.com/tanabee/items/3c17dcdbb8efb7afe212)