* @copyright Since 2013 Ovidiu Cimpean * @license Do not edit, modify or copy this file * * @version Release: 4 */ class NewsletterProTask extends ObjectModel { public $id_newsletter_pro_smtp; public $id_newsletter_pro_tpl_history; public $date_start; public $date_modified; public $active; public $template; public $send_method; public $started; public $status; public $sleep; public $pause; public $emails_count; public $emails_error; public $emails_success; public $emails_completed; public $done; public $error_msg; /** * variables. */ private $log; private $num_sent = 0; /** * This variable is used into the function send proccess. * * @var object */ private $task_step; /** * 24000. */ const MAX_EXECUTION_TIME = 24000; const STATUS_DEFAULT = 0; const WRITE_LOG = 0; const STATUS_IN_PROGRESS = 1; public static $definition = [ 'table' => 'newsletter_pro_task', 'primary' => 'id_newsletter_pro_task', 'fields' => [ 'id_newsletter_pro_smtp' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true], 'id_newsletter_pro_tpl_history' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true], 'date_start' => ['type' => self::TYPE_DATE, 'validate' => 'isDateFormat'], 'date_modified' => ['type' => self::TYPE_DATE, 'validate' => 'isDateFormat'], 'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'template' => ['type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true], 'send_method' => ['type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true], 'started' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'status' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'sleep' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], 'pause' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'emails_count' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], 'emails_error' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], 'emails_success' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], 'emails_completed' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], 'done' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'error_msg' => ['type' => self::TYPE_HTML, 'validate' => 'isString'], ], ]; public function __construct($id = null) { // set defaults values $this->active = 1; $this->error_msg = serialize([]); $this->log = []; $this->one_mb = 1048576; $this->memory_limit = (int) ini_get('memory_limit') * $this->one_mb; $current_mem = memory_get_usage(true); if ((int) ini_get('memory_limit') <= 0) { $this->memory_limit = $current_mem + $this->one_mb * 24 + 1; } parent::__construct($id); if (Validate::isLoadedObject($this)) { $this->log(sprintf(NewsletterPro::getInstance()->l('Task id : %s'), $this->id)); $this->log(sprintf(NewsletterPro::getInstance()->l('Send method : %s'), $this->send_method)); $this->log(sprintf(NewsletterPro::getInstance()->l('Total emails : %s'), $this->emails_count)); $this->log(sprintf(NewsletterPro::getInstance()->l('Sent success : %s'), $this->emails_success)); $this->log(sprintf(NewsletterPro::getInstance()->l('Sent errors : %s'), $this->emails_error)); $this->log(sprintf(NewsletterPro::getInstance()->l('Sent completed : %s')."\n", $this->emails_completed)); } else { $this->log(NewsletterPro::getInstance()->l('No task has been loaded.')); } } public static function newInstance($id = null) { return new self($id); } public function add($autodate = true, $null_values = false) { return parent::add($autodate, $null_values); } public function update($null_values = false) { $this->date_modified = date('Y-m-d H:i:s'); return parent::update($null_values); } public function updateFields($fields = []) { $fields_default = [ 'date_modified' => pSQL(date('Y-m-d H:i:s')), ]; return Db::getInstance()->update('newsletter_pro_task', array_merge($fields_default, $fields), '`id_newsletter_pro_task` = '.(int) $this->id); } public function delete() { $evaluate = NewsletterProEvaluate::newInstance(); foreach ($this->getStepsIds() as $id) { $task_step = NewsletterProTaskStep::newInstance($id); $evaluate->add($task_step->delete()); } return parent::delete() && $evaluate->success(); } public function log($value) { $this->log[] = $value; } public function displayLog($separator = "\n") { echo '
';
        echo join($separator, $this->log).$separator;
        echo '
'; return $this; } public function uniqueLog() { $this->log = array_unique($this->log); return $this; } public function emptyLog() { $this->log = []; return $this; } public function getStepsIds($step_active = false, $limit = 0) { $results = Db::getInstance()->executeS(' SELECT `id_newsletter_pro_task_step` FROM `'._DB_PREFIX_.'newsletter_pro_task_step` WHERE `id_newsletter_pro_task` = '.(int) $this->id.' '.($step_active ? ' AND `step_active` = 1' : '').' ORDER BY `id_newsletter_pro_task_step` '.($limit > 0 ? ' LIMIT '.(int) $limit : '').' '); return array_map([$this, 'getStepsIdsCallback'], $results); } private function getStepsIdsCallback($row) { return !empty($row) ? $row['id_newsletter_pro_task_step'] : false; } /** * @param DateTime|string|null $dateTime * * @return NewsletterProTask|false */ public static function getTask($dateTime = null) { if (is_null($dateTime)) { $dateTime = new DateTime('now'); } else { if (is_string($dateTime)) { $dateTime = new DateTime($dateTime); } elseif (!($dateTime instanceof DateTime)) { throw new Exception('Invalid task date.'); } } $id = (int) Db::getInstance()->getValue(' SELECT `id_newsletter_pro_task` FROM `'._DB_PREFIX_.'newsletter_pro_task` WHERE `date_start` <= "'.pSQL($dateTime->format('Y-m-d H:i:s')).'" AND ( DATE(`date_start`) = "'.pSQL($dateTime->format('Y-m-d')).'" OR `started` = 1 ) AND `active` = 1 AND `done` = 0 ORDER BY `date_start` ASC; '); $task = new self($id); if (Validate::isLoadedObject($task)) { return $task; } return false; } /** * Sent the task. * * @return int Number of newsletters sent */ public function send() { if (0 == (int) $this->started) { $this->started = 1; $this->update(); } ignore_user_abort(true); set_time_limit(0); @ini_set('max_execution_time', self::MAX_EXECUTION_TIME); register_shutdown_function([$this, 'sendShutdownFunctionCallback']); $tlog = _NEWSLETTER_PRO_DIR_.'/logs/task.log'; // update task status $this->status = self::STATUS_IN_PROGRESS; $this->update(); $this->task_step = $this->getCurrentStep(); if ($this->task_step) { $emails_to_send = $this->task_step->getEmailsToSend(); $emails_sent = $this->task_step->getEmailsSent(); foreach ($emails_to_send as $index => $email) { if ((bool) pqnp_config('TASK_MEMORY_CHECK_ENABLED')) { $current_mem = memory_get_usage(true); if ($current_mem + $this->one_mb * 24 >= $this->memory_limit) { exit('Exit during the memory limt. If the tasks don\'t start sending try to put this command "configuration -set TASK_MEMORY_CHECK_ENABLED 0" into the Terminal tab and press enter.'); exit; } } if (!$this->taskExists()) { exit; } if ($this->isTaskPaused()) { exit; } $template = NewsletterProTemplate::newHistory($this->id_newsletter_pro_tpl_history, $email)->load(); if ($template->user) { // exclude emails $exclude_email = false; $to_info = NewsletterProMail::getEmailInfo($template->user->to()); $to_email = $to_info['email']; if (NewsletterProEmailExclusion::newInstance()->emailExists($to_email)) { $exclude_email = true; } // build forwarders $forward = NewsletterProSendManager::buildForward($this->id_newsletter_pro_tpl_history, 'history', $template->user->to()); $message = $template->message(); $title = $message['title']; $body = $message['body']; $send_manager = NewsletterProSendManager::getInstance(); $send_manager->setTemplateNameForAttachment($template->name); try { $send = $send_manager->sendNewsletter($title, $body, $template->user->to(), [ 'user' => $template->user, 'id_smtp' => (int) $this->id_newsletter_pro_smtp, 'send_method' => $this->send_method, ], $forward, false, $exclude_email); } catch (Exception $e) { $send = [$e->getMessage()]; // $send = false; } if (!is_array($send) && true == $send) { $emails_sent[] = [ 'email' => $template->user->email, 'status' => true, ]; ++$this->emails_success; ++$this->num_sent; $this->log(NewsletterPro::getInstance()->l('Success').' : '.(string) $template->user->email); if (self::WRITE_LOG) { $msgSucc = NewsletterPro::getInstance()->l('Success').' : '.(string) $template->user->email; if ($h = fopen($tlog, 'a+')) { fwrite($h, date('Y-m-d H:i:s').' '.$msgSucc."\n"); fclose($h); } } } else { $emails_sent[] = [ 'email' => $template->user->email, 'status' => false, ]; ++$this->emails_error; $this->appendError([ 'smtp' => join('
', $send), ]); $this->log(NewsletterPro::getInstance()->l('Error').' : '.join("\n", $send).' : '.(string) $template->user->email); if (self::WRITE_LOG) { $msgError = NewsletterPro::getInstance()->l('Error').' : '.join("\n", $send).' : '.(string) $template->user->email; if ($h = fopen($tlog, 'a+')) { fwrite($h, date('Y-m-d H:i:s').' '.$msgError."\n"); fclose($h); } } } unset($emails_to_send[$index]); $this->task_step->setEmailsToSend($emails_to_send); $this->task_step->setEmailsSent($emails_sent); $this->task_step->step_active = (count($emails_to_send) > 0 ? 1 : 0); if (!$exclude_email && (int) $this->sleep > 0) { sleep((int) $this->sleep); } } else { $this->appendError([ 'email' => pSQL(sprintf(NewsletterPro::getInstance()->l('The email %s does not exists in the database.'), $email)), ]); } ++$this->emails_completed; if (0 == $this->emails_completed % (int) NewsletterPro::getInstance()->ini_config['task_write_db_limit']) { $this->task_step->update(); $this->updateFields([ 'emails_completed' => (int) $this->emails_completed, 'emails_success' => (int) $this->emails_success, 'emails_error' => (int) $this->emails_error, ]); } } // end the script if it's the case if ($this->emails_completed > $this->emails_count) { return $this->endSend(); } // update when the step is done $this->task_step->update(); $this->updateFields([ 'emails_completed' => (int) $this->emails_completed, 'emails_success' => (int) $this->emails_success, 'emails_error' => (int) $this->emails_error, ]); // get next step emails return $this->send(); } return $this->endSend(); } private function endSend() { $this->done = 1; $this->update(); return $this->num_sent; } public function sendShutdownFunctionCallback() { $ob_content = ob_get_contents(); @ob_end_clean(); if (preg_match('/Fatal error/', $ob_content) && preg_match('/Maximum execution time/', $ob_content)) { $this->displayLog()->emptyLog(); echo '
';
            echo "\n";
            echo NewsletterPro::getInstance()->l('PHP max execution time exceeded. Access the script again to continue the sending process.');
            echo '
'; } else { echo $ob_content; } if (Validate::isLoadedObject($this)) { $this->status = self::STATUS_DEFAULT; $this->update(); } if (isset($this->task_step) && Validate::isLoadedObject($this->task_step)) { $this->task_step->update(); } } public function sendTaskAjax() { $errors = []; if (NewsletterProTask::taskInProgress()) { $errors[] = NewsletterPro::getInstance()->l('Cannot send multiple tasks in the same time.'); } @ob_end_clean(); @ob_start(); echo Tools::jsonEncode([ 'status' => empty($errors), 'errors' => $errors, ]); $size = ob_get_length(); header("Content-Length: $size"); header('Connection: close'); @ob_end_flush(); @ob_flush(); flush(); if (session_id()) { session_write_close(); } if (empty($errors)) { // run in background $this->send(); } } public function isInProgress() { return $this->status; } /** * The the current task step or the next step if the current step does not have emails addresses. * * @return object/boolean */ public function getCurrentStep() { $steps_id = $this->getStepsIds(true, 1); if ($steps_id) { $task_step = NewsletterProTaskStep::newInstance($steps_id[0]); $emails_to_send = $task_step->getEmailsToSend(); if (empty($emails_to_send)) { $task_step->step_active = 0; // if there are no step emails, write step_active = 0 if ($task_step->update()) { return $this->getCurrentStep(); } } else { return $task_step; } } else { // if there are no steps, siable the task $this->activ = 0; $this->update(); } return false; } /** * Task exist. * * @param int $id * * @return int */ public function taskExists() { return Db::getInstance()->getValue( ' SELECT count(*) FROM `'._DB_PREFIX_.'newsletter_pro_task` WHERE `id_newsletter_pro_task`='.(int) $this->id ); } /** * Is task paused. * * @param int $id * * @return bool */ public function isTaskPaused() { $this->pause = (int) Db::getInstance()->getValue( ' SELECT `pause` FROM `'._DB_PREFIX_.'newsletter_pro_task` WHERE `id_newsletter_pro_task`='.(int) $this->id ); return $this->pause; } private function appendError($error_msg) { $error_msg_db = NewsletterProTools::unSerialize($this->error_msg); $error_msg_write = array_merge($error_msg_db, $error_msg); $this->error_msg = serialize($error_msg_write); return $this->update(); } /** * Task in progress. * * @return bool */ public static function taskInProgress() { return Db::getInstance()->getValue(' SELECT count(*) FROM `'._DB_PREFIX_.'newsletter_pro_task` WHERE `status` = '.(int) self::STATUS_IN_PROGRESS.' AND `done` = 0 ') ? true : false; } public static function getTaskInProgress() { $task_id = (int) Db::getInstance()->getValue(' SELECT `id_newsletter_pro_task` FROM `'._DB_PREFIX_.'newsletter_pro_task` WHERE `status` = '.(int) self::STATUS_IN_PROGRESS.' AND `done` = 0 '); $task = NewsletterProTask::newInstance($task_id); if (Validate::isLoadedObject($task)) { if ($task->getCurrentStep()) { return $task; } } return false; } public function pauseTask() { return (int) Db::getInstance()->update('newsletter_pro_task', [ 'pause' => 1, ], '`id_newsletter_pro_task` = '.(int) $this->id, 1); } public static function exportPrivacy($email) { $response = new NewsletterProPrivacyDataResponse(NewsletterProPrivacyDataResponse::TYPE_EXPORT, 'newsletter_pro_task', $email); try { // nothing to export } catch (Exception $e) { $response->addException($e); } return $response; } public static function privacySerach($email) { $response = new NewsletterProPrivacyDataResponse(NewsletterProPrivacyDataResponse::TYPE_SEARCH, 'newsletter_pro_task', $email); try { $count = (int) Db::getInstance()->getValue(' SELECT COUNT(*) FROM `'._DB_PREFIX_.'newsletter_pro_task` WHERE `error_msg` REGEXP "'.pSQL(preg_quote($email)).'[^A-Za-z0-9]" '); $response->addToCount($count); } catch (Exception $e) { $response->addException($e); } return $response; } public static function clearPrivacy($email) { $response = new NewsletterProPrivacyDataResponse(NewsletterProPrivacyDataResponse::TYPE_CLEAR, 'newsletter_pro_task', $email); try { if (Db::getInstance()->update('newsletter_pro_task', [ 'error_msg' => serialize([]), ], '`error_msg` REGEXP "'.pSQL(preg_quote($email)).'[^A-Za-z0-9]"')) { $response->addToCount(Db::getInstance()->Affected_Rows()); } } catch (Exception $e) { $response->addException($e); } return $response; } }