Рассылка по расписанию

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

На всякий случай напоминаю алгоритм работы компонента:

  1. Создаём рассылку и указываём ей свойства. Обязательно указать шаблон или сниппет.

  2. Добавляем пользователей (или они добавляются самостоятельно, через сайт)

  3. На странице очереди писем добавляем новые, путем выбора рассылки. В зависимости от того, что в ней указано, текст письма генерируется сниппетом или шаблоном.

  4. Отправляем письма. Можно вручную, из админки, или скриптом, по расписанию.

Всё, кроме скрипта мы уже сделали.

Отправка писем по расписанию

Учитывая, что все необходимое у нас уже прописано в классе sxQueue, нужно только подключить модель компонента, выбрать записи и вызвать метод send().


<?php
// Это для вызова из директории разработки
if (file_exists(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/config.core.php')) {
    require_once dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/config.core.php';
}
else {
    require_once dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))) . '/config.core.php';
}

// Получаем конфиг и вызываем MODX
require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
require_once MODX_CONNECTORS_PATH . 'index.php';

// Добавляем модель
$modx->addPackage('sendex', MODX_CORE_PATH . 'components/sendex/model/');

// Выбираем 100 писем
$q = $modx->newQuery('sxQueue');
$q->limit($modx->getOption('sendex_queue_limit', null, 100, true));

// Отправляем
$queue = $modx->getCollection('sxQueue');
/** @var sxQueue $email */
foreach ($queue as $email) {
    $email->send();
}

После отправки письмо удаляется, так что скрипт можно запускать хоть через каждые 2 минуты - нагрузки он не создаст.

Я добавил этот файл в /core/components/sendex/cron/send.php, чтобы его нельзя было вызвать из браузера. Зачем давать возможность непонятно кому отправлять ваши письма?

Добавить в cron его можно или вручную (см. документацию к своему хостингу). Обычно нужно зайти на сервер через SSH и набрать crontab -e и в редакторе добавить:


*/2  *  * * *   php /var/www/site/www/core/components/sendex/cron/send.php

Сервер будет дергать файл каждые 2 минуты, и все рассылки улетят мгновенно. На лабораторном тарифе MODXCloud я не смог зайти в SSH, наверное отключено.

Вот коммит с файлом send.php.

В принципе, вот и всё последнее занятие, но добавйте еще пройдёмся по теме добавления писем в очередь через API.

Добавление писем через API

Не знаю, как сделано в других решениях, но я не представляю, как можно автоматизировать добавление писем в рассылку.

Ведь может быть огромное количество ситуаций и условий. Рассылка новых тикетов из раздела вопросы, или новостей с шаблоном 5 из родителя 13 - это все не нарисуешь в интерфейсе.

Поэтому, автоматическое добавление писем в рассылку мы оставляем на совести разработчика сайта. Мы ему уже все подготовили:

  1. Создать сниппет или шаблон, который будет формировать тело письма

  2. Создать рассылку и указать сниппет или шаблон

  3. Получить объект sxNewsletter и вызвать метод addQueues

То есть, все очень просто.

На всякий случай вот код:


$modx->addPackage('sendex', MODX_CORE_PATH . 'components/sendex/model/');

/** @var sxNewsletter $newsletter */
if ($newsletter = $modx->getObject('sxNewsletter', array('name' => 'Рассылка новостей'))) {
    $newsletter->addQueues();
}

Можно вызывать этот код при публикации новых страниц, по расписанию или еще как то. В своем сниппете или шаблоне вы можете выбирать любые документы и по любым условиям. Вообще, можно набивать рассылки какой угодно информацией.

Я считаю, это достаточно перспективная разработка, которую будет легко и приятно использовать. Тем более, мы написали всё, чтобы управлять рассылками вручную.

Заключение

На этом наш курс занятий заканчивается.

Задавайте вопросы, будем обсуждать непонятности. В ближайшее время я постараюсь просмотреть код, возможно немного доработать логику и выложить уже в репозиторий.

За всеми изменениями можно следить на GitHub.

На всякий случай оставляю доступ к моему сайту разработки:


Manager:	http://c2263.paas2.ams.modxcloud.com/manager/
Login:		admin
Password:	adminadmin
← Предыдущая заметка
Самостоятельная подписка и отписка пользователя
Комментарии (10)
inetloverАлександр Наумов
13.01.2014 17:07

Курс замечательный, правда глубоко не углублялся пока, но это дело времени. Единственно что бы еще хотелось узнать по данной теме, это то, как проектировать плейсхолдеры. Может быть кто-нибудь знает материалы на эту тему?

bezumkinВасилий Наумкин
13.01.2014 17:39

Плейсхолдер, это буквально "держатель места". То есть, это какой-то набор символов чанке, который будет заменен значением при работе.

