Отправка писем в формате HTML Win / Mac + FileMaker Server + PHP. Часть 1

Автор Андрей Волков.

Отправка электронной почты в формате HTML определенно необходимая и полезная возможность. Особенно для CRM систем, завязанных на работу с клиентами. Это и презентации, и коммерческие предложения, и формы подтверждения заказов. Все то, что характеризует качество и стиль. Появления такой возможности я ждал от релиза к релизу. Почему любой простецкий сайт может отправлять HTML почту, а FileMaker – нет? Почему разработчики платформы не добавят такой простой и важной опции в команду Send Mail?

Ответ на эти вопросы очень прост: на самом деле файлмейкер (в варианте клиент + сервер) может отправлять HTML почту, и эта возможность существует у любых версий. Реализуется такая возможность очень просто и быстро.

Сама технология отправки почты реализована в языке PHP. Использование PHP технологий возможно потому, что файлмейкер имеет собственную службу Web Publishing via PHP, то есть развертывает собственный веб-сервер с PHP процессором.

Многие разработчики не рассматривают для себя вариант «веб-публикации с помощью PHP», поэтому в настройках ФМ-сервера флаг Enable PHP publishing обычно выключен. Но ведь PHP используется не только для формирования динамических веб-страниц (сайтов). Служебные страницы PHP могут выполнять много разных полезных функций, таких как XML POST, архивация файлов, размещение файлов на FTP узлах, применение hash-функций, наконец, то, что нас интересует: отправку почты в формате HTML.

Итак, в настройках ФМ-сервера необходимо выставить флаг Enable PHP publishing. При этом служебные страницы PHP, размещенные в папку (Windows):
C:\Program Files\FileMaker\FileMaker Server\Web Publishing\web-server-support\test\fmi-test\
cтанут доступными при обращении к ним через браузер:  http://hostipaddress/fmi-test/page.php

Для проверки такой возможности создайте текстовый файл phpinfo.php, содержимым которого является следующий текст:

 <?php phpinfo(); ?> 

Сохраните этот файл как:  C:\Program Files\FileMaker\FileMaker Server\Web Publishing\web-server-support\test\fmi-test\phpinfo.php
И наберите в браузере адрес: localhost/fmi-test/phpinfo.php или удаленно http:// filemakerhostaddress/fmi-test/phpinfo.php

Вы должны увидеть страницу описания PHP настроек, это значит — служба работает нормально.

Технология отправки HTML почты будет очень простой. На сервере необходимо разместить специальный php файл, который содержит сценарий отправки почты. В приложении файлмейкер для запуска сценария мы будем использовать команду Insert from URL (есть решение и для более ранних(9-11) версий фм , можно будет обсудить отдельно). В качестве урла необходимо передать адрес страницы, добавив ряд параметров (чтобы сценарий мог узнать, кому какое письмо отправлять). Так же, как для отправки обычного письма, нужно знать настройки SMTP-сервера (имя сервера, логин, пароль).

Разобраться с языком PHP файлмейкер-разработчику не составит труда. Синтаксис будет ему очень знаком. Посмотрим, как выглядит элементарный php скрипт для отправки почты

<?php 
require_once «Mail.php»; 

$from = «addressfrom@mail.com»; 
$to = «addressto@mail.com»; 
$body = «<H1> test </H1> Hi,\n\nHow are you?»; 

$headers = array ( 
‘From’ => $from, 
‘To’ => $to, 
‘Cc’ => ‘addresscc@mail.com’, 
‘Bcc’ => ‘addressbcc@mail.com’, 
‘Subject’ => ‘Hi everybody’, 
‘Content-type’ => ‘text/html; charset=iso-8859-1 \r\n’ 
);        

$smtp = Mail::factory(‘smtp’, 
array (‘host’ => ‘smtp.mail.com’, 
‘auth’ => true, 
‘username’ => ‘login@mail.com’, 
‘password’ => ‘secretword’)); 

$mail = $smtp->send($to, $headers, $body); 

?> 

php код — это все, что находится внутри тегов <?php …  ?>
Первая строчка require_once «Mail.php»; означает что для работы скрипта требуется дополнительный файл Mail.php

