Відправка електронної пошти у форматі HTML

Введення
Майже в кожному проекті доводиться думати про відправлення листів по електронній пошті. Основними вимогами при цьому є, крім надійності доставки, привабливість і зручність електронних листів.
Основні нюанси при формуванні таких листів:
  • Всі стилі повинні вбудовуватися (inline) у вигляді атрибута
    style
    для певного HTML-елемента.
  • Всі зображення повинні вбудовуватися, або як окремі вкладення в листі, або у вигляді base64-кодованих даних (друге банально зручніше).
  • Лист має підтримувати DKIM (настройка мэйлера), а домен відправника — утримувати SPF-запис.
Раніше я використовував для формування HTML-листів проект Premailer, створений на Ruby. Довелося навіть зайнятися підтримкою проекту (зараз часу на це немає, мэйнтейнеры вітаються).
Зараз же хотілося уникнути впровадження Ruby, в той час, як Node проник скрізь.
Juice
На щастя, сучасна екосистема Node надає багаті можливості по формуванню електронних листів. Ми вибрали ланцюжок з формування електронної пошти у вигляді pug-шаблонів, перетворенню їх з допомогою juice і підстановки конкретних даних на бэкэнде (у нас це Perl).
Передбачається, що Ви використовуєте
node 6+
,
babel
(es2015, es2016, es2017, stage-0 presets).
Установка
npm install gulp-cli -g
npm install gulp --save-dev
npm install del --save-dev
npm install gulp-rename --save-dev
npm install gulp-pug --save-dev
npm install premailer-gulp-juice --save-dev
npm install gulp-postcss --save-dev
npm install autoprefixer --save-dev
npm install gulp-less --save-dev

gulpfile.babel.js:
'use strict';

import gulp from 'gulp';
import mail from './builder/tasks/mail';
gulp.task('mail', mail);

builder/tasks/mail.js:
'use strict';

import gulp from 'gulp';
import stylesheets from './mail/stylesheets';
import templates from './mail/templates';
import clean from './mail/clean';

const mail = gulp.series(clean, stylesheets, templates);

export default mail;

builder/tasks/mail/stylesheets.js
'use strict';

import gulp from 'gulp';
import from config 'config';
import rename from 'gulp-rename';
import postcss from 'gulp-postcss';
import autoprefixer from 'autoprefixer';
import less from 'gulp-less';

const stylesheetsPath = config.get('srcPath') + '/mail/stylesheets';

const stylesheetsGlob = stylesheetsPath + '/**/*.less';

const mailStylesheets = () => {
return gulp.src(stylesheetsGlob)
.pipe(less())
.pipe(postcss([
autoprefixer({browsers: ['last 2 versions']}),
]))
.pipe(gulp.dest(stylesheetsPath));
};

export default mailStylesheets;

builder/tasks/mail/templates.js:
'use strict';

import gulp from 'gulp';
import from config 'config';
import pug from 'gulp-pug';
import rename from 'gulp-rename';
import juice from 'premailer-gulp-juice';

const templatesPath = config.get('srcPath') + '/mail';
const mailPath = config.get('mailPath');

const templatesGlob = templatesPath + '/**/*.pug';

const mailTemplates = () => {
return gulp.src(templatesGlob)
.pipe(rename(path => {
path.extname = '.html';
}))
.pipe(pug({
client: false
}))
.pipe(juice({
webResources: {
relativeTo: templatesPath,
images: 100,
strict: true
}
}))
.pipe(gulp.dest(mailPath));
};

export default mailTemplates;

builder/tasks/mail/clean.js:
'use strict';

import del from 'del';
import gutil from 'gulp-util';

const clean = done => {
return del([
'mail/*.html',
'src/mail/stylesheets/*.css'
]).then(() => {
gutil.log(gutil.colors.green('Delete src/mail/stylesheets/*.css and mail/*.html'));
done();
});
};

export default clean;

Типовий шаблон виглядає так (generic.pug):
include base.pug

+base
tr(height='74')
td.b-mail__table-row--heading(align='left', valign='top') Привіт,
tr
td(align='left', valign='top')
| <%== $html %>

Де base.pug:
mixin base(icon, alreadyEncoded)
doctype html
head
meta(charset="utf8")
link(rel="stylesheet", href="/stylesheets/mail.css")
body
table(width='100%', border='0', cellspacing='0', cellpadding='0')
tbody
tr
td.b-mail(align='center', valign='top', bgcolor='#ffffff')
br
br
table(width='750', border='0', cellspacing='0', cellpadding='0')
tbody.b-mail__table
tr.b-mail__table-row(height='89')
tr.b-mail__table-row
td(align='left', valign='top', width='70')
img(src='/images/logo.jpg')
td(align='left', valign='top')
table(width='480', border='0', cellspacing='0', cellpadding='0')
tbody
if block
block
td(align='right', valign='top')
if alreadyEncoded
img.fixed(src!=icon, data-inline-ignore)
else if icon
img.fixed(src!=icon)
br
br
tr
td(align='center', valign='top')

Власне, болванка готова, шаблони компілюються. Формування модуля config тривіально і необов'язково.
Готова болванка репозиторію тут: https://github.com/premailer/gulp-juice-demo
gulp mail

ViewAction
Багато поштові клієнти, такі, як GMail/Inbox, підтримують спеціальні дії в режимі перегляду повідомлень. Впровадити їх простіше простого, додавши вміст повідомлення наступні теги:
div(itemscope, itemtype="http://schema.org/EmailMessage")
div(itemprop="action", itemscope, itemtype="http://schema.org/ViewAction")
link(itemprop="url", href="https://github.com/imlucas/gulp-juice/pull/9")
meta(itemprop="name", content="View Pull Request")
meta(itemprop="description", content="View this Pull on Request GitHub")

Ну і трохи інтеграції з (виберіть свою мову, тут потрібен був Perl)
sub prepare_mail_params {
my %params = %{ shift() };

my @keys = keys %params;
# Camelize params
for my $param ( @keys ) {
my $new_param = $param;
$new_param =~ s/^(\w)/\U$1\E/;
next if $new_param eq $param;
$params{$new_param} = delete $params{$param};
}

%params = (
Type => 'multipart/mixed; charset=UTF-8',
From => 'support@ourcompany.co.uk',
Subject => ",
%params,
);

# Mime params
for my $param ( keys %params ) {
$params{$param} = encode( 'MIME-Header', $params{$param} );
}

return \%params;
}

sub _template_processor {
state $instance = Mojo::Template->new(
vars => 1,
auto_escape => 1,
);
return $instance;
}

sub send_mail {
my %params = %{ shift() };

my $html = (delete $params{message}) // ";

my $template = delete $params{template};
my $stash = (delete $params{stash}) // {};
unless ( $template ) {
$template = 'generic';
$stash->{html} = $html;
}

$html = _template_processor()->render_file(
Config->directories->{mail}. "/$template.html",
$stash,
);

$html = encode_utf8( $html );

my $msg = MIME::Lite->new(
%{ prepare_mail_params( \%params )}
);

$msg->attach(
Type => 'HTML',
Data => $html,
);

if ( $mail_settings->{method} eq 'sendmail' ) {
return $msg->send();
}

if ( $mail_settings->{method} eq 'smtp' ) {
return $msg->send('smtp', $mail_settings->{host}, Timeout => $mail_settings->{timeout});
}

croak "Unknown Config mail.method: ". $mail_settings->{method};
}

Корисні посилання
P. S.: Спасибі pstn за доопрацювання шаблонів листів.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.