PHP
gulp
HTMLメール
NIJIBOXDay 20

HTMLメールの手間がかかる部分をいくらか自動化する

More than 1 year has passed since last update.


はじめに

レガシーの亡霊が追いかけてくる皆様お元気ですか。

今回は言わずと知れたレガシーの最前線であるHTMLメール、そのめんどくさいところを気持ちばかりモダンに寄せて手間を省こうとしました。

メタ言語やタスクランナーには積極的に甘やかされていきたいですね。

なお具体的なOutlook対応などのコツなどはいくらでも知見がありますのでここでは割愛し、コーディング環境の構築と後処理の半自動化に絞っています。


要件


  • 色違い程度のバリエーションが複数あるし画像置き場のURLも納品直前まで未定だし、HTMLをテンプレート化したい

  • テーブルレイアウトを組みやすい Dreamweaver でプレビューしたい

  • CSSはメタ言語で書きたいが、最終的には Gmail 対応のためにインライン化したい

  • エディタやタスクランナーの都合で作業は UTF-8 で行いたいが、納品物は ISO-2022-JP にエンコードしたい

モダンとレガシーのせめぎあい。

手順が多すぎて修正するたびどこか忘れそうなのでこのへんを自動化するわけですが、落とし所はPHPとGulpの併用でした。


  • DreamweaverでプレビューできるPHPでHTMLをテンプレート化

  • CSSはStylusで書いてGulpでコンパイル

  • CSSのインライン化とcharsetの変更もGulpで

Dreamweaver使わないぞ、テーブルレイアウトなんてそらで組めるぞというちゃきちゃきのIE6世代であれば、むしろpugなりslimなりでゴリゴリ書くほうが最初から最後までGulpで一貫して処理できますので、そちらのほうがおすすめです。

私はにわかIE6世代なのでDreamweaver大先生に甘えました。


1. HTMLをPHPでテンプレート化する

PHPについては初学者もいいところで恐縮ですが、今回はHTMLに変数を突っ込みたいだけですので、下記の sample_red みたいなファイルをバリエーションの数だけ作り、共通の変数とかメール本文とかをパーツ分けするだけです。

あとでHTML化するときに正しいパスを渡せるよう、パスは __DIR__ をつけて書いておきます。


sample_red.php

<?php

require_once( __DIR__ . '/_includes/_vars.php' );
$template_id = 'red';
$bgcolor = '#ff0040';
$bgcolor_mso = '#29aee3';
require( __DIR__ . '/_includes/_template.php' );


_includes/_vars.php

<?php

$img_path = [
'common' => './img/common',
'current' => './img/sample'
// 画像置き場が決まったら下にすげ替える
// 'common' => 'https://example.com/img/common',
// 'current' => 'https://example.com/img/sample'
];


_includes/_template.php

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- 中略 -->
<link rel="stylesheet" media="all" href="./css/common.css" type="text/css">
</head>
<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<table cellpadding="0" cellspacing="0" border="0" align="center" width="99%" bgcolor="<?= $bgcolor ?>" class="l-wrapper-<?= $template_id ?>">
<tr>
<td width="100%">
<img src="<?= $img_path['common'] ?>/spacer.gif" alt="" width="*" height="20" class="u-spacer">
</td>
<!-- 以下略 -->


2. CSSをStylusで書く

background-image のパスを変数に入れておきたい程度の使用動機です。Outlookが background-image に非対応なので使わないに越したことはないのですが。

私がStylus派なのでこれを選びましたが、別にお好きな言語で大丈夫です。私は好きにしました。

普通にCSSファイルとして書き出せば大丈夫ですので詳細は割愛します。


_dev/stylus/common.styl

@charset 'utf-8';

$img_path = {
common : 'https://example.com/img/common',
current : 'https://example.com/img/sample'
}
.l-wrapper-red
margin 0 auto
width 600px
background #ff0040 url + \($img_path[common] + '/bg_red.png'\) top repeat-x
// 以下略



3. PHPをHTMLに変換する

「PHPファイルをHTMLへワンライナーで変換する簡単な方法」で詳しく説明されておられるほぼそのままです。

ただしテンプレートの中のパーツは変換不要ですので、ファイル名がアンダースコアから始まるものを除外するよう少しだけ書き換えました。


_func/_convert_to_html.php

<?php

$target_dir = '../';
$iterator = new RecursiveDirectoryIterator($target_dir);
$iterator = new RecursiveIteratorIterator($iterator);

$list = array();
foreach ($iterator as $fileinfo) {
if (!$fileinfo->isFile()) {
continue;
}

$file = $fileinfo->getPathname();
$info = new SplFileInfo($file);
if($info->getExtension() == 'php' && 0 !== strpos($info->getFilename(), '_')){
convertHtml($info);
}
}

