見出し画像

Google Apps Script - 備忘録 - 001

  • Google Apps Scriptの練習のために簡単なWebアプリを作成してみました。

  • ドットインストールの「詳解PHP ウェブ開発編」を参考にさせて頂いております。

※プログラミング初級者が勉強の為の備忘録として書き綴ったメモとして利用しているため、内容に誤りもあるかと思いますが多めに見て下さると幸いです。


詳解PHP ウェブ開発編 » #03 PHPのコードを埋め込んでみよう

以下、お手本のコード。
(有料コンテンツなので一部だけ)

<p>Today: <?= date('Y-m-d H:i:s l'); ?></p>

以下、GASのコード。

// コード.gs

function doGet(e) {
  let temp = HtmlService.createTemplateFromFile('index');
  let today = new Date();
  today = replaceFormat(today, 'y-m-d h:n:s w');
  temp.today = today;
  let output = temp.evaluate();
  output.setTitle('PHP Practice');
  return output;
}

function replaceFormat(today, format) {
  let str = format;
  str = str.replace(/y/, today.getFullYear());
  str = str.replace(/m/, today.getMonth() + 1);
  str = str.replace(/d/, today.getDate());
  str = str.replace(/h/, today.getHours());
  str = str.replace(/n/, today.getMinutes());
  str = str.replace(/s/, today.getSeconds());
  str = str.replace(/w/, getWeekName(today.getDay()));
  return str;
}

function getWeekName(weekNum) {
  let weekName = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday'
  ]
  return weekName[weekNum];
}
<!-- index.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <p>Hello, PHP!</p>
    <p>Today: <?= today; ?></p>
  </body>
</html>
  • JavascriptにはDateオブジェクトに表示形式を設定するメソッドがないので自作の「replaceFormat」関数を作成。

  • 同様に、曜日を表示する関数もないので自作。→「getWeekName」

日付を埋め込むだけでこんなに記述量が必要とは。。
いきなりGASとPHPとの格差を体感。。
PHP便利。

詳解PHP ウェブ開発編 » #04 htmlspecialchars()を使ってみよう

以下、お手本のコード。
(有料コンテンツなので一部だけ)

$name = 'Taro <script>alert(1);</script>';
<p>Hello, <?= htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); ?>!</p>

以下、GASのコード。

// コード.gs

function doGet(e) {
  let temp = HtmlService.createTemplateFromFile('index');
  temp.name = 'Taro <script>alert(1)</script>';
  let output = temp.evaluate();
  output.setTitle('PHP Practice');
  return output;
}
<!-- index.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <p>Hello, <?= name; ?>!</p>
  </body>
</html>
  • GASだとhtmlspecialchars関数を使わなくても”文字実体参照”にしてくれるみたい。

  • ただ、<?= name; ?> のところを <?!= name; ?> に変えるとアラートが生じるようになります。

Standard scriptlets
Standard scriptlets, which use the syntax <? ... ?>, execute code without explicitly outputting content to the page. However, as this example shows, the result of the code inside a scriptlet can still affect the HTML content outside of the scriptlet:

Printing scriptlets
Printing scriptlets, which use the syntax <?= ... ?>, output the results of their code into the page using contextual escaping.
Contextual escaping means that Apps Script keeps track of the output’s context on the page — inside an HTML attribute, inside a client-side script tag, or anywhere else — and automatically adds escape characters to protect against cross-site scripting (XSS) attacks.
In this example, the first printing scriptlet outputs a string directly; it is followed by a standard scriptlet that sets up an array and a loop, followed by another printing scriptlet to output the contents of the array.

Force-printing scriptlets
Force-printing scriptlets, which use the syntax <?!= ... ?>, are like printing scriptlets except that they avoid contextual escaping.
Contextual escaping is important if your script allows untrusted user input. By contrast, you’ll need to force-print if your scriptlet’s output intentionally contains HTML or scripts that you want to insert exactly as specified.
As a general rule, use printing scriptlets rather than force-printing scriptlets unless you know that you need to print HTML or JavaScript unchanged.

https://developers.google.com/apps-script/guides/html/templates

