[EmailQueue] - Очередь писем
С сайта бывает требуется отсылать много писем. Но многие хостеры ограничивают число писем что можно сразу отправить. Например на одном хостинге можно отправить только 60 писем в минуту. Чтобы обойти это ограничение нужно организовывать очередь писем и отправлять письма частями по, например, 50 штук. Чтобы не писать такую очередь каждый раз когда отправка многих писем нужна в компоненте, написал отдельный компонент что организует такую очередь.
В админке сделано относительно удобное управление очередью.
Для отправки писем из очереди поставите в cron файл core/components/emailqueue/cron/send.php с нужной периодичностью.
Настройки:
emailqueue_limit — сколько писем отправлять за раз.
emailqueue_store_days — сколько дней письма хранить в очереди.
Для помещения писемь в очередь используйте примерно такой код:
Думаю небольшие компоненты для разработчиков лучше делать бесплатными. Так что компонетнт по крайней мере пока бесплатный :). Но от доната не откажусь. Кто захочет поблагодарить Номер кошелька Яндекс 410011280920822
Гитхаб https://github.com/touol/EmailQueue
UPD 10.12.2018
В версии 1.1.0-beta
Сделан более удобный topBar.
Добавлен статус «Отправляется» для для избежания отправки дубликатов писем.
Добавлены хуки EmailQueue и EmailQueueAutoResponder для замены хуков email и FormItAutoResponder в FormIt/AjaxForm.
EmailQueue — Cниппет для drop-in replacement замены хука email для FormIt/AjaxForm
Просто в вызове поменять хук на EmailQueue
EmailQueueAutoResponder — Cниппет для drop-in replacement замены хука FormItAutoResponder для FormIt/AjaxForm
Просто в вызове поменять хук на EmailQueueAutoResponder
И не все параметры FormIt поддерживает (например emailHtml, emailToName, emailCC, итд), т.к. компонент их не учитывает.
Если хотите помочь развитию компонента делайте мне пулл-реквест на гитхабе. Или просто публикуйте изменения кода в комментариях :).
В админке сделано относительно удобное управление очередью.
Для отправки писем из очереди поставите в cron файл core/components/emailqueue/cron/send.php с нужной периодичностью.
Настройки:
emailqueue_limit — сколько писем отправлять за раз.
emailqueue_store_days — сколько дней письма хранить в очереди.
Для помещения писемь в очередь используйте примерно такой код:
if (!$EmailQueue = $modx->getService('emailqueue', 'EmailQueue', $modx->getOption('emailqueue_core_path', null,
$modx->getOption('core_path') . 'components/emailqueue/') . 'model/emailqueue/', array())) {
return;
}
$queue_email = $modx->newObject('EmailQueueItem');
$data1 = array(
'sender_package'=>'UserTest', //дополнение отправитель письма
'to'=>$invite->user_email, //емаил
'subject'=>$modx->lexicon('usertest_invite_subject',array('test_name' => $test->name)), // тема письма
'body'=>$modx->getChunk('tpl.UserTest.InviteEmail',array('test_name' => $test->name,'link'=>$invite->url)), // тело письма
'date'=>date("Y-m-d H:i:s"), // дата в очереди. Нужно чтоб письма удалялись по истечении срока хранения.
);
if($modx->getOption('usertest_invite_email_from', null, false))
$data1['from'] = $modx->getOption('usertest_invite_email_from'); //необязательно
if($modx->getOption('usertest_invite_email_from_name', null, false))
$data1['from_name'] = $modx->getOption('usertest_invite_email_from_name'); //необязательно
$queue_email->fromArray($data1);
if($queue_email->save())
$modx->log(modX::LOG_LEVEL_INFO,"Приглашение на $email добавлено в очередь писем!");
Дополнительно можно задать:$queue_email->reply_to // обратный адрес
$queue_email->attachments //полные имена приклепляемых файлов через запятую
Компонент доступен в Modstore https://modstore.pro/packages/alerts-mailing/emailqueue.Думаю небольшие компоненты для разработчиков лучше делать бесплатными. Так что компонетнт по крайней мере пока бесплатный :). Но от доната не откажусь. Кто захочет поблагодарить Номер кошелька Яндекс 410011280920822
Гитхаб https://github.com/touol/EmailQueue
UPD 10.12.2018
В версии 1.1.0-beta
Сделан более удобный topBar.
Добавлен статус «Отправляется» для для избежания отправки дубликатов писем.
Добавлены хуки EmailQueue и EmailQueueAutoResponder для замены хуков email и FormItAutoResponder в FormIt/AjaxForm.
EmailQueue — Cниппет для drop-in replacement замены хука email для FormIt/AjaxForm
Просто в вызове поменять хук на EmailQueue
EmailQueueAutoResponder — Cниппет для drop-in replacement замены хука FormItAutoResponder для FormIt/AjaxForm
Просто в вызове поменять хук на EmailQueueAutoResponder
[[!Formit?
&hooks=`EmailQueue,EmailQueueAutoResponder`
&emailTpl=`feedbackEmail`
&emailTo=`test@mail.ru`
&emailSubject=`Сообщение с сайта`
&replyTo=`[[+email]]`
&emailFrom=`[[++emailsender]]`
&fiarTpl=`feedbackEmail`
&fiarSubject=`Сообщение с сайта`
&fiarReplyTo=`[[+email]]`
&fiarFrom=`[[++emailsender]]`
]]
Поддержку вложений не делал, т.к. не было необходимости.И не все параметры FormIt поддерживает (например emailHtml, emailToName, emailCC, итд), т.к. компонент их не учитывает.
Если хотите помочь развитию компонента делайте мне пулл-реквест на гитхабе. Или просто публикуйте изменения кода в комментариях :).
Поблагодарить автора
Отправить деньги
Комментарии: 69
Скоро придется отправить 4000 писем пользователям сайта, который переношу с WP, и как раз об этом думал, как же решить это. Большое спасибо за компонентище!
Надеюсь скоро опубликуют. Рад что вам пригодиться. Забыл только очистку всей очереди добавить :(, но попозже добавлю. Если вам быстрее компонент надо будет могу выслать по почте.
да не, не раньше, чем через 2 недели нужно будет)
Очень здорово! очень нужно, спасибо автор!
Тоже со временем воспользуюсь, возьму на заметку.
Тоже со временем воспользуюсь, возьму на заметку.
Спасибо большое!
Использовал для этих целей очереди Sendex, добавлял туда письма и дёргал очередь через крон. Ваше дополнение удобнее и видна статистика отправленных писем.
Использовал для этих целей очереди Sendex, добавлял туда письма и дёргал очередь через крон. Ваше дополнение удобнее и видна статистика отправленных писем.
Еще дополнение на модерации и его еще в глаза никто не видел, а уже 3 спасибо :). Всем пожалуйста. Сейчас думаю на всякий случай в гитхаб выложу. Вдруг кому-то уже надо.
Выложил на гитхаб
Александр, обнови пост. Компонент доступен в Модстор — modstore.pro/packages/alerts-mailing/emailqueue
Спасибо. Как-то из головы вылетело.
Это такая дата по умолчанию была. Так 0000-00-00 00:00:00 mysql записывает. Модераторы модсторе тоже сказали это поменять. В новой версии по умолчанию дата отправки null и в поле пусто. Сейчас обновил компонент на демо, чтоб вас это не смущало.
Да, спасибо, пустота будет лучше нулей и непонятных дат.
Я обычно даты в свои таблицы записываю в unix-формате и никаких проблем с этим. Пример беру с дат в таблице ресурсов, там все они в unix-е.
Я обычно даты в свои таблицы записываю в unix-формате и никаких проблем с этим. Пример беру с дат в таблице ресурсов, там все они в unix-е.
Да, и ещё не сразу понял назначение двух одинаковых кнопок с самолётиками в панели с кнопками над таблицей, запутало как-то.
Хм… плохо у меня с дизайном :(. Воображения на картинки не хватает. Если есть идеи какие значки повешать на эти действия, напишите плиз :). Если писать словами, то в 1 слово не обойдешься и ширины экрана в итоге не хватит.
Хорошая идея. Только выпадающие списки я такие еще не делал. посмотрю как там в этом компоненте сделано.
Развития компонента пока не планируется :). В смысле, а есть куда его развивать? Рассылки все равно лучше делать через специализированные сервисы. Хостеры, если блокируют письма, то ничего не сообщают и не понятно ушло письмо или нет. Этот компонент на случай если сервис еще не имеет смысла подключать.
Развития компонента пока не планируется :). В смысле, а есть куда его развивать? Рассылки все равно лучше делать через специализированные сервисы. Хостеры, если блокируют письма, то ничего не сообщают и не понятно ушло письмо или нет. Этот компонент на случай если сервис еще не имеет смысла подключать.
а есть куда его развивать?Есть!
Например первое что я поискал, это возможность подключения шаблонов.
Это можно например реализовать так: отдельное поле template в таблице, в который можно будет указывать id существующего шаблона или чанка. В форме можно это селектом сделать самоподгружающим эти эти элементы, но на первое время и простое поле тоже сойдёт, главное чтобы работало. В самом шаблоне чтобы были доступны переменными (желательно через Fenom если установлен) все поля письма. Ну и если шаблон указан, то отправляется письмо красиво оформленное, если нет, то простой текст.
Для многих это довольно важно, т.к. кастомизация, сохранения брендового стиля и т.д.
Ну тут идея компонента не совсем такая :). В очереди хранятся уже готовые письма. А письма в очередь добавляются кодом, в котором как раз письмо красиво и оформляется.
'body'=>$modx->getChunk('tpl.UserTest.InviteEmail',array('test_name' => $test->name,'link'=>$invite->url)),
Создание и редактирование писем в компоненте это просто на всякий случай. Чтоб протестировать компонент или просто отправку писем. Посмотреть что вообше в очереди лежит.
Просто дело в том, что при редактировании письма код не очень-то и подредактируешь, будет каша. Тогда уже лучше редактор кода привязать к полю редактирования в админке…
Спасибо за объяснение, забыл про getChunk (что компонент больше для разработчиков) и сосредоточился на удобстве формы из админки…
Спасибо за объяснение, забыл про getChunk (что компонент больше для разработчиков) и сосредоточился на удобстве формы из админки…
Например для кнопки «Восстановить письма с ошибками в очереди» больше подойдёт значок icon-refresh.
Кнопки Ошибки и Все непонятно что делают, просят больше описания действий. Ну и значков просят, а то высота кнопок прыгает.
Две кнопки удалить — тоже можно было бы их в выпадалку запрятать и текст написать.
Я просто не сразу заметил, что у кнопок есть подсказка при наведении, да и не всегда она у меня срабатывает, видно потому что навешана на иконку значка, а не на всю кнопку.
Кнопки Ошибки и Все непонятно что делают, просят больше описания действий. Ну и значков просят, а то высота кнопок прыгает.
Две кнопки удалить — тоже можно было бы их в выпадалку запрятать и текст написать.
Я просто не сразу заметил, что у кнопок есть подсказка при наведении, да и не всегда она у меня срабатывает, видно потому что навешана на иконку значка, а не на всю кнопку.
Кнопки Ошибки и Все — это не кнопки вообще. Это подписи что следующие кнопки к Ошибкам относяться и к всем письмам. Мда… замученный у меня дизайн получился. Буду выпадающим списком делать, но не пока занят через неделю думаю сделаю.
Ок, я тебе только идей подкинул для рихтовки, а так молодец — компонент очень классный и очень нужный! Буду его использовать на многих сайтах.
В новой версии еще и 2 значка удаления. 1 удаляет письма с ошибками. Другой очищает всю очередь.
Не, это будет путаница однозначно!
Лучше в выпадающий список запрятать некоторые кнопки.
Лучше в выпадающий список запрятать некоторые кнопки.
Добрый день! Подскажите пожалуйста, как лучше организовать помещение в очередь если используется сниппет (вызывается по cron)
На первый взгляд кажется, что проще переписать весь сниппет и сделать по вашему шаблону, но непонятно, что вписывать в графе
users()
->members('en')
->profile()
->where('modUser.active = 1 AND DAYOFMONTH(FROM_UNIXTIME(Profile.dob)) = DAYOFMONTH(now()) AND MONTH(FROM_UNIXTIME(Profile.dob)) = MONTH(now())')
->joinGroup('group2')
->each(function($user, $idx) {
if (is_email($user['email'])) {
email()->to($user['email'])->subject('Поздравляем с Днём рождения!')->tpl('chunkName', $user)->send();
}
});
который использует компонент modHelpers?На первый взгляд кажется, что проще переписать весь сниппет и сделать по вашему шаблону, но непонятно, что вписывать в графе
$data1 = array(
'sender_package'=>'UserTest', //дополнение отправитель письма
Примерно так думаю
if (!$EmailQueue = $modx->getService('emailqueue', 'EmailQueue', $modx->getOption('emailqueue_core_path', null,
$modx->getOption('core_path') . 'components/emailqueue/') . 'model/emailqueue/', array())) {
return;
}
users()
->members('en')
->profile()
->where('modUser.active = 1 AND DAYOFMONTH(FROM_UNIXTIME(Profile.dob)) = DAYOFMONTH(now()) AND MONTH(FROM_UNIXTIME(Profile.dob)) = MONTH(now())')
->joinGroup('group2')
->each(function($user, $idx) {
if (is_email($user['email'])) {
$data1 = array(
'sender_package'=>'UsersSend', //дополнение отправитель письма
'to'=>$user['email'], //емаил
'subject'=>'Поздравляем с Днём рождения!', // тема письма
'body'=>$modx->getChunk('chunkName', $user), // тело письма
'date'=>date("Y-m-d H:i:s"), // дата в очереди. Нужно чтоб письма удалялись по истечении срока хранения.
);
$queue_email->fromArray($data1);
if(!$queue_email->save())
$modx->log(1,"Письмо для ".$user['email']." не удалось добавить в очередь писем!");
}
});
sender_package Нужно только чтоб видеть откуда письмо в очереди. Если вдруг несколько компонентов сразу отправляют. Можно написать что угодно или даже оставить пустым.
ошибку заметил
if (!$EmailQueue = $modx->getService('emailqueue', 'EmailQueue', $modx->getOption('emailqueue_core_path', null,
$modx->getOption('core_path') . 'components/emailqueue/') . 'model/emailqueue/', array())) {
return;
}
users()
->members('en')
->profile()
->where('modUser.active = 1 AND DAYOFMONTH(FROM_UNIXTIME(Profile.dob)) = DAYOFMONTH(now()) AND MONTH(FROM_UNIXTIME(Profile.dob)) = MONTH(now())')
->joinGroup('group2')
->each(function($user, $idx) {
if (is_email($user['email'])) {
$queue_email->newObject('EmailQueueItem'); //забыл создать объект :(
$data1 = array(
'sender_package'=>'UsersSend', //дополнение отправитель письма
'to'=>$user['email'], //емаил
'subject'=>'Поздравляем с Днём рождения!', // тема письма
'body'=>$modx->getChunk('chunkName', $user), // тело письма
'date'=>date("Y-m-d H:i:s"), // дата в очереди. Нужно чтоб письма удалялись по истечении срока хранения.
);
$queue_email->fromArray($data1);
if(!$queue_email->save())
$modx->log(1,"Письмо для ".$user['email']." не удалось добавить в очередь писем!");
}
});
Спасибо, буду пробовать.
ps пока помню — есть небольшая ошибка в верхнем меню компонента.
тэг title (всплывающая подсказка) при наведении на кнопки не выводится в FF, а в chrome выводится только при наведении на иконку внутри кнопки, а не на саму кнопку. Может пригодится.
ps пока помню — есть небольшая ошибка в верхнем меню компонента.
тэг title (всплывающая подсказка) при наведении на кнопки не выводится в FF, а в chrome выводится только при наведении на иконку внутри кнопки, а не на саму кнопку. Может пригодится.
Ну это известная проблема. Позже переделаю на выпадающий список
Получила вот такое
Fatal error: Call to a member function newObject() on a non-objectЕсли без этой
$queue_email->newObject('EmailQueueItem');
строки тоFatal error: Call to a member function getChunk() on a non-object
Блин поторопился. Конечно
$queue_email=$modx->newObject('EmailQueueItem');
Fatal error: Call to a member function getChunk() on a non-objectУ вас в начале скрипта сам $modx подключен?
что то вроде такого
require_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/config.core.php';
require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
require_once MODX_CONNECTORS_PATH . 'index.php';
Нет. Сейчас буду разбираться.
Подключила
require_once
, изменила $queue_email=$modx->newObject('EmailQueueItem');
и всё равно выход то жеFatal error: Call to a member function newObject() on a non-object
Полностью код можно? И путь до файла
<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
require_once dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))) . '/config.core.php';
require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
require_once MODX_CONNECTORS_PATH . 'index.php';
if (!$EmailQueue = $modx->getService('emailqueue', 'EmailQueue', $modx->getOption('emailqueue_core_path', null,
$modx->getOption('core_path') . 'components/emailqueue/') . 'model/emailqueue/', array())) {
return;
}
users()
->members('ru')
->profile()
->select('email,dob')
->where('modUser.active = 1 AND DAYOFMONTH(FROM_UNIXTIME(Profile.dob)) = DAYOFMONTH(now()) AND MONTH(FROM_UNIXTIME(Profile.dob)) = MONTH(now())')
->each(function($user, $idx) {
if (is_email($user['email'])) {
$queue_email = $modx->newObject('EmailQueueItem');
$data1 = array(
'sender_package'=>'UsersSend', //дополнение отправитель письма
'to'=>$user['email'], //емаил
'from_name'=>'Тест',
'subject'=>'Поздравляем с Днём рождения!', // тема письма
'body'=>$modx->getChunk('email_ru', $user), // тело письма
'date'=>date("Y-m-d H:i:s"), // дата в очереди. Нужно чтоб письма удалялись по истечении срока хранения.
);
$queue_email->fromArray($data1);
if(!$queue_email->save())
$modx->log(1,"Письмо для ".$user['email']." не удалось добавить в очередь писем!");
}
});
Про путь до файла не поняла. Делаю стандартный сниппет в админке, запускаю по Cron Manager.
Не работал с Cron Manager. По идее в нем не надо $modx подключать. он уже должен быть подключен. Надо разбираться. Так ничего не понятно. Мой скайп touols. Можите доступ скинуть?
странно что на $modx->getService не ругается
странно что на $modx->getService не ругается
Подключать $modx не надо по моему тоже. Через 1-1.5 часа сделаю копию на Modhost и если там не заработает пришлю доступы.
Поставил Cron Manager. Запустил такой код. И все успешно.
<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
if (!$EmailQueue = $modx->getService('emailqueue', 'EmailQueue', $modx->getOption('emailqueue_core_path', null,
$modx->getOption('core_path') . 'components/emailqueue/') . 'model/emailqueue/', array())) {
return;
}
$user['email'] = 'a@b.ru';
$queue_email = $modx->newObject('EmailQueueItem');
$data1 = array(
'sender_package'=>'UsersSend', //дополнение отправитель письма
'to'=>$user['email'], //емаил
'from_name'=>'Тест',
'subject'=>'Поздравляем с Днём рождения!', // тема письма
'body'=>$modx->getChunk('email_ru', $user), // тело письма
'date'=>date("Y-m-d H:i:s"), // дата в очереди. Нужно чтоб письма удалялись по истечении срока хранения.
);
$queue_email->fromArray($data1);
if(!$queue_email->save())
$modx->log(1,"Письмо для ".$user['email']." не удалось добавить в очередь писем!");
Здравствуйте! После перехода с php5.6 на php7.2 этот код перестал работать. В очередь попадает письмо с пустым полем «Сообщение».
то есть
Не знаете с чем может быть связано?
то есть
'body'=>$modx->getChunk('email_ru', $user), // тело письма
ничего не передаёт.Не знаете с чем может быть связано?
Попробуйте указать текст
П.С. Лично мне кажется странным вот этот код
Да и вообще весь описанный код странный. Но это вопрос к автору кода.
'body'=>'Текст для проверки', // тело письма
П.С. Лично мне кажется странным вот этот код
$user['email'] = 'a@b.ru';
Зачем создавать массив?Да и вообще весь описанный код странный. Но это вопрос к автору кода.
'body'=>'Текст для проверки', // тело письма
поле тоже остается пустымИ ошибок никаких не выдает. Журнал тоже пуст.
Тогда ждите ответа от автора дополнения.
Здравствуйте! Вот чтоб именно пустое поле получилось вообще странно :(. Пока скачиваю Опенсервер новый. Попробую поставить PHP 7.2.00 и посмотреть.
Хм. Он 3 часа обещает скачиваться. Может все таки дадите доступ к вашему MODX?
Хм. Он 3 часа обещает скачиваться. Может все таки дадите доступ к вашему MODX?
Посмотрела в БД — не пустое, стоит 0
А так же стоит 0 в поле attachments, которое вроде и не указано нигде в вызове.
с доступом ну никак, могу сделать копию
А так же стоит 0 в поле attachments, которое вроде и не указано нигде в вызове.
с доступом ну никак, могу сделать копию
Сделайте плиз если не трудно. у меня на php 7.2 ничего тестового нет
Отправила
В файле схемы были не правильно заданы типы полей phptype. Вместо text поставил string и все заработало. На рабочем сайте обновите компонент и проверяйте как там работает.
В emailqueue.mysql.schema.xml вместо этого
В emailqueue.mysql.schema.xml вместо этого
<field key="body" dbtype="text" phptype="text" null="true" default=""/>
<field key="attachments" dbtype="text" phptype="text" null="true" default=""/>
сделал это<field key="body" dbtype="text" phptype="string" null="true" default=""/>
<field key="attachments" dbtype="text" phptype="string" null="true" default=""/>
Я эти поля как в modExtra сделано так и делал.
Похоже я понял в чем косяк. $modx внутри функции вызывается. А внутри функции его нет. Попробуйте перед if (is_email($user['email'])) { вставить global $modx;
->each(function($user, $idx) {
global $modx;
if (is_email($user['email'])) {
$queue_email = $modx->newObject('EmailQueueItem');
Да, так ругается уже на последнюю строку
Спасибо!
$queue_email->fromArray($data1);
Fatal error: Call to a member function fromArray() on a non-objectЕсли вызвать
global $modx;
ещё и в начале сниппета, тогда работает. Только глаз режет :-)Спасибо!
Или передайте в функцию $modx третьим параметром —
->each(function($user, $idx, $modx) {
if (is_email($user['email'])) {
$queue_email = $modx->newObject('EmailQueueItem');
Вы написали про «режет глаз». Я описал способ избавиться от этого. На будущее.
Пример сниппета для drop-in replacement замены хука email для FormIt/AjaxForm
Просто в вызове поменять хук на email_queue (или свое название сниппета)
И не все параметры FormIt поддерживает (например emailHtml, emailToName, emailCC, итд), т.к. компонент их не учитывает.
Просто в вызове поменять хук на email_queue (или свое название сниппета)
[[!Formit?
&hooks=`email_queue`
&emailTpl=`feedbackEmail`
&emailTo=`test@mail.ru`
&emailSubject=`Сообщение с сайта`
&replyTo=`[[+email]]`
&emailFrom=`[[++emailsender]]`
]]
Поддержку вложений не делал, т.к. не было необходимости.И не все параметры FormIt поддерживает (например emailHtml, emailToName, emailCC, итд), т.к. компонент их не учитывает.
<?php
$fields = $hook->getValues();
$tpl = $modx->getOption('emailTpl', $hook->formit->config, '');
/* get from name */
$emailFrom = $modx->getOption('emailFrom', $hook->formit->config, '');
if (empty($emailFrom)) {
$emailFrom = !empty($fields['email']) ? $fields['email'] : $modx->getOption('emailsender');
}
$emailFrom = $hook->_process($emailFrom, $fields);
$emailFromName = $modx->getOption('emailFromName', $hook->formit->config, $modx->getOption('site_name', null, $emailFrom));
$emailFromName = $hook->_process($emailFromName, $fields);
/* get subject */
$useEmailFieldForSubject = $modx->getOption('emailUseFieldForSubject', $hook->formit->config, true);
if (!empty($fields['subject']) && $useEmailFieldForSubject) {
$subject = $fields['subject'];
} else {
$subject = $modx->getOption('emailSubject', $hook->formit->config, '');
}
$subject = $hook->_process($subject, $fields);
/* check email to */
$emailTo = $modx->getOption('emailTo', $hook->formit->config, '');
$emailToName = $modx->getOption('emailToName', $hook->formit->config, $emailTo);
if (empty($emailTo)) {
$hook->errors['emailTo'] = $modx->lexicon('formit.email_no_recipient');
$modx->log(\modX::LOG_LEVEL_ERROR, '[FormIt] '.$modx->lexicon('formit.email_no_recipient'));
return false;
}
/* compile message */
$origFields = $fields;
if (empty($tpl)) {
$tpl = 'fiDefaultEmailTpl';
$f = [];
$multiSeparator = $modx->getOption('emailMultiSeparator', $hook->formit->config, "\n");
$multiWrapper = $modx->getOption('emailMultiWrapper', $hook->formit->config, "[[+value]]");
foreach ($fields as $k => $v) {
if ($k == 'nospam') {
continue;
}
if (is_array($v) && !empty($v['name']) && isset($v['error']) && $v['error'] == UPLOAD_ERR_OK) {
$v = $v['name'];
$f[$k] = '<strong>'.$k.'</strong>: '.$v.'<br />';
} elseif (is_array($v)) {
$vOpts = array();
foreach ($v as $vKey => $vValue) {
if (is_string($vKey) && !empty($vKey)) {
$vKey = $k.'.'.$vKey;
$f[$vKey] = '<strong>'.$vKey.'</strong>: '.$vValue.'<br />';
} else {
$vOpts[] = str_replace('[[+value]]', $vValue, $multiWrapper);
}
}
$newValue = implode($multiSeparator, $vOpts);
if (!empty($vOpts)) {
$f[$k] = '<strong>'.$k.'</strong>:'.$newValue.'<br />';
}
} else {
$f[$k] = '<strong>'.$k.'</strong>: '.$v.'<br />';
}
}
$fields['fields'] = implode("\n", $f);
} else {
/* handle file/checkboxes in email tpl */
$multiSeparator = $modx->getOption('emailMultiSeparator', $hook->formit->config, "\n");
if (empty($multiSeparator)) {
$multiSeparator = "\n";
}
if ($multiSeparator == '\n') {
$multiSeparator = "\n"; /* allow for inputted newlines */
}
$multiWrapper = $modx->getOption('emailMultiWrapper', $hook->formit->config, "[[+value]]");
if (empty($multiWrapper)) {
$multiWrapper = '[[+value]]';
}
foreach ($fields as $k => &$v) {
if (is_array($v) && !empty($v['name']) && isset($v['error']) && $v['error'] == UPLOAD_ERR_OK) {
$v = $v['name'];
} elseif (is_array($v)) {
$vOpts = array();
foreach ($v as $vKey => $vValue) {
if (is_string($vKey) && !empty($vKey)) {
$vKey = $k.'.'.$vKey;
$fields[$vKey] = $vValue;
unset($fields[$k]);
} else {
$vOpts[] = str_replace('[[+value]]', $vValue, $multiWrapper);
}
}
$v = implode($multiSeparator, $vOpts);
if (!empty($vOpts)) {
$fields[$k] = $v;
}
}
}
}
$message = $hook->formit->getChunk($tpl, $fields);
$message = $hook->_process($message, $this->config);
if (!$EmailQueue = $modx->getService('emailqueue', 'EmailQueue', $modx->getOption('emailqueue_core_path', null,
$modx->getOption('core_path') . 'components/emailqueue/') . 'model/emailqueue/', array())) {
$hook->addError('email', 'При отправке произошла ошибка');
return false;
}
$data = array(
'sender_package' => 'FormIt',
'from' => $emailFrom,
'from_name' => $emailFromName,
'subject' => $subject,
'body' => $message,
'date' => date("Y-m-d H:i:s"),
);
$emailReplyTo = $modx->getOption('emailReplyTo', $hook->formit->config, '');
if (!empty($emailReplyTo)) {
$date['replyto'] = $emailReplyTo;
}
$emailTo = array_map('trim', explode(',', $emailTo));
foreach ($emailTo as $to) {
$to = $hook->_process($to, $fields);
if (!empty($to)) {
$data['to'] = $to;
$queue_email = $modx->newObject('EmailQueueItem');
$queue_email->fromArray($data);
$queue_email->save();
}
}
return true;
Добрый день! Когда писал компонент вот на использование в хуке для FormIt не планировал, что это понадобиться. Идея была в том, что за раз нельзя бывает много писем отправить и чтобы это сделать понадобился компонент. А в FormIt одно письмо за раз отправляется. Для чего вам этот хук? Мне просто не очень понятно зачем он. Раз написали думаю нужен :), но вот зачем теряюсь в догадках :).
Наверное потому что при загруженном функционале каждая секунда на счету, а добавление письма в очередь в базу происходит почти мгновенно в отличие от ожидания отправки письма…
Ну чтобы письма обратной связи и прочие тоже отправлялись через очередь.
Вот сейчас отправлять нужно от домена заказчика, а чтобы соблюсти все DKIM, SPF и прочее без правки DNS вариант только через доступ по SMTP.
А коннект к SMTP визуально секунды 3 и это тормозит на фронте.
Плюс опять же возможны какие-то лимиты по кол-ву в час, одновременных соединений и тп.
И у пользователя возникает ошибка и форма не отправляется.
Вот сейчас отправлять нужно от домена заказчика, а чтобы соблюсти все DKIM, SPF и прочее без правки DNS вариант только через доступ по SMTP.
А коннект к SMTP визуально секунды 3 и это тормозит на фронте.
Плюс опять же возможны какие-то лимиты по кол-ву в час, одновременных соединений и тп.
И у пользователя возникает ошибка и форма не отправляется.
И кстати с большим кол-вом писем могут быть проблемы. В скрипте не обнаружил защиту от повторного запуска скрипта.
Допустим лимит стоит 50 писем за раз, а запуск скрипта каждую минуту.
В большинстве случаев все работает и 50 писем успевают отправится за 1 минуту.
Но в какой-то час SMTP или сеть начнет тормозить и отправка станет занимать 3-5 сек.
За минуту успеют отправится только 20 писем, затем запуститься второй скрипт и начнет отправлять оставшиеся 30 письма, т.к. они еще не помечены как отправленные. В итоге пользователю будут приходить дубли.
Допустим лимит стоит 50 писем за раз, а запуск скрипта каждую минуту.
В большинстве случаев все работает и 50 писем успевают отправится за 1 минуту.
Но в какой-то час SMTP или сеть начнет тормозить и отправка станет занимать 3-5 сек.
За минуту успеют отправится только 20 писем, затем запуститься второй скрипт и начнет отправлять оставшиеся 30 письма, т.к. они еще не помечены как отправленные. В итоге пользователю будут приходить дубли.
Мда… не продумал. Думаю тогда сделать статус Отправляется и в кроне перед циклом отправки добавить цикл, выставляющий этот статус. Это просто можно сегодня сделать. А вот я обещал еще кнопки сделать удобнее. Это сложнее надеюсь за 2 часа справлюсь и тоже сегодня сделаю.
Не возражаете если я ваш хук включу в состав компонента?
Конечно нет. К тому же я его не сам писал, а взял из FormIt github.com/Sterc/FormIt/blob/develop/core/components/formit/src/FormIt/Hook/Email.php#L61
Просто убрал все лишнее что сейчас не нужно было мне и адаптировал под компонент.
И он на первое время сойдет, но для полноценной замены желательно чтобы поддерживал все параметры FormIt, такие как скрытые копии, html или текст сообщение, вложения и прочее.
docs.modx.com/extras/revo/formit/formit.hooks/formit.hooks.email
Можно не все их в таблицу добавлять, оставить только важные, которые отображать в компоненте, а остальные в поле properties в json-виде, так расширять можно будет без правки схемы. Главное чтобы скрипт отправки поддерживал.
И у FormIt есть еще хук FormItAutoResponder docs.modx.com/extras/revo/formit/formit.hooks/formit.hooks.formitautoresponder
Это бывает полезно для обратной связи когда email уходит менеджеру, а пользователю дополнительно отправляется другой email со своим шаблоном «Спасибо за ваш отзыв. Менеджер уже занимается вашим вопросом»
Просто убрал все лишнее что сейчас не нужно было мне и адаптировал под компонент.
И он на первое время сойдет, но для полноценной замены желательно чтобы поддерживал все параметры FormIt, такие как скрытые копии, html или текст сообщение, вложения и прочее.
docs.modx.com/extras/revo/formit/formit.hooks/formit.hooks.email
Можно не все их в таблицу добавлять, оставить только важные, которые отображать в компоненте, а остальные в поле properties в json-виде, так расширять можно будет без правки схемы. Главное чтобы скрипт отправки поддерживал.
И у FormIt есть еще хук FormItAutoResponder docs.modx.com/extras/revo/formit/formit.hooks/formit.hooks.formitautoresponder
Это бывает полезно для обратной связи когда email уходит менеджеру, а пользователю дополнительно отправляется другой email со своим шаблоном «Спасибо за ваш отзыв. Менеджер уже занимается вашим вопросом»
Конечно нет. К тому же я его не сам писал, а взял из FormIt github.com/Sterc/FormIt/blob/develop/core/components/formit/src/FormIt/Hook/Email.php#L61Ну я так и пишу. Подсмотреть убрать лишнее и добавить нужное :).
Просто убрал все лишнее что сейчас не нужно было мне и адаптировал под компонент.
С поддержкой всех параметров сложно. Не думаю что я буду это делать :(. У меня в этом потребности нет. И все прописать достаточно сложно. Сегодня я сделаю топбар и статус отправляется и выложу на гитхаб. Если вы захотите, можете сделать форк доработать компонент прислать мне пулл-реквест.
насчет FormItAutoResponder подумаю как прикрутить.
насчет FormItAutoResponder подумаю как прикрутить.
Ну если кто и будет это делать, то просто создать еще один сниппет, который понимает парметры FormItAutoResponder и добавляет еще одно письмо в очередь.
Я для своего хука так и делал. Через FormIt можно указать несколько получателей в параметре &emailTo, я на каждого создавал отдельное письмо в очереди.
Сделал EmailQueueAutoResponder и остальное что хотел :). Смотрите изменения в теме и качайте новую версию с модсторе. Планировал 2 часа а потратил весь вечер :(.
Планировал 2 часа а потратил весь вечер :(.А так всегда… Главное что довёл дело до логического конца, молодец!
Классно выглядит новая версия!
Теперь всё логично и понятно — компонент имеет вид завершенного продукта.
Можно смело использовать на всех проектах без опаски за непонятный функционал (такая путаница с кнопками была раньше).
Теперь всё логично и понятно — компонент имеет вид завершенного продукта.
Можно смело использовать на всех проектах без опаски за непонятный функционал (такая путаница с кнопками была раньше).
Можно сделать отправку письма через 12 часов после постановки в очередь? т.е. задержка после добавления в очередь
Ну можно либо крон раз в 12 часов поставить либо в кроне send.php строка 11
$q->where(array('status'=>1, 'date:<'=> date('Y-m-d', strtotime("-12 hours"))));
Примерно так. Не тестировал.
Можно еще часть отправлять через 12 часов а часть сразу :). Это допустим те, что через 12 часов отправляете в очередь с sender_package = «12hours». А в кроне при отправке фильтровать по sender_package.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.