Sendex и AjaxForm - подписка на рассылку ч/з ajax

Sendex, как учебный компонент, особо не развивается, но он довольно удобен и приятен в обращении изнутри сайта. В связи с чем регулярно его использую. Но вот «снаружи», на фронте, он не очень удобен. Очередная просьба от клиента «чтобы окошко появлялось, а страничка не перезагружалась», побудила переписать для себя сниппет так, чтобы потом пользовать его через ajaxform. Получилось три чанка, один сниппет и немного джаваскрипта.

Итак.

Сниппет Sendex_custom принимает четыре действия (action):
  • check — проверка, подписан ли текущий пользователь. Если да — возвращает его код, если нет — false
  • confirm — подтверждение подписки при переходе по ссылке из письма. Если ты перешел по ссылке и подписка подтвердилась, выставляется плейсхолдер sendex.confirm с содержимым чанка из параметра 'tpl'
  • subscribe
  • unsubscribe
В том месте, где должна быть форма подписки, проверяем, подписан ли пользователь, и показываем ему соответствующую форму (id — идентификатор подписки):

{set $subscribed = '!sendex_custom' | snippet : ['id' => 1, 'action' => 'check']}
<div id="sendexForm">
	{if $subscribed}
		{'!ajaxform' | snippet : [
			'snippet' => 'sendex_custom',
			'form' => 'sendex.unsubscribe',
			'id' => 1,
			'action' => 'unsubscribe',
			'code' => $subscribed,
		]}
	{else}
		{'!ajaxform' | snippet : [
			'snippet' => 'sendex_custom',
			'form' => 'sendex.subscribe',
			'id' => 1,
			'action' => 'subscribe',
			'tplActivate' => 'sendex.activate',
		]}
	{/if}
</div>

Модифицированные чанки выглядят вот так:

sendex.subscribe:

<form id="sendexForm_form_subscribe">
	<div class="hidden"><input type="text" name="address"></div><!-- это только ради антиспама -->
	<input type="email" name="email" value="{$_modx->user.email}" placeholder="Ваш e-mail">
	<button type="submit">Подписаться</button>
</form>

sendex.unsubscribe:

<form id="sendexForm_form_unsubscribe">
	<div class="hidden"><input type="text" name="address"></div>
	<input type="hidden" name="code" value="[[+code]]">
	<button type="submit">Отписаться</button>
</form>

Добавляем к этому небольшую js-обвязочку:

$(document).on('af_complete', function(e, response) {
	if (response.success) {
		switch (response.form.attr('id')) {
			case 'sendexForm_form_subscribe':
				$('#sendexForm').html('Вы подписались на рассылку!');
				break;
					
			case 'sendexForm_form_unsubscribe':
				$('#sendexForm').html('Вы отписались от рассылки');
				break;
		}
	}	
});

Теперь пользователь будет видеть стандартные красные jGrowl-всплывашки с ошибками, если что-то сделает не так, и зеленые с сообщением «все отлично, пройди по ссылке». А форма в случае успешной отправки будет заменяться нужным нам текстом.

Итак, пользователю пришла ссылка, он по ней перешел. Надо бы его встретить. В дело вступает confirm.

Вставляем перед формами подписки (я вставил в самое начало шаблона, еще до head) следующий код:

{$_modx->runSnippet('!sendex_custom', [
	'id' => 1, 
	'action' => 'confirm', 
	'tpl' => 'sendex.confirm'
])}

И в любом месте шаблона вызываем плейсхолдер с сообщением об успехе.

{'sendex.confirm' | placeholder}

Чанк sendex.confirm у меня выглядит вот так:

<div id="sendexConfirm" class="mfp-hide">
	<h4>Поздравляем!</h4>
	<p>Вы только что успешно 
подписались на нашу рассылку.</p>
	<p>Отписаться вы можете в любой момент, перейдя по ссылке "Отписаться" из очередного письма.</p>
	<button onclick="$.magnificPopup.close()">ОК</button>