$from, $body, $headers, $smtp, $mail – это переменные
Array() – это массив. Его заполнение интуитивно понятно.
Mail::factory — функция factory из файла Mail создает новую сущность $smtp, обладающую методом send($to, $headers, $body);

собственно этот метод и выполняет отправку почты.
Комментарии в php привычны всем нам:

// — это однострочный комментарий 


/*
это
многострочный
комментарий
*/ 

обратите внимание, что в конце каждой команды в скрипте обязательно ставится точка с запятой;

Как мы видим, для работы нашего скрипта нам потребовался дополнительный файл Mail.php. Этот файл внутри себя содержит ссылки на другие файлы, которые в свою очередь тоже на что-то ссылаются. Таким образом низовой уровень работы сценария отправки письма для нас скрыт (он нам не особо и интересен). Для нас важно лишь, чтобы эти необходимые файлы физически присутствовали рядом с нашим собственным файлом.

Сделать это не так и сложно, поскольку все необходимые для нас файлы, входят в две стандартные библиотеки PEAR, которые называются соответственно Mail и Net_SMTP. Некоторые из библиотек PEAR изначально установлены вместе с php сервером. Их можно найти в директории C:\Program Files\FileMaker\FileMaker Server\Web Publishing\publishing-engine\php\ в папке PEAR. Но нужные нам Mail и Net_SMTP отсутствуют. Их придется установить самостоятельно.