function convertHtml($target) {
$contents = '';
echo $target;
ob_start();
include($target);
$contents = ob_get_contents();
ob_end_clean();

file_put_contents(
preg_replace('/\A(.+)\.(php?|html?)\z/', '$1.html', $target),
preg_replace('/\A\n/', '', $contents)
);
}


あとはPHPにパスが通っていればコマンド一発でHTMLファイルが生成されます。ありがたい……

php _convert_to_html.php


4. CSSをインライン化する

Gmailでは <style> に記述したCSSがすべてなかったことにされてしまうため(対応されたという噂も聞くものの確認できず)、スタイルをクラスではなくHTMLタグにstyle属性で直接書く必要があります。

手作業でひとつひとつやっていたらちょっとした修正でも発狂できますので gulp-inline-css でやります。

「HTMLメール作成時のインラインスタイル化をgulpで行う」をはじめ、詳しい記事がありますのでここではコードだけで失礼します。

ついでにHTMLをminifyしますが、納品後に編集されるであろうことを踏まえてコメントや改行は残しておきます。

とりあえず最低限これだけという感じで。


_dev/gulp.js

var gulp = require('gulp'),

plumber = require('gulp-plumber'),
inlineCSS = require('gulp-inline-css'),
htmlmin = require('gulp-htmlmin');

gulp.task('dest', function(){
gulp.src(['../*.html'])
.pipe(plumber())
.pipe(inlineCSS({applyStyleTags: false, removeStyleTags: false}))
.pipe(htmlmin({preserveLineBreaks: true, collapseWhitespace: true}))
.pipe(gulp.dest('../html_dist'));
});


npm install --save-dev gulp gulp-plumber gulp-inline-css gulp-htmlmin

gulp dest

これで html_dist ディレクトリに納品できるHTMLファイルが書き出されます。


5. 文字コードを iso-2022-jp に変換する

Gmail のブラウザ版は UTF-8 でしか送信できなくなったと聞きますし、 Outlook 2003 以降であれば UTF-8 形式の送受信に対応していますから、この項目が必須でなくなるのも遠い未来ではないのかもしれません。

多少明るい未来はすぐそこです。

さて、 UTF-8 以外で作業するのはいまどきコーディングにもプレビューにも骨が折れますから、納品物だけ iso-2022-jp にエンコードすれば十分です。

今回は gulp-iconv を選んだのですが、これがうまくいきません。類似のライブラリも同様でした。

変換を行っている iconv-lite 側のサポートに起因するようで、Issuesを覗いてみますと、かいつまんで言えば「レアな形式だし、他の形式とかなり違うし」とのこと。

はい。おっしゃる通りです。だがここはガラパゴスなのだ。

そんなわけで iso-2022-jp を受け入れてくれる jconv を使うよう書き換えてお茶を濁します。

HTMLの charset もここで一緒に置換しておきます。

npm install --save-dev gulp-replace gulp-iconv

cd node_modules/gulp-iconv
npm install --save-dev jconv

続いて gulp-iconv をちょっと編集します。軽微な変更なのでもう直接失礼します。


_dev/node_modules/gulp-iconv/lib/gulp-iconv.js

// この行の iconv-lite を

// _ref = require('iconv-lite'), decode = _ref.decode, encode = _ref.encode, decodeStream = _ref.decodeStream, encodeStream = _ref.encodeStream;

// jconv に書き換えるだけ
_ref = require('jconv'), decode = _ref.decode, encode = _ref.encode, decodeStream = _ref.decodeStream, encodeStream = _ref.encodeStream;


最後に gulp.js に追記。


_dev/gulp.js

var replace = require('gulp-replace'),

iconv = require('gulp-iconv'),
jconv = require('jconv');

// 先ほどの 'dest' タスクを拡張
gulp.task('dest', function(){
gulp.src(['../*.html'])
.pipe(plumber())
.pipe(inlineCSS({applyStyleTags: false, removeStyleTags: false}))
.pipe(htmlmin({preserveLineBreaks: true, collapseWhitespace: true}))
.pipe(replace('utf-8', 'iso-2022-jp'))
.pipe(iconv({
decoding: 'utf8',
encoding: 'iso2022jp'
}))
.pipe(gulp.dest('../html_dist'));
});


gulp dest

これで文字コードも charset も iso-2022-jp に変換された納品用ファイルが出来上がります。

やりました。


おわりに

HTML/CSSを編集したら php _convert_to_html.phpgulp dest だけ。手順がだいぶ簡略化されてミスりにくくましたね。

私はここで満足しましたが、改善の余地は多々あると思います。

あとホームページビルダーは偉大だったなと思いました。