[Sendex] Компонент управления рассылками

Как вы уже знаете, полтора месяца назад стартовал интересный эксперимент — обучающий курс по разработке компонентов для MODX Revolution.

Как таковых обучающих курсов, наверное, много, но здесь цель была не только чему-то научить, но и написать полезное дополнение.

Довольны должны быть все: читатели наконец получили ответы на многие вопросы, а сообщество — новый компонент для работы с рассылками (на этом поле пока не особо есть из чего выбирать).

И вот сейчас я готов показать, что у нас вышло.

Принцип работы

Компонент состоит из 3х объектов, которые хранятся в отдельных таблицах:
  • sxNewsletter — объект подписки со всеми параметрами: тема, от кого, шаблон оформления и т.д.
  • sxSubscriber — объект подписчик. Здесь обязательно есть email и id подписки. Если юзер был авторизован в момент подписки — то и его id.
  • sxQueue — объект письма, стоящего в очереди на отправку со всеми свойствами.

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

Создаём рассылку:

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

Подписываем пользователя:

В ручном режиме подписать можно только юзера сайта, но компонент работает и с анонимами.

Генерируем письма для всех подписчиков рассылки:

Рассылка смотрит в список подписавшихся и генерирует для них письма, согласно своих настроек.

Отправляем:


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

Сниппет Sendex

Сниппет очень простенький, и позволяет юзерам самостоятельно работать с подпиской: подписываться и отписываться.

Если пользователь авторизован, ему нужно просто нажать на кнопку. Если нет — ему нужно будет подтвердить свой email.

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

Сниппет — это просто пример логики работы, потому что все нужные методы содержатся в классе sxNewsletter и вы можете легко написать свой сниппет, с их использованием.

Основные методы объектов

Объект sxNewsletter
  • addQueues — проходит по своим подписчикам и добавляет для них письма в очередь
  • checkEmail — отправка проверочного кода на email анонимного юзера
  • confirmEmail — подтверждение email анонимного юзера
  • Subscribe — подписка на рассылку любого юзера, по id или email
  • unSubscribe — отписка от рассылки, по уникальному коду подписчика в sxSubscriber
  • isSubscribed — проверка, подписан ли юзер на рассылку по email или id. Возвращает id sxSubscriber или 0.

После создания рассылки её можно получить через MODX API и вызвать генерацию писем:
$modx->addPackage('sendex', MODX_CORE_PATH . 'components/sendex/model/');

/** @var sxNewsletter $newsletter */
if ($newsletter = $modx->getObject('sxNewsletter', 1)) {
	$response = $newsletter->addQueues();
	if ($response !== true) {
		echo $response;die;
	}
}
Метод вернет true или текст ошибки.

Объект sxSubscriber ничего интересного не содержит. Разве что, уникальное поле code, при передаче которого в метод sxNewsletter::unSubscribe() юзер будет отписан от рассылки. Обратите внимание, что само наличие этого объекта — и есть подписка юзера. То есть, при отписке sxSubscriber удаляется.

Объект sxQueue
  • send — отправка письма. Если удачно — оно удаляется из очереди, если нет — выдаёт ошибку и остаётся для следующей попытки.

Таким образом, для отправки всех писем из очереди можно делать так:
$modx->addPackage('sendex', MODX_CORE_PATH . 'components/sendex/model/');

$q = $modx->newQuery('sxQueue');
$queue = $modx->getCollection('sxQueue');
/** @var sxQueue $email */
foreach ($queue as $email) {
	$email->send();
}


Именно так и работает скрипт отправки писем по cron, который лежит в /core/components/sendex/cron/send.php.

Заключение

Итак, вам нужно

  1. Оформить шаблон, создать рассылку и указать его ей.
  2. Подписать юзеров вручную, или через сниппет.
  3. При наступлении како-то события или через api сгенерировать письма.
  4. Отправить письма по расписаниею через cron, или из админки

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

В любом случае, это всего лишь первая бета-версия, и компонент еще будет развиваться. Тем более, у нас есть много людей, которые знают, как именно он работает =)

Sendex распространяется через репозиторий Simple Dream.
Исходный код находится на GutHub.

Следующая заметка
[pdoTools] Версия 1.9.0-beta со своим парсером
Предыдущая заметка
[debugParser] Отлавливаем узкие места