Например, есть чанк

Имя [[+name]]

И ты выводишь его на странице

[[$Chunk?name=`Саша`]]

Выйдет

Имя Саша

Плейсхолдеры нужны для разделения логики и представления, то есть PHP и HTML. Что ты хочешь вывести из PHP - то и задаёшь в чанке, а потом меняешь. Вот смотри, свежая дока по этой теме.

inetloverАлександр Наумов
13.01.2014 17:43

Василий, спасибо за наводку!

Roman Smile
06.04.2014 21:02

В курсе мы не взаимодействовали с формами ресурсов. Но вот, например, нам нужно при редактировании обычного ресурса иметь обычный textfield, но с автокомплитом из некой таблицы в БД. Как реализовать такой текстфилд в компоненте понятно, но как это впихнуть в форму редактирования ресурса?

bezumkinВасилий Наумкин
07.04.2014 09:13

Только плагином на загрузку страницы админки. Так можно зарегистрировать свой javascript, который заменит стандартный textfield.

Roman Smile
16.04.2014 05:15

Ок, удалось успешно подключить вот так:

switch ($modx->event->name) {
    case 'OnManagerPageBeforeRender':
        $modx->controller->addCss(MODX_ASSETS_URL . 'components/new/css/mgr/manager-main.css');
        $modx->controller->addJavascript(MODX_ASSETS_URL . 'components/new/js/mgr/inc/manager-main.js');
        break;
}

Еще вопрос. Как из этого подключенного js повесить функцию на удаление какого-нибудь ресурса через контекстное меню? Ну, когда мы в дереве ресурсов жмем правой кнопкой на документ и выбираем "Удалить".

Roman Smile
16.04.2014 06:38

Или, возможно, посоветуете другой вариант решения следующей задачи. Когда документ меняется пользователем, то нужно делать разные проверки и затем, если необходимо, давать изменяющему документ пользователю окно с сообщением. Это можно реализовать с помощью плагина на OnBeforeDocFormSave:

$modx->event->output('Something did not validate!');

Но это работает только когда юзер открыл форму редактирования документа сохраняет его оттуда. Но документ может изменяться и с помощью того самого контекстного меню "Удалить" или "Снять с публикации". Нужно как-то сделать возможным делать проверки и выдавать модальные окна, когда пользователь не только редактирует документ с помощью формы, но и жмакает в этом контекстном меню. Вот, пока только представляется вариант отправлять отдельный AJAX-запрос, когда юзер тыкает в "Удалить" или "Снять с публикации". Вопрос в том, как изящно повесить функцию на это событие, получив при этом id документа, которому вызвали контекстное меню.

bezumkinВасилий Наумкин
16.04.2014 07:24

Не могу ничего посоветовать, придётся копать исходники самостоятельно.

Roman Smile
18.04.2014 16:26

Вопрос по добавлению своей иконки для своего CRC. Например, в Тикетс для TicketsSection добавление происходит через заданный класс .icon-ticketssection, я нашел его в css: https://github.com/bezumkin/Tickets/search?q=icon-ticketssection&ref=cmdform

А на каком этапе этот класс .icon-ticketssection присваивается узлу в дереве ресурсов?

bezumkinВасилий Наумкин
18.04.2014 16:39

Это делает сам MODX - добавляет класс к иконке.

bezumkin
Василий Наумкин
04.07.2022 23:34
Что-то странное у тебя произошло: миграция есть, и вроде как выполнена, но таблицы при этом отсутств...
inetlover
Александр Наумов
03.07.2022 20:36
Василий, спасибо! Все понятно!
bezumkin
Василий Наумкин
02.07.2022 20:28
Спасибо, поправил!
bezumkin
Василий Наумкин
30.06.2022 03:58
Есть ли возможность формировать &quot;friendly URL aliases&quot;, используя аналог translit MODx? ...
bezumkin
Василий Наумкин
27.06.2022 03:32
Спасибо за исправления, очень выручаешь =) Но учитывая количество не описаных в заметке дополнительн...
bezumkin
Василий Наумкин
27.06.2022 03:10
что будет использоваться для вывода многоуровневого меню Посмотри как работают комментарии на этом ...
bezumkin
Василий Наумкин
25.06.2022 11:56
Поправил, спасибо!
bezumkin
Василий Наумкин
21.06.2022 01:58
onLoad(data) { this.total = data.total }, и onLoad({total}) { this.total = total }, В нашем случ...
bezumkin
Василий Наумкин
20.06.2022 14:01
Прекрасно тебя понимаю, я когда сам в этом разбирался - голова дымилась. Но зато теперь прямо-таки п...
bezumkin
Василий Наумкин
20.06.2022 09:30
Не надо, оно по умолчанию так - я просто чуть более подробно написал.