Скачиваем библиотеки бесплатно с сайта REAR (http://pear.php.net/).
http://pear.php.net/package/Mail
http://pear.php.net/package/Net_SMTP/
Каждая библиотека представляет собой архив. Процедура установки имеет особенности для MacOS и для Windows; подробное описание можно найти в сети. Я подробнее освещу процесс установки в ОС Windows.

Итак,
Для установки необходимо скачать с сайта оба архива. Я скачивал Mail-1.2.0 и Net_SMTP-1.4.1
Архивы размещаем в папке C:\Program Files\FileMaker\FileMaker Server\Web Publishing\publishing-engine\php\PEAR\
В директории C:\Program Files\FileMaker\FileMaker Server\Web Publishing\publishing-engine\php находим файлик pear.bat
Запускаем cmd.exe
Перетаскиваем файл pear.bat в окно консоли и дописываем install + имя архива
Т.е. в консоли команда выглядит так:
«С:\Program Files\FileMaker\FileMaker Server\Web Publishing\publishing-engine\php\pear.bat» install Mail-1.2.0
Исполняем, смотрим результат.

Вторая команда будет
«С:\Program Files\FileMaker\FileMaker Server\Web Publishing\publishing-engine\php\pear.bat» install Net_SMTP-1.4.1
Исполняем, смотрим результат.

Если все прошло гладко, то можно проверить результат наших действий командой
«С:\Program Files\FileMaker\FileMaker Server\Web Publishing\publishing-engine\php\pear.bat» list
В результате выполнения команды увидим в окне консоли в списке установленных библиотек те, что мы только что установили.

Теперь можно попробовать работу нашего сценария. Создадим файл
C:\Program Files\FileMaker\FileMaker Server\Web Publishing\web-server-support\test\fmi-test\sendmail.php с содержимым, которое приведено выше. Подставим вместо тестовых имен реальные данные.
Теперь попробуем запустить из браузера http:// filemakerhostaddress/fmi-test/sendmail.php
И проверяем почтовый ящик. Если все работает, то запускаем php из файлмейкера с помощью команды Insert From URL

Далее мы научимся усложнять наш запрос, чтобы он содержал все необходимые параметры для отправки конкретного письма конкретному адресату. А PHP скрипт научим считывать эти параметры и возвращать нам результаты отправки письма (сообщение об ошибках, флаг «успешно» и т.п.)

Что происходит, когда мы в браузере пытаемся открыть некую php-страницу? Например, http://221.100.100.80/page.php

Фактически мы отправляем на сервер 221.100.100.80 некий ЗАПРОС с требованием исполнить сценарий, содержащийся в файле page.php. Сценарий исполняется на сервере, а в окне браузера мы видим лишь результат исполнения этого сценария – ответ от искомой страницы (разумеется, если ответ предусмотрен самим сценарием, иначе мы видим пустое окно).

Можем ли мы сделать так, чтобы сервер не просто исполнял жесткий сценарий, прописанный в файле, но чтобы он мог передать в сценарий некоторые дополнительные данные?
Да, конечно. Существует два стандартных способа передать параметры в веб-запросе. Это методы GET и POST.

Оба метода могут быть реализованы в файлмейкере для отправки почты. Метод POST технически более сложен. Метод GET более простой и по этой причине предпочтителен.

Реализуется он очень просто. Необходимо в адресную строку к основному url после знака вопроса добавить параметры запроса:
http://221.100.100.80/page.php?from=myadressfrom&to=myaddressto&subject=mysubject

один параметр от другого отделяется амперсандом.
PHP процессор способен считывать эти параметры. Например, вместо того, чтобы писать

$from = «addressfrom@mail.com»;

$headers = array (
‘From’ => $from,
‘To’ => ‘addressto@mail.com’,
‘Cc’ => ‘addresscc@mail.com’,
‘Bcc’ => ‘addressbcc@mail.com’,
‘Subject’ => ‘Hi everybody’,
‘Content-type’ => ‘text/html; charset=iso-8859-1 \r\n’
);

Мы могли бы изменить сценарий следующим образом:

if(isset($_GET[‘to’])) {
$to = $_GET[‘to’];

}
if(isset($_GET[‘from’])) {
$from = $_GET[‘from’];
}

if(isset($_GET[‘cc’])) {
$cc = $_GET[‘cc’];
}
if(isset($_GET[‘bcc’])) {
$bcc = $_GET[‘bcc’];
}

if(isset($_GET[‘subject’])) {
$subject = $_GET[‘subject’];
}

$headers = array (
‘From’ => $from,
‘To’ => $to,
‘Cc’ => $cc,
‘Bcc’ => $bcc,
‘Subject’ => $subject,
‘Content-type’ => ‘text/html; charset=iso-8859-1 \r\n’
);

Где конструкция

if(isset($_GET[‘from’])) {
$from = $_GET[‘from’];
}

Означает буквально следующее: Если передан get-методом параметр from, то создается переменная $from, которой присваивается значение соответствующего параметра.
(нельзя написать просто $from = $_GET[‘from’]; , так как может возникнуть ошибка исполнения, если такой параметр не передан в урле)[/code]

Итак, мы как-будто нашли способ передать нужные параметры в php файл. Но возникает два важных ограничения.

Первое: метод GET передает адрес в сети со всеми параметрами в явном виде. Передавать в качестве параметров настройки SMTP сервера небезопасно. Это ограничение можно обходить следующим образом. В урл настройки почтового сервера НЕ передавать! Хранить настройки SMTP в самом файле php, а если используется несколько акаунтов (хотя это редкость), то для каждого создавать и использовать отдельный php файл.

<?php
require_once «Mail.php»;

// ——— DEFAULT SETTINGS ———- //
$cc =»»;
$bcc =»»;
$from = «addressfrom@mail.com»;
$to = «addressto@mail.com»;
$subject = «No subject»;
$body = «This is sample body»;
$html_ref=»»;

// ——— DEFAULT SETTINGS ———- //

// ——— SMTP SERVER SETTINGS ———- //
$host = «smtp.mail.com»;
$username = «userlogin@mail.com»;
$password = «secret»;
// ——— SMTP SERVER SETTINGS ———- //

if(isset($_GET[‘to’])) {
$to = $_GET[‘to’];
}

if(isset($_GET[‘body’])) {
$body = $_GET[‘body’];
}

if(isset($_GET[‘from’])) {
$from = $_GET[‘from’];
}

if(isset($_GET[‘cc’])) {
$cc = $_GET[‘cc’];
}

if(isset($_GET[‘bcc’])) {
$bcc = $_GET[‘bcc’];
}

if(isset($_GET[‘subject’])) {
$subject = $_GET[‘subject’];
}

$headers = array (
‘From’ => $from,
‘To’ => $to,
‘Cc’ => $cc,
‘Bcc’ => $bcc,
‘Subject’ => $subject,
‘Content-type’ => ‘text/html; charset=iso-8859-1 \r\n’
);

$smtp = Mail::factory(‘smtp’,
array (‘host’ => $host,
‘auth’ => true,
‘username’ => $username,
‘password’ => $password));
$mail = $smtp->send($to, $headers, $body);

?>

Второе ограничение метода GET  — это ограничение на количество символов в адресной строке.
Понятно, что to, from, cc, bcc, subject без труда можно передать в строке URL, хотя и придется обязательно использовать функцию GetAsURLEncoded().

Но как быть с body?

Во-первых, тело письма может достигать весьма значительных размеров. Во-вторых, тело содержит html теги, которые никоим образом невозможно привести к валидному URL. Как быть?

Нам снова приходит на помощь PHP, а именно — встроенная функция file_get_contents()

file_get_contents() принимает в качестве параметра путь к файлу, а возвращает в текстовом виде его содержимое. Причем путь к файлу может быть как локальным (относительный или полный путь к файлу на диске) так и сетевым.

Таким образом, если мы сформируем нужный нам HTML файл ( в боди мы ведь хотим отправлять HTML содержимое) и найдем способ «скормить» этот файл нашему php скрипту, то задачу следует признать решенной.

Сформировать HTML – не проблема. Во всех актуальных версиях файлмейкера Export Field Contents работает стабильно. А вот как сохранить этот файл, чтобы он был виден php процессору?

Здесь уже нам на помощь приходит сам файлмейкер сервер и его возможности по XML-публикации.

Кто ни разу не читал guide файлмейкера по custom web-publishing, потерял драгоценную возможность воспользоваться расширенными функциями файлмейкера.
Во-первых, потому что данный guide содержит крайне полезную информацию, как запускать скрипты на сервере (аналог Perform Script On Server в 13 версии).
Во-вторых, там же — крайне полезная инфа о том, как формируется веб-ссылка на файлы, сохраненные в серверной БД.

Первому вопросу можно будет посвятить отдельную статью, а второй – разберем прямо сейчас.

Итак, файлмейкер сервер реализует возможность доступа к данным через браузерный запрос. Ответом на такой запрос обычно является XML файл. Также можно по особой форме запросить доступ к хранящемуся в поле-контейнере файлу. Результат выдается в зависимости от вида файла либо как:
— картинка (если загружена картинка)
— PDF файл (в хроме он открывается как документ, с возможностью просмотра)
— как текст (если это txt или xml)
— как HTML страница, если сохранен HTML (здесь я могу ошибаться, поправьте)
— в остальных случаях как файл (сразу начинается закачка).

Формат запроса к фм-серверу следующий:

http://fmserverhostipaddress/fmi/xml/cnt/data.cont?-db=databasename&-lay=layoutname&-field=contfieldname(1)&-recid=32

в котором
fmserverhostipaddress – соответственно адрес хоста файлмейкер-сервера
databasename – имя базы данных (файла)
layoutname — название лэйаута на котором размещено поле-контейнер с файлом
contfieldname(1) – название поля-контейнера. Единица в скобках указывает на номер репетиции.
recid=32 – следует подставить вместо 32 соответствующий идентификатор записи (считывается с помощью функции Get(RecordID))

Чтобы запрос такого типа работал, необходимо:

В файле базы данных в настройках создать акаунт с привилегиями работы через XML, настроить для него доступ к таблице с файлами и лэйауту с файлами. Если это единственный акаунт, который работает с XML, то при XML запросах он будет использоваться по умолчанию. В противном случае в запросе следует явно указать логин-пароль данного акаунта.

Необходим специальный лэйаут для таблицы, в которой хранятся отправляемые файлы HTML (обычно это таблица с письмами). Лэйаут нельзя переименовывать.

Необходимо разместить на этом лэйауте поле-контейнер.

Убедитесь, что у вас все это создано. Добавьте запись, вставьте в контейнер картинку, получите ID записи и сформируйте запрос в формате
http://fmserverhostipaddress/fmi/xml/cnt/data.cont?-db=databasename&-lay=layoutname&-field=contfieldname(1)&-recid=yourID

Теперь откройте свою ссылку в браузере и убедитесь, что картинка из файла отображается в браузере.

Итак, мы добились возможности сохранить файл с телом письма, чтобы он был виден php процессору. Мы ипользовали при этом собственные средства файлмейкера. Осталось лишь передать php скрипту ссылку на файл, чтобы он мог его прочитать. Но здесь мы неожиданно сталкиваемся с препятствием: мы не можем в одном урле в качестве параметра передать другой урл. Но у нас есть варианты решения:

1) Сохранить урл практически целиком в php файле, а в запросе передавать лишь параметр recid (только ид записи).