標準スクリプトレット
構文<?...?>を使用する標準スクリプトレット、コンテンツをページに明示的に出力せずにコードを実行します。ただし、この例が示すように、スクリプトレット内のコードの結果は、スクリプトレット外のHTMLコンテンツに影響を与える可能性があります。

スクリプトレットの印刷
構文<?= ...?>を使用するスクリプトレットを印刷すると、コンテキストエスケープを使用してコードの結果がページに出力されます。コンテキストエスケープとは、AppsScriptがページ上の出力のコンテキストをHTML属性内で追跡することを意味します。クライアント側のスクリプトタグ内、またはその他の場所—クロスサイトスクリプティング(XSS)攻撃から保護するために、エスケープ文字を自動的に追加します。この例では、最初の印刷スクリプトレットが文字列を直接出力します。その後に、配列とループを設定する標準のスクリプトレットが続き、その後に、配列の内容を出力する別の印刷スクリプトレットが続きます。

スクリプトレットを強制印刷する
構文<?!= ...?>を使用するスクリプトレットの強制印刷は、コンテキストエスケープを回避することを除いて、スクリプトレットの印刷に似ています。スクリプトで信頼できないユーザー入力が許可されている場合は、コンテキストエスケープが重要です。対照的に、スクリプトレットの出力に、指定どおりに正確に挿入するHTMLまたはスクリプトが意図的に含まれている場合は、強制印刷する必要があります。原則として、必要があることがわかっている場合を除き、スクリプトレットを強制印刷するのではなく、スクリプトレットを印刷することを使用してください。 HTMLまたはJavaScriptを変更せずに印刷します。

  • 上記はむりやりGoogle翻訳した結果。

  • エスケープは『< や > といった特殊記号を文字列に変換する』ということみたいです。


詳解PHP ウェブ開発編 » #06 配列を埋め込んでみよう

以下、お手本のコード。
(有料コンテンツなので一部だけ)

  <ul>
  <?php if (empty($names)) { ?>
    <li>Nobody!</li>
  <?php } else { ?>
  <?php foreach ($names as $name) { ?>
    <li><?= h($name); ?></li>
  <?php } ?>
  <?php } ?>
  </ul>

以下、GASのコード。

// コード.gs

function doGet(e) {
  let temp = HtmlService.createTemplateFromFile('index');
  let names = [
    // 'Taro',
    // 'Jiro',
    // 'Saburo'
  ];
  temp.names = names;
  let output = temp.evaluate();
  output.setTitle('PHP Practice');
  return output;
}
<!-- index.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <ul>
      <? if (names.length == 0) { ?>
        <li>Nobody!</li>
      <? } else { ?>
        <? for (let i in names) { ?>
          <li><?= names[i]; ?></li>
        <? } ?>
      <? } ?>
    </ul>
  </body>
</html>
  • Javascriptにはempty関数と同じ処理の関数が無かった(もしあったらすみませんmm)ので、lengthプロパティを使用して代用しました。

  • PHPとJavascript、制御構文が似てるから関数も同じものが用意されてるかと思ってたけど、色々差がありますのね。

詳解PHP ウェブ開発編 » #10 フォームから値を受け取ってみよう

以下、お手本のコード。
(有料コンテンツなので一部だけ)

  <form action="result.php" method="get">
    <input type="text" name="message">
    <input type="text" name="username">
    <button>Send</button>
  </form>
$message = filter_input(INPUT_GET, 'message');
$username = filter_input(INPUT_GET, 'username');
  <p><?= h($message); ?> by <?= h($username); ?></p>
  <p><a href="index.php">Go back</a></p>

以下、GASのコード。

// コード.gs

function doGet(e) {
  let page = e.parameter['page'];

  if (page == null || page == 'index') {
    let temp = HtmlService.createTemplateFromFile('index');
    let output = temp.evaluate();
    output.setTitle('PHP Practice');
    return output;
  } else if (page == 'result') {
    let temp = HtmlService.createTemplateFromFile('result');
    let message = e.parameter['message'];
    let username = e.parameter['username'];
    temp.message = message;
    temp.username = username;
    let output = temp.evaluate();
    output.setTitle('PHP Practice');
    return output;
  }
}
<!-- index.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <form action="https://script.google.com/macros/s/☆ID☆/exec" method="GET">
      <input type="hidden" name="page" value="result">
      <input type="text" name="message">
      <input type="text" name="username">
      <button>Send</button>
    </form>
  </body>