</div>
{set $script}
	<script>
		$(document).on('ready', function() {
			$.magnificPopup.open({
				type: 'inline',
				items: {
					src: '#sendexConfirm',
				}
			});
		});
	</script>
{/set}
{$_modx->regClientScript($script, true)}

Здесь у нас невидимый блок с сообщением и кусок скрипта, который его покажет во всплывающем окне.
Я использую для всплывающих окон MagnificPopup. Этот код рассчитан на него. Если вы используете другой скрипт для поп-апов, легко переделаете код под себя.

Ну и теперь, собственно, сам сниппет.

<?php
if (!empty($_POST['address']) or empty($scriptProperties['action']) or !in_array($scriptProperties['action'], array('subscribe', 'confirm', 'check', 'unsubscribe'))) {
	return $AjaxForm->error('Неверный запрос');
}

if (!$modx->loadClass('pdofetch', MODX_CORE_PATH . 'components/pdotools/model/pdotools/', false, true)) {
    return false;
}
$pdoFetch = new pdoFetch($modx, $scriptProperties);

$Sendex = $modx->getService('sendex','Sendex',$modx->getOption('sendex_core_path',null,$modx->getOption('core_path').'components/sendex/').'model/sendex/',$scriptProperties);
if (!($Sendex instanceof Sendex)) {
	return false;
}

if (empty($scriptProperties['id']) or !($newsletter = $modx->getObject('sxNewsletter', $scriptProperties['id'])) or !$newsletter->active) {
	return false;
}

if (empty($scriptProperties['linkTTL'])) {
	$linkTTL = 1800;
}

$placeholders = $newsletter->toArray();
$placeholders['message'] = '';
$placeholders['error'] = 0;

if ($modx->user->isAuthenticated($modx->context->key)) {
	$placeholders = array_merge(
		$modx->user->toArray(),
		$modx->user->Profile->toArray(),
		$placeholders
	);
}

$isAuthenticated = $modx->user->isAuthenticated($modx->context->key);

switch ($scriptProperties['action']) {
	case 'confirm':
		if (!empty($_GET['hash']) && $_GET['sx_action'] == $scriptProperties['action']) {
			if ($response = $newsletter->confirmEmail($_GET['hash'])) {
				return $modx->setPlaceholder('sendex.confirm', $pdoFetch->getChunk($tpl));
			} else {
				return false;
			}
		}
		break;
		
	case 'check':
		if ($isAuthenticated and $subs_id = $newsletter->isSubscribed(0, $modx->user->Profile->email) and $subscriber = $modx->getObject('sxSubscriber', $subs_id)) {
			return $subscriber->code;
		}
		return false;
		break;
		
	case 'subscribe':
		$error = '';
		$params = array();
		if (empty($_POST['email'])) {
			$error = $modx->lexicon('sendex_subscribe_err_email_ns');
		} else {
			$email = strip_tags(trim(preg_replace('/\s{2,}/', ' ', $_POST['email'])));
			
			if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
				$error = 'Некорректный email';
			} else {
				$response = $newsletter->checkEmail($email, 0, $linkTTL);
				if ($response === true) {
					$error = $modx->lexicon('sendex_subscribe_err_already');
				} elseif ($response === false) {
					$error = $modx->lexicon('sendex_subscribe_err_email_wrong');
				} else {
					$params['hash'] = $response;
					$params['sx_action'] = 'confirm';
					$placeholders['link'] = $modx->makeUrl($modx->resource->id, $modx->context->key, $params, 'full');
					$placeholders['email_body'] = $modx->getChunk($tplActivate, $placeholders);
					$response = $Sendex->sendEmail($email, $placeholders);
					if ($response !== true) {
						$error = $modx->lexicon('sendex_subscribe_err_email_send');
					} else {
						return $AjaxForm->success('Мы отправили Вам письмо со ссылкой - перейдите по ней, и Ваша подписка будет активирована.');
					}
				}
			}
		}
		if (!empty($error)) {
			return $AjaxForm->error($error);
		}
		
		break;
		
	case 'unsubscribe':
		if (!empty($_REQUEST['code']) and $response = $newsletter->unSubscribe($_REQUEST['code'])) {
			return $AjaxForm->success('Вы отписались от рассылки');
		}
		return $AjaxForm->error('Ошибка');
		
		break;
}
mngatoff
26 августа 2017, 19:52
modx.pro
26
4 936
+8