2) Разбить урл на части и передавать в запросе обычным способом его переменные части, такие как fmserverhostipaddress, databasename, layoutname, contfieldname и recid

Второй вариант более универсальный, но у меня в готовом виде имеется сейчас первый вариант, который я и привожу ниже:

<?php
require_once «Mail.php»;

// ——— DEFAULT SETTINGS ———- //
$cc =»»;
$bcc =»»;
$from = «addressfrom@mail.com»;
$to = «addressto@mail.com»;
$subject = «No subject»;
$body = «»;
$html_ref=»»;

// ——— DEFAULT SETTINGS ———- //

// ——— SMTP SERVER SETTINGS ———- //
$host = «smtp.mail.com»;
$username = «userlogin@mail.com»;
$password = «secret»;
// ——— SMTP SERVER SETTINGS ———- //

if(isset($_GET[‘to’])) {
$to = $_GET[‘to’];
}

if(isset($_GET[‘from’])) {
$from = $_GET[‘from’];
}

if(isset($_GET[‘cc’])) {
$cc = $_GET[‘cc’];
}

if(isset($_GET[‘bcc’])) {
$bcc = $_GET[‘bcc’];
}

if(isset($_GET[‘subject’])) {
$subject = $_GET[‘subject’];
}

if(isset($_GET[‘html_ref’])) {
$html_ref = «http://210.100.100.80/fmi/xml/cnt/data.cont?-db=contacts&-lay=xml_letter&-field=HTML_FILE(1)&-recid=» . $_GET[‘html_ref’];
$body = file_get_contents($html_ref);
$body = iconv ( «ucs-2» , «utf-8» , $body );
}

