GASを用いて google フォームから google ドキュメントやスプレッドシートを経由してPDFファイルを送付する方法

# 概要

[google Apps Script (GAS)](https://developers.google.com/gsuite/aspects/appsscript?hl=ja) を用いて，google form でデータを収集し，google document and/or google spread sheet を経由し，PDFファイルを生成し，メールを送信するシステムを作成する簡単な例を紹介します。

状況設定はこちらになります．

<img width="886" alt="gas_gaiyou.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/b1f90744-960a-9337-575f-bc52af82074c.png">

ユーザーから google form で情報を収集し，google form のバインドされたGAS(コンテナバインドと呼ぶ)を用いて，goole document and/or google spread sheet のテンプレートを更新してユーザー向けの文書を生成し，それをPDF化してユーザーにメールで通知する，というシステムになります．

ここでは例として，google document と spread sheet の両方を使いますが，それぞれ独立したプログラムと設定なので，一つだけでも動きます．

## 基本事項 GASを用いたメール転送

GASを用いた一番簡単で便利な使い方である，メールを転送する基本の型をまずは知っておこう．一番よくある例はフォームの情報を，メールで転送する，というパターンであろう．

``` js:
FormApp.getActiveForm();
function form2email(e) {
        
  var email = ""; // default emails to know if it fails
  email = e.response.getRespondentEmail(); // get email from method or you can change anything you like. 
  var results = ""; // 全結果  
    
  var itemResponses = e.response.getItemResponses(); // get data from the form
  // store results and several keys
  var x = ""; // contents
  var y = ""; // results  

  itemResponses.forEach(function(itemResponse){
  x = itemResponse.getItem().getTitle()
  y = itemResponse.getResponse()  
    results += '【' + x + '】\n'
    results += y + '\n\n'
  });
      
  // create email 
  var subject = '[受領通知]';
  var emailbody = 'みなさま\n\n\n';
  emailbody += '受領したことを通知致します．\n\n';
  emailbody += '-------------------------------------------------------------\n\n';  
  emailbody += results
  var sender_email = "syamada@univ.ac.jp"; 
  var objArgs = {from:sender_email};
  GmailApp.sendEmail(email, subject, emailbody, objArgs);
}
```

この例では，メールの送信先は email という変数にformから取得したメールアドレスを設定している．これを，

``` js:
var email = 'syamada@univ.ac.jp,tanaka@univ.ac.jp';
```
のように，任意のメールアドレス(gmailでなくてもよい)を指定して転送することができる．

[Googleフォームに「控え」の送信機能が備わっていた](https://kurihara-office.com/170130google-form) にあるように，google form の控えを送信するだけでれば，Google form の 「回答のコピーを送信」というチェックボックスをONするだけで実行できる．

単純にメールを送るだけであれば，[GASでのメール送信についてまとめてみる](https://qiita.com/mistolteen/items/404c10657b1b27007a3c) が参考になりました．

この記事では，単純にメールを送るだけではなくて，あらかじめ用意しておいた google document or spreadsheet のテンプレートを書き換え、それを pdf 化してメールで送る，という例を紹介します．

## google form の設定

google form から，「メールアドレス」，「氏名」，「報告文」の３つの情報を集めるとします．「メールアドレス」は google form の自動収集機能を用いた場合は，

``` js:
  email = e.response.getRespondentEmail(); // get email from method 
```

として，[getRespondentEmail](https://developers.google.com/apps-script/reference/forms/form-response#getRespondentEmail())を用いて，メールアドレスを収集します．

その次に，このformから「スクリプトエディタ」を開きます．こうすると，自動的にこのformにバインドされたGASの設定になります．

<img width="827" alt="gas_make.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/0ad721b6-2578-3aa4-ef65-3792f11eada4.png">

## 書類テンプレートの作成

文書のテンプレートを作成します．
テンプレートには「input1」「input2」「input3」の３つのプレースホルダーが記述されているとします．これらを，GASの中で定義された「fname」「formattedDate」「houkoku」にそれぞれ変換し，新しい文書を作成し，PDF化してメール送信する，という作戦です．

<img width="511" alt="gas_template.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/fdf8970f-9b97-c899-0e44-105169afead2.png">


## 固有IDの取得

テンプレートのファイルの固有IDを取得します．https 以下の docs.google.com/document/d/**ID**/edit の **ID** がこのファイルの固有IDとなるので，これをGASの中で使います． 

(同様に，作業場所の固有ID も drive.google.com/drive/folders/**ID** にあるので，これもGASの中で使います． は古い記述。targetFolderIdの指定なくても動きます。2020.11.13追記)

## GAS の設定

### google ドキュメントから pdf生成とメール送信

google form に新規入力があったときに，form2doc.gs が動作し，google document の
テンプレートを更新して，メール送信します．

``` js:form2doc.gs
FormApp.getActiveForm();

// var targetFolderId = 'IDIDIDIDIDIDIDIDIDIDIDIDIDIDIDIDI' <== 更新ください

function form2doc(e) {
    
  // define the folder of your work
  var folder = DriveApp.getRootFolder();
  var folderFiles = folder.getFiles(); 
  
  // set the template file ID 
  var fileId = "FIFIFIFIFIFIFIFIFIFIFIFIFIFIFIFIF"  <== 更新ください
    
  var fname = ""; // name
  var houkoku = ""; // houkoku
  var email = ""; // default emails to know if it fails
  email = e.response.getRespondentEmail(); // get email from method 
  var results = ""; // 全結果  
    
  var itemResponses = e.response.getItemResponses(); // get data from the form
  // store results and several keys
  var x = ""; // contents
  var y = ""; // results  
  itemResponses.forEach(function(itemResponse){
  x = itemResponse.getItem().getTitle()
  y = itemResponse.getResponse()  
    results += '【' + x + '】\n'
    results += y + '\n\n'
    if(x == "氏名"){
      fname = y;}
    else if (x == "報告文"){
        houkoku = y;}
    else if (x == "メールアドレス"){
        email = y;}
  });

  
  // copy and update documents
  var date = new Date(); //ファイル名の日付部分作成
  var formattedDate = Utilities.formatDate(date, "JST",  "yyyy'年'MM'月'dd'日'");
  //コピー元のファイルを開く
  var file = DriveApp.getFileById(fileId);
  //コピーを作成，作成したコピーを参照
  var newFile = file.makeCopy(fname + ".doc");  
  //コピーしたファイルをすぐに更新するため，IDを取得
  var newid = newFile.getId();
  var doc = DocumentApp.openById(newid)
  var docbody = doc.getBody(); // documents の本体を取得  
  docbody.replaceText('input1',fname);  
  docbody.replaceText('input2',formattedDate);       
  docbody.replaceText('input3',houkoku);
  doc.saveAndClose(); // 強制的にファイルを更新する．
  // reopen the file
//  var newid = newFile.getId();  
  var document = DriveApp.getFileById(newid)  
  var link = 'https://docs.google.com/document/d/'+newid
  results += 'Your document is created at ' + link;
    
  // create email 
  var subject = '[受領通知]';
  var emailbody = fname + ' 様\n\n\n';
  emailbody += '受領したことを通知致します．\n\n';
  emailbody += '-------------------------------------------------------------\n\n';  
  emailbody += results
  
  Logger.log(email);    
  Logger.log(results);  
  Logger.log(link);

  // create pdf from the google document
  // DriveApp.getFolderById(targetFolderId).createFile(document.getAs(MimeType.PDF).setName(fname +  ".pdf")); // convert pdf
  var pdf = document.getAs(MimeType.PDF).setName(fname + ".pdf"); // get pdf 

  // set sharing for the google document 
  var access;
  var permission;
  access = DriveApp.Access.DOMAIN_WITH_LINK;
  permission = DriveApp.Permission.EDIT;
  document.setSharing(access, permission);
//  document.setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.VIEW);
//  document.addEditor(email);
  // send email with pdf 
  var sender_email = "syamada@univ.ac.jp"; 
  var objArgs = {from:sender_email, attachments:pdf};
  GmailApp.sendEmail(email, subject, emailbody, objArgs);
}
```

### google スプレッドシートから pdf生成とメール送信

google form に新規入力があったときに，form2sp.gs が動作し，google spreadsheet の
テンプレートを更新して，メール送信します．

``` js:form2sp.gs
FormApp.getActiveForm();

// var targetFolderId = 'IDIDIDIDIDIDIDIDIDIDIDIDIDIDIDIDI'  <== 更新ください

function form2sp(e) {
    
  // define the folder of your work
  var folder = DriveApp.getRootFolder();
  var folderFiles = folder.getFiles(); 
  
  // set the template file ID 
  var fileId = "FIFIFIFIFIFIFIFIFIFIFIFIFIFIFIFIF"   <== 更新ください
    
  var fname = ""; // name
  var houkoku = ""; // houkoku
  var email = ""; // default emails to know if it fails
  email = e.response.getRespondentEmail(); // get email from method 
  var results = ""; // 全結果  
    
  var itemResponses = e.response.getItemResponses(); // get data from the form
  // store results and several keys
  var x = ""; // contents
  var y = ""; // results  
  itemResponses.forEach(function(itemResponse){
  x = itemResponse.getItem().getTitle()
  y = itemResponse.getResponse()  
    results += '【' + x + '】\n'
    results += y + '\n\n'
    if(x == "氏名"){
      fname = y;}
    else if (x == "報告文"){
        houkoku = y;}
    else if (x == "メールアドレス"){
        email = y;}
  });

  
  // copy and update documents
  var date = new Date(); //ファイル名の日付部分作成
  var formattedDate = Utilities.formatDate(date, "JST",  "yyyy'年'MM'月'dd'日'");
  //コピー元のファイルを開く
  var file = DriveApp.getFileById(fileId);
  //コピーを作成，作成したコピーを参照
  var newFile = file.makeCopy(fname);  
  //コピーしたファイルをすぐに更新するため，IDを取得
  var newid = newFile.getId();
  var spreadsheet = SpreadsheetApp.openById(newid)
  // シートを取得する
  var sheet = spreadsheet.getSheets()[0] ;
  var textFinder = sheet.createTextFinder('input1');
  textFinder.replaceAllWith(fname);
  
  textFinder = sheet.createTextFinder('input2');  
  textFinder.replaceAllWith(formattedDate);

  textFinder = sheet.createTextFinder('input3');  
  textFinder.replaceAllWith(houkoku);
  
  SpreadsheetApp.flush();  // 変更内容を即反映
  // reopen the file
//  var newid = newFile.getId();  
  var document = DriveApp.getFileById(newid)  
  var link = 'https://docs.google.com/spreadsheets/d/'+newid
  results += 'Your spreadsheet is created at ' + link;
    
  // create email 
  var subject = '[受領通知]';
  var emailbody = fname + ' 様\n\n\n';
  emailbody += '受領したことを通知致します．\n\n';
  emailbody += '-------------------------------------------------------------\n\n';  
  emailbody += results
  
  Logger.log(email);    
  Logger.log(results);  
  Logger.log(link);

  // create pdf from the google document
  // DriveApp.getFolderById(targetFolderId).createFile(document.getAs(MimeType.PDF).setName(fname +  "_sp.pdf")); // convert pdf
  var pdf = document.getAs(MimeType.PDF).setName(fname + "_sp.pdf"); // get pdf 

  // set sharing for the google document 
  var access;
  var permission;
  access = DriveApp.Access.DOMAIN_WITH_LINK;
  permission = DriveApp.Permission.EDIT;
  document.setSharing(access, permission);
//  document.setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.VIEW);
//  document.addEditor(email);
  // send email with pdf 
  var sender_email = "syamada@univ.ac.jp"; 
  var objArgs = {from:sender_email, attachments:pdf};
  GmailApp.sendEmail(email, subject, emailbody, objArgs);
}
```

### トリガーの設定

「編集 」==> 「現在のプロジェクトのトリガー」を押して，それぞれの関数に対して，「フォーム送信時」のイベントとして登録する．

<img width="495" alt="gas_trigger.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/54de25e4-8614-d023-57be-e0ae60be219b.png">

基本設定は以上になります．


# 技術的な補足

## GAS関係

### 文字列変換

文字の変換方法は google ドキュメントとスプレッドシートで異なるので，注意が必要です．

google document の場合は，

``` js:
  var docbody = doc.getBody(); // documents の本体を取得  
  docbody.replaceText('input1',fname);  
  docbody.replaceText('input2',formattedDate);       
  docbody.replaceText('input3',houkoku);
  doc.saveAndClose(); // 強制的にファイルを更新する．
```

のように，[replaceText](https://developers.google.com/apps-script/reference/document/text#replaceText(String,String)) で変換して，強制的にファイル更新する．強制的に更新するのは，変更前のものがPDF化されるのを回避するためです．

google spreadsheet の場合は，

``` js:
  // シートを取得する
  var sheet = spreadsheet.getSheets()[0] ;
  var textFinder = sheet.createTextFinder('input1');
  textFinder.replaceAllWith(fname);
  
  textFinder = sheet.createTextFinder('input2');  
  textFinder.replaceAllWith(formattedDate);

  textFinder = sheet.createTextFinder('input3');  
  textFinder.replaceAllWith(houkoku);
``` 

のように，sheet を取得して，[createTextFinder](https://developers.google.com/apps-script/reference/spreadsheet/sheet#createTextFinder(String)) で検索して，[replaceAllWith](https://developers.google.com/apps-script/reference/spreadsheet/text-finder#replaceAllWith(String)) で変換します．

### ファイルのアクセス権限の設定

更新したユーザ用のファイルのアクセス権限は次のようにして設定しています．

``` js:
  // set sharing for the google document 
  var access;
  var permission;
  access = DriveApp.Access.DOMAIN_WITH_LINK;
  permission = DriveApp.Permission.EDIT;
  document.setSharing(access, permission);
//  document.setSharing(DriveApp.Access.PRIVATE, DriveApp.Permission.VIEW);
//  document.addEditor(email);
```

この例はドメインでの設定になります．他の例は，[DriveApp.Access](https://developers.google.com/apps-script/reference/drive/access)を参照ください．

もし個人のメールアドレスごとに設定したい場合は，[addEditor](https://developers.google.com/apps-script/reference/drive/file#addEditor(String)) 関数の引数にメールアドレスを入れます．

### pdf変換

PDFへの変換は，MineType.PDFを指定して，[createFile](https://developers.google.com/apps-script/reference/drive/drive-app#createFile(BlobSource)) を実行するだけでよい．

``` js:
var document = DriveApp.getFileById(newid)   
DriveApp.getFolderById(targetFolderId).createFile(document.getAs(MimeType.PDF).setName(fname +  "_sp.pdf")); // convert pdf
```

とするだけでよい．書き方はgoogle document でも spreadsheet でも変わらない．その理由は，Blobという概念があるらしく，
[GAS の Blob とファイル変換まとめ](https://qiita.com/a_eau_/items/01bc14bfc5cb5ef12e64) が参考になりました．


ただし，google spread sheet の場合は，グリッド線を消しておかないと，pdf にしたときにセルの線が出てしまう．

![スクリーンショット 2020-05-18 13.12.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179652/661fdf8c-e09c-e8cd-f715-0d615608fa0b.png)

## メールの送信方法

[GmailApp.sendEmail](https://developers.google.com/apps-script/reference/gmail/gmail-app#sendEmail(String,String,String,Object)ga) を使います．

``` gs:
  var sender_email = "syamada@rikkyo.ac.jp"; 
  var objArgs = {from:sender_email, attachments:pdf};
  GmailApp.sendEmail(email, subject, emailbody, objArgs);
```

sender_email はなくてもよいですが，エイリアスを張っているアドレスから送りたい場合に使います．PDFを添付して，emailbodyというテキストを subject というタイトルで email 宛に送信します．



---


[編集編集後記]

google の G Suite を用いた環境で使っていますので，別環境で動作が違う場合はすいません．

- 2020.5.18, first draft, 簡単なフォームで作成．(自分がGAS初心者)

### 関連ページ

https://qiita.com/yamadasuzaku/items/b1fe20d7e6c5a6c98f72