Комментарии: 17

Любовь
15 сентября 2017, 08:20
0
Подскажите, пожалуйста. У меня вот такие конструкции {} не отрабатываются, pdoTools стоит последний, т.е. Fenom там по идее должен быть. Что-то ещё нужно установить?
    Александр
    15 сентября 2017, 09:09
    0
    Системные настройки
    pdotools_fenom_default — Да
      Любовь
      15 сентября 2017, 09:33
      0
      Спасибо! А после этого стандартные конструкции MODX будут работать?
      И эта опция оказывается включена, в чанках отрабатывается, а в шаблонах и на страницах нет.
        mngatoff
        23 сентября 2017, 14:05
        0
        «использовать fenom на страницах» тоже надо включить. И следить потом, чтобы все что с фигурными скобками и при этом не феном (какая-нибудь гугл-аналитика, к примеру) либо было в теге {ignore}{/ignore}, либо имело пробел после скобки.
        Стандартные теги будут работать, но возможны сюрпризы. Да и зачем.
    TITAN-UZ
    23 сентября 2017, 13:54
    0
    пользователь это зарегистрированный юзер или гости тоже могут использовать
      mngatoff
      23 сентября 2017, 14:05
      +1
      гости тоже
      antonlynin
      20 декабря 2017, 16:24
      0
      Здравствуйте!
      Все сделал по инструкции, все работает при подписке, все красиво. Спасибо.
      Но. когда отписываюсь из письма, переходит на страницу и все. Ничего не пишет, что я отписался, и из админки не отписывается. В чем может быть проблема? Спасибо!
        mngatoff
        21 декабря 2017, 14:30
        0
        приветствую. В данном примере отписки по ссылке нет, ее надо дописать — по аналогии, сниппет принимающий парметры из ссылки и отписывающий человека, а затем показывающий сообщение
        Кирилл
        22 марта 2018, 15:06
        0
        Подскажите, я так понимаю что Sendex не поддерживает синтаксис fenom в чанках?? Например чанк tpl.Sendex.activate и плейсхолдер [[+link]] не преобразется в fenom, ни {$link}, ни {$_pls['link']}
          Stan Ezersky
          18 октября 2018, 10:28
          0
          Кто дописывал возможность отписаться?
            Александр
            19 октября 2018, 23:45
            0
            Судя по коду, тут эта возможность уже есть.
            SEQUEL.ONE
            04 апреля 2019, 23:21
            0
            Так не смог разобраться в чём дело. Выдаёт ошибку.

            Ошибка
            Не могу отправить email.
              SEQUEL.ONE
              04 апреля 2019, 23:57
              0
              Вот что выдаёт в консоле:

              {"success":false,"message":"\u041d\u0435 \u043c\u043e\u0433\u0443 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c email.","data":[]}
                Роман
                25 сентября 2019, 19:32
                0
                Решился вопрос? Та же проблема
                  Роман
                  25 сентября 2019, 20:21
                  0
                  Попробовал на другом сайте, та же история.
                    SEQUEL.ONE
                    26 сентября 2019, 13:16
                    0
                    А у вас в базу записываются email'ы после перехода из письма? Попробовал сегодня с утра, письма приходят, а данные в базу не записываются. Думаю это уже на стороне Sendex проблема. Будем разбираттся)
                  Роман
                  26 сентября 2019, 08:00
                  0
                  В статье не указан чанк: sendex.activate создай его, можно копирнуть и переименовать из tpl.Sendex.activate

                  п.с. mngatoff допиши в статью, что бы другие как я не задавали вопросов.
                  п.с.с. спасибо за статью!
                  Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                  17