$headers = array (
‘From’ => $from,
‘To’ => $to,
‘Cc’ => $cc,
‘Bcc’ => $bcc,
‘Subject’ => $subject,
‘Content-type’ => ‘text/html; charset=iso-8859-1 \r\n’
);

$smtp = Mail::factory(‘smtp’,
array (‘host’ => $host,
‘auth’ => true,
‘username’ => $username,
‘password’ => $password));
$mail = $smtp->send($to, $headers, $body);

?>

Вы заметили, что добавился всего лишь один кусочек кода:

if(isset($_GET[‘html_ref’])) {
$html_ref = «http://210.100.100.80/fmi/xml/cnt/data.cont?-db=contacts&-lay=xml_letter&-field=HTML_FILE(1)&-recid=» . $_GET[‘html_ref’];
$body = file_get_contents($html_ref);
$body = iconv ( «ucs-2» , «utf-8» , $body );
}

смысл которого в следующем. Если передан параметр html_ref, то этот параметр добавляется к текстовой строке и таким образом получается валидная ссылка на файл. Далее в переменную $body считывается содержимое файла. В последней строке текст из стандартной кодировки, в которую выгружает данные файлмейкер, преобразуется в тот же текст, но с правильной кодировкой utf-8

Думаю, уместно прямо сейчас описать процедуру отправки. Давайте представим себе, что мы всю почту храним в специальной таблице. Перед отправкой письма мы создаем запись в таблице и заполняем все поля: кому, от кого, тема, текст, копия…