</html>
<!-- result.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <p><?= message; ?> by <?= username ?></p>
    <p><a href="https://script.google.com/macros/s/☆ID☆/exec?page=index">Go back</a></p>
  </body>
</html>
  • GASだとformタグのaction属性にはアプリのURLを入力するので、htmlファイルを指定できない。

  • なので、hiddenタイプのinputタグを使ってhtmlファイルの名前を指定しました。

  • doGet関数でhtmlファイル名による分岐をし、それぞれの処理をしています。

詳解PHP ウェブ開発編 » #11 入力内容をチェックしよう

以下、お手本のコード。
(有料コンテンツなので一部だけ)

$message = trim(filter_input(INPUT_GET, 'message'));
$message = $message !== '' ? $message : '...';

以下、GASのコード。

// コード.gs

function doGet(e) {
  let page = e.parameter['page'];

  if (page == null || page == 'index') {
    let temp = HtmlService.createTemplateFromFile('index');
    let output = temp.evaluate();
    output.setTitle('PHP Practice');
    return output;
  } else if (page == 'result') {
    let temp = HtmlService.createTemplateFromFile('result');
    let message = e.parameter['message'];
    message = message.trim();
    message = message !== '' ? message: '...';
    temp.message = message;
    let output = temp.evaluate();
    output.setTitle('PHP Practice');
    return output;
  }
}
<!-- index.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <form action="https://script.google.com/macros/s/☆ID☆/exec" method="GET">
      <input type="hidden" name="page" value="result">
      <input type="text" name="message">
      <button>Send</button>
    </form>
  </body>
</html>
<!-- result.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <p><?= message; ?></p>
    <p><a href="https://script.google.com/macros/s/☆ID☆/exec?page=index">Go back</a></p>
  </body>
</html>
  • PHPはtrim関数、Javascriptはstringオブジェクトのtrimメソッド。

  • この違い毎回戸惑う。

詳解PHP ウェブ開発編 » #12 textareaから値を受け取ろう

以下、お手本のコード。
(有料コンテンツなので一部だけ)

<p><?= nl2br(h($message)); ?></p>

以下、GASのコード。

// コード.gs

function doGet(e) {
  let page = e.parameter['page'];

  if (page == null || page == 'index') {
    let temp = HtmlService.createTemplateFromFile('index');
    let output = temp.evaluate();
    output.setTitle('PHP Practice');
    return output;
  } else if (page == 'result') {
    let temp = HtmlService.createTemplateFromFile('result');
    let message = e.parameter['message'];
    message = message.trim();
    message = message !== '' ? message: '...';
    message = nl2arr(message);
    temp.message = message;
    let output = temp.evaluate();
    output.setTitle('PHP Practice');
    return output;
  }
}

function nl2arr(str) {
  return str.split(/\n/);
}
<!-- index.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <form action="https://script.google.com/macros/s/☆ID☆/exec" method="GET">
      <input type="hidden" name="page" value="result">
      <textarea name="message"></textarea>
      <button>Send</button>
    </form>
  </body>
</html>
<!-- result.html -->

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <? for (let i in message) { ?>
      <p><?= message[i]; ?></p>
    <? } ?>
    <p><a href="https://script.google.com/macros/s/☆ID☆/exec?page=index">Go back</a></p>
  </body>
</html>
  • Javascriptにnl2br関数がないので、nl2arrを自作。これで改行区切りの配列に変換してループ処理にした。

  • <?!=  ?>を使えば、配列になんかせずに改行文字を<br>に置換すれば済むのだが、XSS対策を考えると上述のやり方しかないか。。


1万文字を超えたのでここまで。

この記事が気に入ったらサポートをしてみませんか?