vendor/contao/newsletter-bundle/src/Resources/contao/modules/ModuleSubscribe.php line 59

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. /**
  11.  * Front end module "newsletter subscribe".
  12.  *
  13.  * @property string $nl_subscribe
  14.  * @property array  $nl_channels
  15.  * @property string $nl_template
  16.  * @property string $nl_text
  17.  * @property bool   $nl_hideChannels
  18.  */
  19. class ModuleSubscribe extends Module
  20. {
  21.     /**
  22.      * Template
  23.      * @var string
  24.      */
  25.     protected $strTemplate 'nl_default';
  26.     /**
  27.      * Display a wildcard in the back end
  28.      *
  29.      * @return string
  30.      */
  31.     public function generate()
  32.     {
  33.         $request System::getContainer()->get('request_stack')->getCurrentRequest();
  34.         if ($request && System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
  35.         {
  36.             $objTemplate = new BackendTemplate('be_wildcard');
  37.             $objTemplate->wildcard '### ' $GLOBALS['TL_LANG']['FMD']['subscribe'][0] . ' ###';
  38.             $objTemplate->title $this->headline;
  39.             $objTemplate->id $this->id;
  40.             $objTemplate->link $this->name;
  41.             $objTemplate->href StringUtil::specialcharsUrl(System::getContainer()->get('router')->generate('contao_backend', array('do'=>'themes''table'=>'tl_module''act'=>'edit''id'=>$this->id)));
  42.             return $objTemplate->parse();
  43.         }
  44.         $this->nl_channels StringUtil::deserialize($this->nl_channels);
  45.         // Return if there are no channels
  46.         if (empty($this->nl_channels) || !\is_array($this->nl_channels))
  47.         {
  48.             return '';
  49.         }
  50.         return parent::generate();
  51.     }
  52.     /**
  53.      * Generate the module
  54.      */
  55.     protected function compile()
  56.     {
  57.         // Overwrite default template
  58.         if ($this->nl_template)
  59.         {
  60.             $this->Template = new FrontendTemplate($this->nl_template);
  61.             $this->Template->setData($this->arrData);
  62.         }
  63.         $this->Template->email '';
  64.         $this->Template->captcha '';
  65.         // Activate e-mail address
  66.         if (strncmp(Input::get('token'), 'nl-'3) === 0)
  67.         {
  68.             $this->activateRecipient();
  69.             return;
  70.         }
  71.         $objWidget null;
  72.         // Set up the captcha widget
  73.         if (!$this->disableCaptcha)
  74.         {
  75.             $arrField = array
  76.             (
  77.                 'name' => 'subscribe_' $this->id,
  78.                 'label' => $GLOBALS['TL_LANG']['MSC']['securityQuestion'],
  79.                 'inputType' => 'captcha',
  80.                 'eval' => array('mandatory'=>true)
  81.             );
  82.             $objWidget = new FormCaptcha(FormCaptcha::getAttributesFromDca($arrField$arrField['name']));
  83.         }
  84.         $strFormId 'tl_subscribe_' $this->id;
  85.         // Validate the form
  86.         if (Input::post('FORM_SUBMIT') == $strFormId)
  87.         {
  88.             $varSubmitted $this->validateForm($objWidget);
  89.             if ($varSubmitted !== false)
  90.             {
  91.                 $this->addRecipient(...$varSubmitted);
  92.             }
  93.         }
  94.         // Add the captcha widget to the template
  95.         if ($objWidget !== null)
  96.         {
  97.             $this->Template->captcha $objWidget->parse();
  98.         }
  99.         $session System::getContainer()->get('session');
  100.         // Confirmation message
  101.         if ($session->isStarted())
  102.         {
  103.             $flashBag $session->getFlashBag();
  104.             if ($flashBag->has('nl_confirm'))
  105.             {
  106.                 $arrMessages $flashBag->get('nl_confirm');
  107.                 $this->Template->mclass 'confirm';
  108.                 $this->Template->message $arrMessages[0];
  109.             }
  110.         }
  111.         $arrChannels = array();
  112.         $objChannel NewsletterChannelModel::findByIds($this->nl_channels);
  113.         // Get the titles
  114.         if ($objChannel !== null)
  115.         {
  116.             while ($objChannel->next())
  117.             {
  118.                 $arrChannels[$objChannel->id] = $objChannel->title;
  119.             }
  120.         }
  121.         // Default template variables
  122.         $this->Template->channels $arrChannels;
  123.         $this->Template->showChannels = !$this->nl_hideChannels;
  124.         $this->Template->submit StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['subscribe']);
  125.         $this->Template->channelsLabel $GLOBALS['TL_LANG']['MSC']['nl_channels'];
  126.         $this->Template->emailLabel $GLOBALS['TL_LANG']['MSC']['emailAddress'];
  127.         $this->Template->formId $strFormId;
  128.         $this->Template->id $this->id;
  129.         $this->Template->text $this->nl_text;
  130.         $this->Template->requestToken System::getContainer()->get('contao.csrf.token_manager')->getDefaultTokenValue();
  131.     }
  132.     /**
  133.      * Activate a recipient
  134.      */
  135.     protected function activateRecipient()
  136.     {
  137.         $this->Template = new FrontendTemplate('mod_newsletter');
  138.         $optIn System::getContainer()->get('contao.opt_in');
  139.         // Find an unconfirmed token
  140.         if ((!$optInToken $optIn->find(Input::get('token'))) || !$optInToken->isValid() || \count($arrRelated $optInToken->getRelatedRecords()) < || key($arrRelated) != 'tl_newsletter_recipients' || \count($arrIds current($arrRelated)) < 1)
  141.         {
  142.             $this->Template->mclass 'error';
  143.             $this->Template->message $GLOBALS['TL_LANG']['MSC']['invalidToken'];
  144.             return;
  145.         }
  146.         if ($optInToken->isConfirmed())
  147.         {
  148.             $this->Template->mclass 'error';
  149.             $this->Template->message $GLOBALS['TL_LANG']['MSC']['tokenConfirmed'];
  150.             return;
  151.         }
  152.         $arrRecipients = array();
  153.         // Validate the token
  154.         foreach ($arrIds as $intId)
  155.         {
  156.             if (!$objRecipient NewsletterRecipientsModel::findByPk($intId))
  157.             {
  158.                 $this->Template->mclass 'error';
  159.                 $this->Template->message $GLOBALS['TL_LANG']['MSC']['invalidToken'];
  160.                 return;
  161.             }
  162.             if ($optInToken->getEmail() != $objRecipient->email)
  163.             {
  164.                 $this->Template->mclass 'error';
  165.                 $this->Template->message $GLOBALS['TL_LANG']['MSC']['tokenEmailMismatch'];
  166.                 return;
  167.             }
  168.             $arrRecipients[] = $objRecipient;
  169.         }
  170.         $time time();
  171.         $arrAdd = array();
  172.         $arrCids = array();
  173.         // Activate the subscriptions
  174.         foreach ($arrRecipients as $objRecipient)
  175.         {
  176.             $arrAdd[] = $objRecipient->id;
  177.             $arrCids[] = $objRecipient->pid;
  178.             $objRecipient->tstamp $time;
  179.             $objRecipient->active '1';
  180.             $objRecipient->save();
  181.         }
  182.         $optInToken->confirm();
  183.         // HOOK: post activation callback
  184.         if (isset($GLOBALS['TL_HOOKS']['activateRecipient']) && \is_array($GLOBALS['TL_HOOKS']['activateRecipient']))
  185.         {
  186.             foreach ($GLOBALS['TL_HOOKS']['activateRecipient'] as $callback)
  187.             {
  188.                 $this->import($callback[0]);
  189.                 $this->{$callback[0]}->{$callback[1]}($optInToken->getEmail(), $arrAdd$arrCids);
  190.             }
  191.         }
  192.         // Confirm activation
  193.         $this->Template->mclass 'confirm';
  194.         $this->Template->message $GLOBALS['TL_LANG']['MSC']['nl_activate'];
  195.     }
  196.     /**
  197.      * Validate the subscription form
  198.      *
  199.      * @param Widget $objWidget
  200.      *
  201.      * @return array|bool
  202.      */
  203.     protected function validateForm(Widget $objWidget=null)
  204.     {
  205.         // Validate the e-mail address
  206.         $varInput Idna::encodeEmail(Input::post('email'true));
  207.         if (!Validator::isEmail($varInput))
  208.         {
  209.             $this->Template->mclass 'error';
  210.             $this->Template->message $GLOBALS['TL_LANG']['ERR']['email'];
  211.             return false;
  212.         }
  213.         $this->Template->email $varInput;
  214.         // Validate the channel selection
  215.         $arrChannels Input::post('channels');
  216.         if (!\is_array($arrChannels))
  217.         {
  218.             $this->Template->mclass 'error';
  219.             $this->Template->message $GLOBALS['TL_LANG']['ERR']['noChannels'];
  220.             return false;
  221.         }
  222.         $arrChannels array_intersect($arrChannels$this->nl_channels); // see #3240
  223.         if (empty($arrChannels) || !\is_array($arrChannels))
  224.         {
  225.             $this->Template->mclass 'error';
  226.             $this->Template->message $GLOBALS['TL_LANG']['ERR']['noChannels'];
  227.             return false;
  228.         }
  229.         $this->Template->selectedChannels $arrChannels;
  230.         // Check if there are any new subscriptions
  231.         $arrSubscriptions = array();
  232.         if (($objSubscription NewsletterRecipientsModel::findBy(array("email=? AND active='1'"), $varInput)) !== null)
  233.         {
  234.             $arrSubscriptions $objSubscription->fetchEach('pid');
  235.         }
  236.         $arrChannels array_diff($arrChannels$arrSubscriptions);
  237.         if (empty($arrChannels))
  238.         {
  239.             $this->Template->mclass 'error';
  240.             $this->Template->message $GLOBALS['TL_LANG']['ERR']['subscribed'];
  241.             return false;
  242.         }
  243.         // Validate the captcha
  244.         if ($objWidget !== null)
  245.         {
  246.             $objWidget->validate();
  247.             if ($objWidget->hasErrors())
  248.             {
  249.                 return false;
  250.             }
  251.         }
  252.         return array($varInput$arrChannels);
  253.     }
  254.     /**
  255.      * Add a new recipient
  256.      *
  257.      * @param string $strEmail
  258.      * @param array  $arrNew
  259.      */
  260.     protected function addRecipient($strEmail$arrNew)
  261.     {
  262.         // Remove old subscriptions that have not been activated yet
  263.         if (($objOld NewsletterRecipientsModel::findOldSubscriptionsByEmailAndPids($strEmail$arrNew)) !== null)
  264.         {
  265.             while ($objOld->next())
  266.             {
  267.                 $objOld->delete();
  268.             }
  269.         }
  270.         $time time();
  271.         $arrRelated = array();
  272.         // Add the new subscriptions
  273.         foreach ($arrNew as $id)
  274.         {
  275.             $objRecipient = new NewsletterRecipientsModel();
  276.             $objRecipient->pid $id;
  277.             $objRecipient->tstamp $time;
  278.             $objRecipient->email $strEmail;
  279.             $objRecipient->active '';
  280.             $objRecipient->addedOn $time;
  281.             $objRecipient->save();
  282.             // Remove the deny list entry (see #4999)
  283.             if (($objDenyList NewsletterDenyListModel::findByHashAndPid(md5($strEmail), $id)) !== null)
  284.             {
  285.                 $objDenyList->delete();
  286.             }
  287.             $arrRelated['tl_newsletter_recipients'][] = $objRecipient->id;
  288.         }
  289.         $optIn System::getContainer()->get('contao.opt_in');
  290.         $optInToken $optIn->create('nl'$strEmail$arrRelated);
  291.         // Get the channels
  292.         $objChannel NewsletterChannelModel::findByIds($arrNew);
  293.         // Prepare the simple token data
  294.         $arrData = array();
  295.         $arrData['token'] = $optInToken->getIdentifier();
  296.         $arrData['domain'] = Idna::decode(Environment::get('host'));
  297.         $arrData['link'] = Idna::decode(Environment::get('base')) . Environment::get('request') . ((strpos(Environment::get('request'), '?') !== false) ? '&' '?') . 'token=' $optInToken->getIdentifier();
  298.         $arrData['channel'] = $arrData['channels'] = implode("\n"$objChannel->fetchEach('title'));
  299.         // Send the token
  300.         $optInToken->send(
  301.             sprintf($GLOBALS['TL_LANG']['MSC']['nl_subject'], Idna::decode(Environment::get('host'))),
  302.             System::getContainer()->get('contao.string.simple_token_parser')->parse($this->nl_subscribe$arrData)
  303.         );
  304.         // Redirect to the jumpTo page
  305.         if (($objTarget $this->objModel->getRelated('jumpTo')) instanceof PageModel)
  306.         {
  307.             /** @var PageModel $objTarget */
  308.             $this->redirect($objTarget->getFrontendUrl());
  309.         }
  310.         System::getContainer()->get('session')->getFlashBag()->set('nl_confirm'$GLOBALS['TL_LANG']['MSC']['nl_confirm']);
  311.         $this->reload();
  312.     }
  313. }
  314. class_alias(ModuleSubscribe::class, 'ModuleSubscribe');