Далее пользователь нажимает кнопку Отправить. Если это обычное письмо, то просто выполняется команда Send Mail, которая отправляет обычное письмо. Далее при необходимости мы считываем код ошибки, делаем отметку Отправлено и т.д…

Что физически происходит, если пользователь отправляет письмо HTML. Вот он нажал кнопку.
1) вычисляется (если это не было сделано ранее) HTML содержимое для body.
2) содержимое этого поля выгружается в файл file.html на жестком диске
3) файл вставляется заново в таблицу в поле-контейнер FILE_HTML
4) для переменной $url вычисляется URL запроса к PHP странице
5) Insert From URL[поле Result, $url]
6) считывается ответ от пхп-страницы, помещенный в поле Result. Далее в обычном порядке в зависимости от ответа.

Это занимает пару секунд, а в ФМ-14 еще меньше.
вычислить урл тоже не сложно:

 «http://210.100.100.80/fmi-test/get_sendmail.php?» &
«to=» & Trim($to) &
If(Length($cc);        «&cc=»         & GetAsURLEncoded($cc)) &
If(Length($bcc);       «&bcc=»        & GetAsURLEncoded($bcc)) &
If(Length($from);      «&from=»       & GetAsURLEncoded($from)) &
If(Length($reply_to);  «&reply_to=»   & GetAsURLEncoded($reply_to)) &
If(Length($subj);      «&subj=»       & GetAsURLEncoded($subj)) &
«&html_ref=»                          & Get(RecordID)

весь скрипт занимает 20 строчек. И он универсальный, его можно тиражировать из проекта в проект.

MAIL: PHP SEND Mail

Set Field [ Letter::body_HTML_stored ; Letter::html_template_calc ]
Commit Records/Requests [ No dialog ]

Set Variable [ $path; Value:»file:» & Get(TemporaryPath) & Letter::letter_id & «.html» ]
Export Field Contents [ Letter::html_template_calc ; “$path” ]
Insert File [ Letter::HTML_FILE ; “$path” ] [ Storage method: Insert ] [ Compression: Never compress ]
Commit Records/Requests [ No dialog ]

Set Variable [ $to; Value:Letter::to ]
Set Variable [ $cc; Value:Letter::cc ]
Set Variable [ $bcc; Value:Letter::bcc ]
Set Variable [ $from; Value:Letter::from ]

# link to html data (body)
Set Variable [ $html_body_url; Value:Get(RecordID) ]
Set Variable [ $subj; Value:Letter::subject ]

# send mail php
Set Variable [ $php_url; Value:»http://210.100.100.80/fmi-test/get_sendmail.php?» & «to=» & Trim($to) & If(Length($cc);        «&cc=»         & GetAsURLEncoded($cc)) & If(Length($bcc);       «&bcc=»        & GetAsURLEncoded($bcc)) & If(Length($from);      «&from=»       & GetAsURLEncoded($from)) & If(Length($reply_to);  «&reply_to=»   & GetAsURLEncoded($reply_to)) &

Insert from URL [ Letter::result ; $php_url ] [ Select; No dialog ]
Commit Records/Requests [ No dialog ]

# check response
If [ GetValue(Letter::result; 1) = 1 ]
Show Custom Dialog [ Title:»Success»; Message: «Letter delivered to » &¶ &  Letter::to; Default Button:“OK”,    Commit:“Yes” ]
Set Field [ Letter::sent_date ; Get(CurrentDate) ]
Commit Records/Requests [ No dialog ]

Else
Show Custom Dialog [ Title:»ERROR»; Message: Letter::result; Default Button:“OK”, Commit:“Yes”]
End If

Exit Script [  ]

php… Удобный бесплатный вариант, один раз настроить и всю жизнь пользоваться. Нажал кнопку — увидел результат. Кстати, насчет отправки нескольких файлов в аттачменте. И это возможно тоже. По той же самой технологии. Тот самый php скрипт, который здесь я привел, потребуется слегка допилить просто.

Что касается текстовых параметров, то отличие для приема пост-запросов будет минимальное:

if(isset($_POST[‘from’])) { 
   $from = $_POST[‘from’]; 
   } 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

41 + = 51