/** * Processes any queued newsletter mailings. * * @param object $job \Components\Cron\Models\Job * @return boolean */ public function processMailings(\Components\Cron\Models\Job $job) { // load needed libraries require_once PATH_CORE . DS . 'components' . DS . 'com_newsletter' . DS . 'tables' . DS . 'mailing.recipient.php'; require_once PATH_CORE . DS . 'components' . DS . 'com_newsletter' . DS . 'helpers' . DS . 'helper.php'; // needed vars $limit = 25; $processed = array(); // do we have a param defined limit $params = $job->get('params'); if (is_object($params) && $params->get('newsletter_queue_limit')) { $paramDefinedLimit = $params->get('newsletter_queue_limit'); if (is_numeric($paramDefinedLimit) && $paramDefinedLimit > 0 && $paramDefinedLimit < 100) { $limit = $paramDefinedLimit; } } // create needed objects $database = App::get('db'); // get all queued mailing recipients $sql = "SELECT nmr.id AS mailing_recipientid, nm.id AS mailingid, nm.nid AS newsletterid, nm.lid AS mailinglistid, nmr.email, nm.subject, nm.html_body, nm.plain_body, nm.headers, nm.args, nm.tracking\n\t\t\t\tFROM `#__newsletter_mailings` AS nm, `#__newsletter_mailing_recipients` AS nmr\n\t\t\t\tWHERE nm.id=nmr.mid\n\t\t\t\tAND nmr.status='queued'\n\t\t\t\tAND nm.deleted=0\n\t\t\t\tAND UTC_TIMESTAMP() >= nm.date\n\t\t\t\tORDER BY nmr.date_added\n\t\t\t\tLIMIT {$limit}"; $database->setQuery($sql); $queuedEmails = $database->loadObjectList(); // loop through each newsletter recipient, prepare and mail foreach ($queuedEmails as $queuedEmail) { if (in_array($queuedEmail->email, $processed)) { continue; } // get tracking & unsubscribe token $emailToken = \Components\Newsletter\Helpers\Helper::generateMailingToken($queuedEmail); // if tracking is on add it to email if ($queuedEmail->tracking) { $queuedEmail->html_body = \Components\Newsletter\Helpers\Helper::addTrackingToEmailMessage($queuedEmail->html_body, $emailToken); } // create unsubscribe link $unsubscribeMailtoLink = ''; $unsubscribeLink = 'https://' . $_SERVER['SERVER_NAME'] . '/newsletter/unsubscribe?e=' . urlencode($queuedEmail->email) . '&t=' . $emailToken; // add unsubscribe link - placeholder & in header (must do after adding tracking!!) $queuedEmail->html_body = str_replace("{{UNSUBSCRIBE_LINK}}", $unsubscribeLink, $queuedEmail->html_body); $queuedEmail->headers = str_replace("{{UNSUBSCRIBE_LINK}}", $unsubscribeLink, $queuedEmail->headers); $queuedEmail->headers = str_replace("{{UNSUBSCRIBE_MAILTO_LINK}}", $unsubscribeMailtoLink, $queuedEmail->headers); // add mailing id to header $queuedEmail->headers = str_replace("{{CAMPAIGN_MAILING_ID}}", $queuedEmail->mailingid, $queuedEmail->headers); // create new message $message = new \Hubzero\Mail\Message(); // add headers foreach (explode("\r\n", $queuedEmail->headers) as $header) { $parts = array_map("trim", explode(':', $header)); switch ($parts[0]) { case 'From': if (preg_match("/\\\"([^\"]*)\\\"\\s<([^>]*)>/ux", $parts[1], $matches)) { $message->setFrom(array($matches[2] => $matches[1])); } break; case 'Reply-To': if (preg_match("/\\\"([^\"]*)\\\"\\s<([^>]*)>/ux", $parts[1], $matches)) { $message->setReplyTo(array($matches[2] => $matches[1])); } break; case 'Importance': case 'X-Priority': case 'X-MSMail-Priority': $priority = isset($parts[1]) && in_array($parts[1], array(1, 2, 3, 4, 5)) ? $parts[1] : 3; $message->setPriority($priority); break; default: if (isset($parts[1])) { $message->addHeader($parts[0], $parts[1]); } } } // build message object and send $message->setSubject($queuedEmail->subject)->setTo($queuedEmail->email)->setBody($queuedEmail->plain_body, 'text/plain')->addPart($queuedEmail->html_body, 'text/html'); // mail message if ($message->send()) { // add to process email array $processed[] = $queuedEmail->email; // load recipient object $newsletterMailingRecipient = new \Components\Newsletter\Tables\MailingRecipient($database); $newsletterMailingRecipient->load($queuedEmail->mailing_recipientid); // mark as sent and save $newsletterMailingRecipient->status = 'sent'; $newsletterMailingRecipient->date_sent = Date::toSql(); $newsletterMailingRecipient->save($newsletterMailingRecipient); } } return true; }
/** * Send Newsletter * * @param $newsletter * @param $newsletterHtmlContent * @param $newsletterPlainContent * @param $newsletterContacts * @param $newsletterMailinglist * @param $sendingTest * @return object */ private function _send($newsletter, $newsletterHtmlContent, $newsletterPlainContent, $newsletterContacts, $newsletterMailinglist, $sendingTest = false) { //set default mail from and reply-to names and addresses $defaultMailFromName = Config::get("sitename") . ' Newsletter'; $defaultMailFromAddress = 'contact@' . $_SERVER['HTTP_HOST']; $defaultMailReplytoName = Config::get("sitename") . ' Newsletter - Do Not Reply'; $defaultMailReplytoAddress = 'do-not-reply@' . $_SERVER['HTTP_HOST']; //get the config mail from and reply-to names and addresses $mailFromName = $this->config->get('newsletter_from_name', $defaultMailFromName); $mailFromAddress = $this->config->get('newsletter_from_address', $defaultMailFromAddress); $mailReplytoName = $this->config->get('newsletter_replyto_name', $defaultMailReplytoName); $mailReplytoAddress = $this->config->get('newsletter_replyto_address', $defaultMailReplytoAddress); //parse newsletter specific emails $params = new Registry($newsletter->params); $mailFromName = $params->get('from_name', $mailFromName); $mailFromAddress = $params->get('from_address', $mailFromAddress); $mailReplytoName = $params->get('replyto_name', $mailReplytoName); $mailReplytoAddress = $params->get('replyto_address', $mailReplytoAddress); //set final mail from and reply-to $mailFrom = '"' . $mailFromName . '" <' . $mailFromAddress . '>'; $mailReplyTo = '"' . $mailReplytoName . '" <' . $mailReplytoAddress . '>'; //set subject and body $mailSubject = $newsletter->name ? $newsletter->name : 'Your ' . Config::get("sitename") . '.org Newsletter'; $mailHtmlBody = $newsletterHtmlContent; $mailPlainBody = $newsletterPlainContent; //set mail headers //$mailHeaders = "MIME-Version: 1.0" . "\r\n"; //$mailHeaders .= "Content-type: text/html; charset=\"UTF-8\"" . "\r\n"; $mailHeaders = "From: {$mailFrom}" . "\r\n"; $mailHeaders .= "Reply-To: {$mailReplyTo}" . "\r\n"; //set mail priority $mailHeaders .= "X-Priority: 3" . "\r\n"; //$mailHeaders .= "X-MSMail-Priority: Normal" . "\r\n"; //$mailHeaders .= "Importance: Normal\n"; //set extra headers $mailHeaders .= "X-Mailer: PHP/" . phpversion() . "\r\n"; $mailHeaders .= "X-Component: " . $this->_option . "\r\n"; $mailHeaders .= "X-Component-Object: Campaign Mailing" . "\r\n"; $mailHeaders .= "X-Component-ObjectId: {{CAMPAIGN_MAILING_ID}}" . "\r\n"; //$mailHeaders .= "List-Unsubscribe: <mailto:{{UNSUBSCRIBE_MAILTO_LINK}}>, <{{UNSUBSCRIBE_LINK}}>"; //set mail args $mailArgs = ''; //$mailArgs = '-f hubmail-bounces@' . $_SERVER['HTTP_HOST']; //are we sending test mailing if ($sendingTest) { foreach ($newsletterContacts as $contact) { // get tracking & unsubscribe token $recipient = new stdClass(); $recipient->email = $contact; $recipient->mailingid = $newsletterMailinglist ? $newsletterMailinglist : -1; $emailToken = \Components\Newsletter\Helpers\Helper::generateMailingToken($recipient); // create unsubscribe link $unsubscribeMailtoLink = ''; $unsubscribeLink = 'https://' . $_SERVER['SERVER_NAME'] . '/newsletter/unsubscribe?e=' . urlencode($contact) . '&t=' . $emailToken; // add unsubscribe link - placeholder & in header (must do after adding tracking!!) $mailHtmlBody = str_replace("{{UNSUBSCRIBE_LINK}}", $unsubscribeLink, $mailHtmlBody); $mailPlainBody = str_replace("{{UNSUBSCRIBE_LINK}}", $unsubscribeLink, $mailPlainBody); // create new message $message = new \Hubzero\Mail\Message(); foreach (explode("\r\n", $mailHeaders) as $header) { $parts = array_map("trim", explode(':', $header)); switch ($parts[0]) { case 'From': if (preg_match("/\\\"([^\"]*)\\\"\\s<([^>]*)>/ux", $parts[1], $matches)) { $message->setFrom(array($matches[2] => $matches[1])); } break; case 'Reply-To': if (preg_match("/\\\"([^\"]*)\\\"\\s<([^>]*)>/ux", $parts[1], $matches)) { $message->setReplyTo(array($matches[2] => $matches[1])); } break; case 'Importance': case 'X-Priority': case 'X-MSMail-Priority': $priority = isset($parts[1]) && in_array($parts[1], array(1, 2, 3, 4, 5)) ? $parts[1] : 3; $message->setPriority($priority); break; default: if (isset($parts[1])) { $message->addHeader($parts[0], $parts[1]); } } } // build message object and send $message->setSubject('[SENDING TEST] - ' . $mailSubject)->setTo($contact)->addPart($mailHtmlBody, 'text/html')->addPart($mailPlainBody, 'text/plain')->send(); } return true; } //get the scheduling $scheduler = Request::getInt('scheduler', 1); if ($scheduler == '1') { $scheduledDate = Date::toSql(); } else { $schedulerDate = Request::getVar('scheduler_date', ''); $schedulerHour = Request::getVar('scheduler_date_hour', '00'); $schedulerMinute = Request::getVar('scheduler_date_minute', '00'); $schedulerMeridian = Request::getVar('scheduler_date_meridian', 'AM'); //make sure we have at least the date or we use now if (!$schedulerDate) { $scheduledDate = Date::toSql(); } //break apart parts of date $schedulerDateParts = explode('/', $schedulerDate); //make sure its in 24 time if ($schedulerMeridian == 'pm') { $schedulerHour += 12; } //build scheduled time $scheduledTime = $schedulerDateParts[2] . '-' . $schedulerDateParts[0] . '-' . $schedulerDateParts[1]; $scheduledTime .= ' ' . $schedulerHour . ':' . $schedulerMinute . ':00'; $scheduledDate = Date::of(strtotime($scheduledTime))->toSql(); } //create mailing object $mailing = new stdClass(); $mailing->nid = $newsletter->id; $mailing->lid = $newsletterMailinglist; $mailing->subject = $mailSubject; $mailing->html_body = $mailHtmlBody; $mailing->plain_body = $mailPlainBody; $mailing->headers = $mailHeaders; $mailing->args = $mailArgs; $mailing->tracking = $newsletter->tracking; $mailing->date = $scheduledDate; //save mailing object $newsletterMailing = new Mailing($this->database); if (!$newsletterMailing->save($mailing)) { $this->setError(Lang::txt('COM_NEWSLETTER_NEWSLETTER_SEND_FAIL')); $this->sendNewsletterTask(); return; } // create recipients $this->_sendTo($newsletterMailing, $newsletterContacts); return $newsletterMailing; }
/** * Processes any queued newsletter mailings. * * @param object $job \Components\Cron\Models\Job * @return boolean */ public function processMailings(\Components\Cron\Models\Job $job) { // load needed libraries require_once PATH_CORE . DS . 'components' . DS . 'com_newsletter' . DS . 'tables' . DS . 'mailing.recipient.php'; require_once PATH_CORE . DS . 'components' . DS . 'com_newsletter' . DS . 'tables' . DS . 'mailing.php'; require_once PATH_CORE . DS . 'components' . DS . 'com_newsletter' . DS . 'tables' . DS . 'mailinglist.php'; require_once PATH_CORE . DS . 'components' . DS . 'com_newsletter' . DS . 'helpers' . DS . 'helper.php'; // needed vars $limit = 25; $processed = array(); // do we have a param defined limit $params = $job->params; if (is_object($params) && $params->get('newsletter_queue_limit')) { $paramDefinedLimit = $params->get('newsletter_queue_limit'); if (is_numeric($paramDefinedLimit) && $paramDefinedLimit > 0 && $paramDefinedLimit < 100) { $limit = $paramDefinedLimit; } } // create needed objects $database = App::get('db'); // get all queued mailing recipients $sql = "SELECT nmr.id AS mailing_recipientid, nm.id AS mailingid, nm.nid AS newsletterid, nm.lid AS mailinglistid, nmr.email, nm.subject, nm.html_body, nm.plain_body, nm.headers, nm.args, nm.tracking, nl.autogen, nm.date AS sendDate\n\t\t\t\tFROM `#__newsletter_mailings` AS nm, `#__newsletter_mailing_recipients` AS nmr, `#__newsletters` AS nl\n\t\t\t\tWHERE nm.id=nmr.mid\n\t\t\t\tAND nm.nid=nl.id\n\t\t\t\tAND nmr.status='queued'\n\t\t\t\tAND nm.deleted=0\n\t\t\t\tAND UTC_TIMESTAMP() >= nm.date\n\t\t\t\tORDER BY nmr.date_added\n\t\t\t\tLIMIT {$limit}"; $database->setQuery($sql); $queuedEmails = $database->loadObjectList(); // Get newsletter, check whether it is autogen // loop through each newsletter recipient, prepare and mail foreach ($queuedEmails as $queuedEmail) { if (in_array($queuedEmail->email, $processed)) { continue; } // get tracking & unsubscribe token $emailToken = \Components\Newsletter\Helpers\Helper::generateMailingToken($queuedEmail); // if tracking is on add it to email if ($queuedEmail->tracking) { $queuedEmail->html_body = \Components\Newsletter\Helpers\Helper::addTrackingToEmailMessage($queuedEmail->html_body, $emailToken); } // create unsubscribe link $unsubscribeMailtoLink = ''; $unsubscribeLink = 'https://' . $_SERVER['SERVER_NAME'] . '/newsletter/unsubscribe?e=' . urlencode($queuedEmail->email) . '&t=' . $emailToken; // add unsubscribe link - placeholder & in header (must do after adding tracking!!) $queuedEmail->html_body = str_replace("{{UNSUBSCRIBE_LINK}}", $unsubscribeLink, $queuedEmail->html_body); $queuedEmail->headers = str_replace("{{UNSUBSCRIBE_LINK}}", $unsubscribeLink, $queuedEmail->headers); $queuedEmail->headers = str_replace("{{UNSUBSCRIBE_MAILTO_LINK}}", $unsubscribeMailtoLink, $queuedEmail->headers); // add mailing id to header $queuedEmail->headers = str_replace("{{CAMPAIGN_MAILING_ID}}", $queuedEmail->mailingid, $queuedEmail->headers); // create new message $message = new \Hubzero\Mail\Message(); // add headers foreach (explode("\r\n", $queuedEmail->headers) as $header) { $parts = array_map("trim", explode(':', $header)); switch ($parts[0]) { case 'From': if (preg_match("/\\\"([^\"]*)\\\"\\s<([^>]*)>/ux", $parts[1], $matches)) { $message->setFrom(array($matches[2] => $matches[1])); } break; case 'Reply-To': if (preg_match("/\\\"([^\"]*)\\\"\\s<([^>]*)>/ux", $parts[1], $matches)) { $message->setReplyTo(array($matches[2] => $matches[1])); } break; case 'Importance': case 'X-Priority': case 'X-MSMail-Priority': $priority = isset($parts[1]) && in_array($parts[1], array(1, 2, 3, 4, 5)) ? $parts[1] : 3; $message->setPriority($priority); break; default: if (isset($parts[1])) { $message->addHeader($parts[0], $parts[1]); } } } // build message object and send $message->setSubject($queuedEmail->subject)->setTo($queuedEmail->email)->setBody($queuedEmail->plain_body, 'text/plain')->addPart($queuedEmail->html_body, 'text/html'); // mail message if ($message->send()) { // add to process email array $processed[] = $queuedEmail->email; // Some trickery for supporting rescheduled emails if ($queuedEmail->autogen == 0 || $queuedEmail->autogen == null) { // load recipient object $newsletterMailingRecipient = new \Components\Newsletter\Tables\MailingRecipient($database); $newsletterMailingRecipient->load($queuedEmail->mailing_recipientid); // mark as sent and save $newsletterMailingRecipient->status = 'sent'; $newsletterMailingRecipient->date_sent = Date::toSql(); $newsletterMailingRecipient->save($newsletterMailingRecipient); } else { $sql = "SELECT *, max(date) AS maxDate FROM #__newsletter_mailings WHERE nid = {$queuedEmail->newsletterid} AND deleted=0;"; $database->setQuery($sql); $latestMailing = $database->loadObject(); switch ($queuedEmail->autogen) { case 1: $lookahead = ' +1 day'; break; case 2: $lookahead = ' +1 week'; break; case 3: $lookahead = ' +1 month'; break; } $nextDate = Date::of(strtotime($latestMailing->maxDate . $lookahead))->toLocal(); $windowMin = strtotime(Date::of(strtotime($nextDate))->toLocal("Y-m-d")); $windowMax = strtotime(Date::of(strtotime($lookahead))->toLocal("Y-m-d")); // If there is no mailing set for the next interval, create it. if ($windowMax - $windowMin == 0) { // Create mailing $newMailing = new Components\Newsletter\Tables\Mailing($database); $newMailing->bind($latestMailing); $newMailing->id = null; $newMailing->date = $nextDate; $newMailing->save($newMailing); // Add recipients $mailingList = new Components\Newsletter\Tables\MailingList($database); $emails = $mailingList->getListEmails($newMailing->lid); // @TODO Verify there is no helper method to determine whether or not to send email foreach ($emails as $email) { if ($email->status == 'active') { $values[] = "(" . $database->quote($newMailing->id) . "," . $database->quote($email->email) . ",'queued', " . $database->quote(Date::of(time())->toLocal()) . ")"; } } // make sure we have some values if (count($values) > 0) { // build full query & execute $sql = "INSERT INTO `#__newsletter_mailing_recipients` (`mid`,`email`,`status`,`date_added`) VALUES " . implode(',', $values); $database->setQuery($sql); $database->query(); } } // End interval creation // load recipient object $newsletterMailingRecipient = new \Components\Newsletter\Tables\MailingRecipient($database); $newsletterMailingRecipient->load($queuedEmail->mailing_recipientid); // mark as sent and save $newsletterMailingRecipient->status = 'sent'; $newsletterMailingRecipient->date_sent = Date::toSql(); $newsletterMailingRecipient->save($newsletterMailingRecipient); } // end autogen logic } } return true; }