Комментарии ()

  1. Alex Vakhitov 19 декабря 2013, 14:41 # 0
    Теперь и с bezumkin.ru ждать рассылок на почту, с подборками интересного за неделю?
    1. Василий Наумкин 19 декабря 2013, 14:52 # 0
      С подбором вряд ли (у меня всё интересное!), а вот подписку на блоги можно сделать.
      1. Alex Vakhitov 19 декабря 2013, 15:20 # 0
        Раньше сказал бы что круто, если бы твои посты в рассылке приходили, но теперь я подписан на тебя в твиттере (:
    2. Иван Климчук 19 декабря 2013, 17:37 # 0
      Жаль, нет крупного проекта, а так можно было бы подружить его с каким-нибудь инструментом очередей, который письма рассылали бы. При 1к+ юзерах это уже не так просто с точки зрения спама и других всяких ограничений.
      1. Василий Наумкин 19 декабря 2013, 17:52 # 0
        У меня за сегодня только сервер отправил 455 писем, безо всяких рассылок. Проблем пока нет.

        Глядеть можно так
        sudo zcat /var/log/mail.log*z | sudo cat /var/log/mail.log - | awk '/sendmail/{print $6}' | sort | uniq | wc -l

        Ну и все письма ставятся в очередь — отправлять их можно как угодно.
        1. Иван Климчук 19 декабря 2013, 18:58 # 0
          Ну 1k наверное мало, хотя делал как-то сайт, где было ограничение на 100 писем в час. Очередь да, но при большом количестве писем эту очередь удобнее класть в какой-нибудь rabbitmq, я про это. Но это скорее не для рядовых сайтов, а там где количество писем действительно огромно.
          1. Василий Наумкин 19 декабря 2013, 19:41 # 0
            Письма лежат в отдельной таблице и сами по себе никуда не отправляются.

            Их можно доставать из MySql и отправлять хоть чем — тут нет ограничений.
      2. Олег Карягин 19 декабря 2013, 17:57 # 0
        А группы нельзя добавлять? Только по одному пользователю?
        1. Василий Наумкин 19 декабря 2013, 17:59 # 0
          Да.

          Нужно было записаться на курсы и предлагать идеи.
          1. Олег Карягин 19 декабря 2013, 18:01 # 0
            Спасибо, сейчас просто по времени не могу, поэтому не записался, может позже запишусь, прочту, из git возьму форкну) Ну или если у тебя/группы ребят кто обучался есть желание мне кажется это будет удобнее, так же как и отправлять письма по группам/сразу всем.
            1. Василий Наумкин 19 декабря 2013, 18:03 # 0
              Еще скажи «адресатов указывать через запятую».

              Рассылка идет по юзерам, они могут сами подписываться и отписываться, с группами это никак не свзано. Более того, подписка работает и для гостей.
              Если есть большое желание добавить всех пользователей группы — используй методы API.
              1. Олег Карягин 19 декабря 2013, 18:05 # 0
                Я может тогда не верно понял идею отправки, но я добавил к рассылке 3х пользователей, и хочу отправить им письма сейчас, руками. У меня появилось 3 строки, чтобы отправить надо нажать на каждую, верно?
                1. Василий Наумкин 19 декабря 2013, 18:06 # 0
                  Верно.

                  Или добавить скрипт в cron и он сам будет отправлять каждые n минут.
                  1. Олег Карягин 19 декабря 2013, 18:08 # 0
                    Безусловно по крону будет удобнее, но не все смогут настроить. А если магазин и хочу отправить 50-100 пользователям, можно почувствовать себя «чуть обезьянкой».
                    Опять же, пойми верно, это просто рассуждение. В качестве учебного компонента безусловно здорово.
                    1. Василий Наумкин 19 декабря 2013, 18:09 # 0
                      github.com/bezumkin/Sendex/issues/new

                      Пиши
                      Добавить кнопку «отправить все письма сразу»
                      1. Олег Карягин 19 декабря 2013, 18:10 # 0
                        Юмор оценил)
                        Спасибо!
        2. Михаил 19 декабря 2013, 18:52 # 0
          Я еще не дошел до конца, изучаю углубленно.
          1. Максим Степанов 20 декабря 2013, 02:55 # 0
            Попробовал создать рассылку но выдает
            (ERROR @ /assets/components/sendex/connector.php) класс modPHPMailer не поддерживает отмену отправки по некоторым адресам. Используйте метод reset() для очистки всех получателей и после этого добавьте нужных получателей снова.
            1. Василий Наумкин 20 декабря 2013, 08:11 # 0
              Что-то с почтой на сервере.

              Если тестируешь на modx-test.com — то там работа с почтой отключена, во избежания работы спамеров.
              1. Максим Степанов 20 декабря 2013, 10:06 # 0
                Написал в тех поддержку сказали добавить в .htaccess строки:
                php_value mail.add_x_header 1 
                php_value mail.log /home/m/maxim88/mail.log
                теперь все работает, Спасибо за рассылку.
                1. Василий Наумкин 20 декабря 2013, 10:19 # 0
                  На здоровье!

                  Спасибо еще тем ребятам, кто подписался на курсы.
                  1. Михаил 20 декабря 2013, 20:45 # 0
                    А я рад что подписался. Деньги с лихвой оправдал. готов на новые
                    1. Василий Наумкин 20 декабря 2013, 20:55 # 0
                      Это самое главное!

                      Все-таки, главная цель была курс отвести, а Sendex — это побочный эксперимент.
                2. Чикин Артур 09 января 2014, 00:50 # 0
                  Та же самая ошибка, хотя письмо ушло нормально. Куда смотреть?

                  P.S Modxcloud.
              2. Максим Степанов 27 декабря 2013, 04:31 # 0
                Василий сообщение «Подтвердите ваш email!» не выводится? Все остальные сообщения появляются
                1. Николай 29 декабря 2013, 06:57 # 0
                  А в компоненте можно сделать так, чтобы рассылать один и тот же набор писем с интервалом новым юзерам, в зависимости от даты регистрации (или даты отправки первого письма)?
                  1. Чикин Артур 09 января 2014, 00:14 # 0
                    При форматировании шаблонов возник вопрос. В чанках форма имеет следующий вид:

                    <form action="" method="post">
                    Как то не солидно оставлять пустой action и по идее он ведь должен быть заполнен.

                    [[~[[*id]]]]
                    или

                    [[~[[+id]]]]
                    ????
                    1. AK 13 февраля 2014, 13:49 # 0
                      Если я правильно понял, текст рассылки пишется в шаблоне? Если рассылки еженедельные с разным контентом, то нужно править шаблон?
                      А есть возможность сделать следующим образом, в плагине на событие публикации документа в конкретном разделе (это я разберусь как реализовать) писать содержимое документа в шаблон подписки?
                      Как отправлять подписку в очередь на рассылку в доках написано.

                      Подскажите, пожалуйста! Очень нужно, спасибо.
                      1. Василий Наумкин 13 февраля 2014, 14:17 # +1
                        Гораздо лучше и проще вызвать в шаблоне свой сниппет, который получит нужный документ и всё оформит.
                        1. AK 13 февраля 2014, 14:42 # 0
                          мм… не понятно )
                          Нужно, чтобы при публикации новости, она отправлялась в очередь на рассылку.
                        2. AK 13 февраля 2014, 18:30 # 0
                          Пришлось пораскинуть мозгами, может кому подойдет такая реализация, делюсь

                          <?php
                          /**
                          ** Плагин, при публикации ресурса в указанном разделе, передает текст ресурса 
                          ** в шаблон Sendex и отправляет подписку в очередь
                          **
                          ** OnDocPublished срабатывает только при публикации ресурса через контекстное 
                          ** меню документа в дереве ресурсов
                          **
                          ** В шаблоне sendex в месте куда нужно вставлять контент должен быть 
                          ** тег <div id="content"></div>
                          */
                          
                          if ($modx->event->name == 'OnDocPublished') {
                          
                              if ($resource->get('parent') != 5) return; // 5 - ID раздела с новостями
                          
                              // Получение шаблона
                          
                              $response = $modx->runProcessor('element/template/get', array(
                                  'id' => 8 // 8 - ID шаблона sendex
                              ));
                          
                              if ($response->isError()) {
                                  $modx->log(modX::LOG_LEVEL_ERROR, 'SendexOnNewsPublished - ошибка чтения шаблона. '.$response->getMessage());
                                  return;
                              } else {
                                  $template = $response->getObject();
                              }
                              $newTplContent = preg_replace('/(<div.*?id="content"[^>]*>)(.*?)(<\/div>)/i', '<div id="content">'.$resource->get('content').'</div>', $template['content'], 1);
                              
                              $data = $template;
                              $data['content'] = $newTplContent;
                          
                              //Запись контента новости в шаблон
                          
                              $response = $modx->runProcessor('element/template/update', $data);
                          
                              if ($response->isError()) {
                                  $modx->log(modX::LOG_LEVEL_ERROR, 'SendexOnNewsPublished - ошибка записи в шаблон. '.$response->getMessage());
                                  return;
                              } else {
                                  $modx->cacheManager->clearCache();
                              }
                          
                              // Отправка шаблона в очередь рассылки
                          
                              $modx->addPackage('sendex', MODX_CORE_PATH . 'components/sendex/model/');
                          
                              if ($newsletter = $modx->getObject('sxNewsletter', 1)) { // 1 - ID рассылки
                                  $response = $newsletter->addQueues();
                                  if ($response !== true) {
                                      echo $response;die;
                                  }
                              }
                          
                          }
                          return;
                          1. Чикин Артур 13 февраля 2014, 18:36 # +1
                            Вот так лучше инициализировать рассылку:

                            $modx->addPackage('sendex', MODX_CORE_PATH . 'components/sendex/model/');
                            
                            $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();
                            }
                            1. AK 13 февраля 2014, 18:52 # 0
                              Это уже после отправки рассылки в очередь нужно добавить, насколько я понимаю.
                              Я планировал отправку вешать на крон.
                        3. Николай Загумённов 16 апреля 2014, 20:35 # 0
                          А как подписать всех юзеров?
                          1. maslitto 18 июля 2014, 13:19 # 0
                            Кто-нибудь jGROWL к Sendex'y приделывал?
                            1. Андрей Лукьянов 10 сентября 2014, 19:59 # 0
                              Василий, вопрос. Письма приходят, но вся кириллица вопросиками. Не подскажешь куда копать?
                              При этом в самом шаблоне все ок, кириллица как кириллица. А вот уже в письме вопросики.
                              1. Василий Наумкин 10 сентября 2014, 20:50 # 0
                                В настройки сервера и кодировки писем, куда ж еще?
                              2. Jurij 21 сентября 2014, 18:06 # 0
                                Попробовал на одном проекте. К сожалению не доходят письма на mail.ru. Приходит ответ с ошибкой 505, отфутболивает как спам…
                                Где копать?
                                1. Василий Наумкин 21 сентября 2014, 18:25 # 0
                                  Нужно настраивать свою почту, добавлять SPF и DKIM.

                                  Можно использовать отправку через почту для доменов Яндекс.
                                2. Jurij 21 сентября 2014, 18:32 # 0
                                  Благодарю за оперативный ответ. Буду пробовать.
                                  1. Юрий Фомин 05 ноября 2014, 19:14 # 0
                                    Доброго времени суток. Вот в чем проблема, в которой никак не могу разобраться, может кто подскажет.
                                    При подписке все срабатывает, но на почту подписчику приходит письмо с следующим содержанием:

                                    От кого: «support@...» <...@gmail.com>
                                    Кому: ...@mail.ru
                                    сегодня, 18:04
                                    .../?v=2.1.5&sx_unsubscribed=1&hash=f0f36db8e79c06127ba6c6da573860fd13d60943&sx_action=confirm

                                    Почему только она?
                                    Шаблон в рассылке указан базовый, но пробовал подставлять свой, результат тот-же.
                                    Спасибо за подсказки.
                                    1. Dmitry 17 ноября 2014, 18:16 # 0
                                      Отличный компонент!
                                      Все здорово не смог разобраться только с вопросом — Как прикрепить файл к письму?
                                      Подскажите пожалуйста куда копать?
                                      1. Артур 22 ноября 2014, 20:19 # 0
                                        Привет! мне как человеку с небольшими знаниями в php всё таки удалось поставить, после прочтения документации.
                                        Возникло парочка вопросов:
                                        — я как понял нет возможности добавить уже созданных «гостей подписчиков» в новую подписку?
                                        — кто нибудь использовал ajax and sendex? если да поделитесь )
                                        — про бывал эксперементировать, правда методом тыка, задача стояла следущая: для гостей возможность отписаться и подписаться с одной формы вот что начудил:

                                        <form action="" method="post">
                                                     <div class="label">Ваша почта:</div>
                                        	<input type="textfield" name="email" value="" placeholder="mail@mail.ru">
                                        	<input type="hidden" name="sx_action" value="subscribe">
                                        <button type="submit" class="btn_sub">Подписаться</button>
                                         <button onclick="window.location.href='[[~[[++site_start]]?scheme=`full`&sx_action=`unsubscribe`&code=`[[+subscriber.code]]`]]'" >Отписаться</button>
                                        	<p>[[+message]]</p>	
                                        </form>
                                        Ничего хорошего не вышло «только подписка» ))

                                        Еще такой вопрос: после того как ввёл мыло нажал отправить все ушла ссылка, а есть ли возможность выводить сообщение после передачи в "[[+message]]", например: На ваш email ушло подтверждение, а ту как то для меня это не правильно, действия были произведены и я не увидел удачно или нет.

                                        Также хотелось бы узнать: можно опустить подписку с подтверждением на мыло, что бы ввел мыло, кликнул и стал true подписчиком.

                                        Добавление новых комментариев отключено.