Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/subscribe/classes/general/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/subscribe/classes/general/posting.php |
<?php IncludeModuleLangFile(__FILE__); use Bitrix\Main\Mail; class CPosting { public $LAST_ERROR = ''; //email count for one hit public static $current_emails_per_hit = 0; //get by ID public static function GetByID($ID) { global $DB; $ID = intval($ID); $strSql = ' SELECT P.* ,' . $DB->DateToCharFunction('P.TIMESTAMP_X', 'FULL') . ' AS TIMESTAMP_X ,' . $DB->DateToCharFunction('P.DATE_SENT', 'FULL') . ' AS DATE_SENT ,' . $DB->DateToCharFunction('P.AUTO_SEND_TIME', 'FULL') . ' AS AUTO_SEND_TIME FROM b_posting P WHERE P.ID=' . $ID . ' '; return $DB->Query($strSql, false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); } //list of categories linked with message public static function GetRubricList($ID) { global $DB; $ID = intval($ID); $strSql = ' SELECT R.ID ,R.NAME ,R.SORT ,R.LID ,R.ACTIVE FROM b_list_rubric R ,b_posting_rubric PR WHERE R.ID=PR.LIST_RUBRIC_ID AND PR.POSTING_ID=' . $ID . ' ORDER BY R.LID, R.SORT, R.NAME '; return $DB->Query($strSql, false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); } //list of user group linked with message public static function GetGroupList($ID) { global $DB; $ID = intval($ID); $strSql = ' SELECT G.ID ,G.NAME FROM b_group G ,b_posting_group PG WHERE G.ID=PG.GROUP_ID AND PG.POSTING_ID=' . $ID . ' ORDER BY G.C_SORT, G.ID '; return $DB->Query($strSql, false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); } // delete by ID public static function Delete($ID) { global $DB; $ID = intval($ID); CPosting::DeleteFile($ID); $res = $DB->Query("DELETE FROM b_posting_rubric WHERE POSTING_ID='" . $ID . "'", false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); if ($res) { $res = $DB->Query("DELETE FROM b_posting_group WHERE POSTING_ID='" . $ID . "' ", false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); } if ($res) { $res = $DB->Query("DELETE FROM b_posting_email WHERE POSTING_ID='" . $ID . "' ", false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); } if ($res) { $res = $DB->Query("DELETE FROM b_posting WHERE ID='" . $ID . "' ", false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); } return $res; } public static function OnGroupDelete($group_id) { global $DB; $group_id = intval($group_id); return $DB->Query('DELETE FROM b_posting_group WHERE GROUP_ID=' . $group_id, true); } public static function DeleteFile($ID, $file_id=false) { global $DB; $rsFile = CPosting::GetFileList($ID, $file_id); while ($arFile = $rsFile->Fetch()) { $DB->Query('DELETE FROM b_posting_file where POSTING_ID=' . intval($ID) . ' AND FILE_ID=' . intval($arFile['ID']), false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); CFile::Delete(intval($arFile['ID'])); } } public static function SplitFileName($file_name) { $found = []; // exapmle(2).txt if (preg_match('/^(.*)\\((\\d+?)\\)(\\..+?)$/', $file_name, $found)) { $fname = $found[1]; $fext = $found[3]; $index = $found[2]; } // example(2) elseif (preg_match('/^(.*)\\((\\d+?)\\)$/', $file_name, $found)) { $fname = $found[1]; $fext = ''; $index = $found[2]; } // example.txt elseif (preg_match('/^(.*)(\\..+?)$/', $file_name, $found)) { $fname = $found[1]; $fext = $found[2]; $index = 0; } // example else { $fname = $file_name; $fext = ''; $index = 0; } return [$fname, $fext, $index]; } public function SaveFile($ID, $file) { global $DB, $APPLICATION; $ID = intval($ID); $filesSize = 0; $arFileName = CPosting::SplitFileName($file['name']); //Check if file with this name already exists $arSameNames = []; $rsFile = CPosting::GetFileList($ID); while ($arFile = $rsFile->Fetch()) { $filesSize += $arFile['FILE_SIZE']; $arSavedName = CPosting::SplitFileName($arFile['ORIGINAL_NAME']); if ($arFileName[0] == $arSavedName[0] && $arFileName[1] == $arSavedName[1]) { $arSameNames[$arSavedName[2]] = true; } } $max_files_size = COption::GetOptionString('subscribe', 'max_files_size') * 1024 * 1024; if ($max_files_size > 0) { $filesSize += $file['size']; if ($filesSize > $max_files_size) { $this->LAST_ERROR = GetMessage('class_post_err_files_size', [ '#MAX_FILES_SIZE#' => CFile::FormatSize($max_files_size), ]); $APPLICATION->ThrowException($this->LAST_ERROR); return false; } } while (array_key_exists($arFileName[2], $arSameNames)) { $arFileName[2]++; } if ($arFileName[2] > 0) { $file['name'] = $arFileName[0] . '(' . ($arFileName[2]) . ')' . $arFileName[1]; } //save file $file['MODULE_ID'] = 'subscribe'; $fid = intval(CFile::SaveFile($file, 'subscribe', true, true)); if (($fid > 0) && $DB->Query('INSERT INTO b_posting_file (POSTING_ID, FILE_ID) VALUES (' . $ID . ' ,' . $fid . ')', false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__)) { return true; } else { $this->LAST_ERROR = GetMessage('class_post_err_att'); $APPLICATION->ThrowException($this->LAST_ERROR); return false; } } public static function GetFileList($ID, $file_id=false) { global $DB; $ID = intval($ID); $file_id = intval($file_id); $strSql = ' SELECT F.ID ,F.FILE_SIZE ,F.ORIGINAL_NAME ,F.SUBDIR ,F.FILE_NAME ,F.CONTENT_TYPE ,F.HANDLER_ID FROM b_file F ,b_posting_file PF WHERE F.ID=PF.FILE_ID AND PF.POSTING_ID=' . $ID . ' ' . ($file_id > 0 ? 'AND PF.FILE_ID = ' . $file_id : '') . ' ORDER BY F.ID '; return $DB->Query($strSql, false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); } //check fields before writing public function CheckFields($arFields, $ID) { /** @var CDatabase $DB */ global $DB; /** @var CMain $APPLICATION */ global $APPLICATION; $this->LAST_ERROR = ''; $aMsg = []; if (array_key_exists('FROM_FIELD', $arFields)) { if (mb_strlen($arFields['FROM_FIELD']) < 3 || !check_email($arFields['FROM_FIELD'])) { $aMsg[] = ['id' => 'FROM_FIELD', 'text' => GetMessage('class_post_err_email')]; } } if (!array_key_exists('DIRECT_SEND', $arFields) || $arFields['DIRECT_SEND'] == 'N') { if (array_key_exists('TO_FIELD', $arFields) && $arFields['TO_FIELD'] == '') { $aMsg[] = ['id' => 'TO_FIELD', 'text' => GetMessage('class_post_err_to')]; } } if (array_key_exists('SUBJECT', $arFields)) { if ($arFields['SUBJECT'] == '') { $aMsg[] = ['id' => 'SUBJECT', 'text' => GetMessage('class_post_err_subj')]; } } if (array_key_exists('BODY', $arFields)) { if ($arFields['BODY'] == '') { $aMsg[] = ['id' => 'BODY', 'text' => GetMessage('class_post_err_text')]; } } if (array_key_exists('AUTO_SEND_TIME', $arFields) && $arFields['AUTO_SEND_TIME'] !== false) { if ($DB->IsDate($arFields['AUTO_SEND_TIME'], false, false, 'FULL') !== true) { $aMsg[] = ['id' => 'AUTO_SEND_TIME', 'text' => GetMessage('class_post_err_auto_time')]; } } if (array_key_exists('CHARSET', $arFields)) { $sCharset = COption::GetOptionString('subscribe', 'posting_charset'); $aCharset = explode(',', ToLower($sCharset)); if (!in_array(ToLower($arFields['CHARSET']), $aCharset, true)) { $aMsg[] = ['id' => 'CHARSET', 'text' => GetMessage('class_post_err_charset')]; } } if (!empty($aMsg)) { $e = new CAdminException($aMsg); $APPLICATION->ThrowException($e); $this->LAST_ERROR = $e->GetString(); return false; } return true; } //relation with categories public function UpdateRubrics($ID, $aRubric) { global $DB; $ID = intval($ID); $DB->Query('DELETE FROM b_posting_rubric WHERE POSTING_ID=' . $ID, false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); $arID = []; if (is_array($aRubric)) { foreach ($aRubric as $i) { $arID[] = intval($i); } } if (count($arID) > 0) { $DB->Query(' INSERT INTO b_posting_rubric (POSTING_ID, LIST_RUBRIC_ID) SELECT ' . $ID . ', ID FROM b_list_rubric WHERE ID IN (' . implode(', ',$arID) . ') ', false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__ ); } } //relation with user groups public function UpdateGroups($ID, $aGroup) { global $DB; $ID = intval($ID); $DB->Query("DELETE FROM b_posting_group WHERE POSTING_ID='" . $ID . "'", false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); $arID = []; if (is_array($aGroup)) { foreach ($aGroup as $i) { $arID[] = intval($i); } } if (count($arID) > 0) { $DB->Query(' INSERT INTO b_posting_group (POSTING_ID, GROUP_ID) SELECT ' . $ID . ', ID FROM b_group WHERE ID IN (' . implode(', ',$arID) . ') ', false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__ ); } } //Addition public function Add($arFields) { global $DB; if (!$this->CheckFields($arFields, 0)) { return false; } if (!array_key_exists('MSG_CHARSET', $arFields)) { $arFields['MSG_CHARSET'] = LANG_CHARSET; } $arFields['VERSION'] = '2'; $arFields['~TIMESTAMP_X'] = $DB->CurrentTimeFunction(); $ID = $DB->Add('b_posting', $arFields, ['BCC_FIELD','BODY']); if ($ID > 0) { $this->UpdateRubrics($ID, $arFields['RUB_ID']); $this->UpdateGroups($ID, $arFields['GROUP_ID']); } return $ID; } //Update public function Update($ID, $arFields) { global $DB; $ID = intval($ID); if (!$this->CheckFields($arFields, $ID)) { return false; } $arFields['~TIMESTAMP_X'] = $DB->CurrentTimeFunction(); $strUpdate = $DB->PrepareUpdate('b_posting', $arFields); if ($strUpdate != '') { $strSql = 'UPDATE b_posting SET ' . $strUpdate . ' WHERE ID=' . $ID; $arBinds = [ 'BCC_FIELD' => $arFields['BCC_FIELD'], //"SENT_BCC" => $arFields["SENT_BCC"], 'BODY' => $arFields['BODY'], //"ERROR_EMAIL" => $arFields["ERROR_EMAIL"], //"BCC_TO_SEND" => $arFields["BCC_TO_SEND"], ]; if (!$DB->QueryBind($strSql, $arBinds)) { return false; } } if (is_set($arFields, 'RUB_ID')) { $this->UpdateRubrics($ID, $arFields['RUB_ID']); } if (is_set($arFields, 'GROUP_ID')) { $this->UpdateGroups($ID, $arFields['GROUP_ID']); } return true; } public function GetEmails($post_arr) { $aEmail = []; //send to categories $aPostRub = []; $post_rub = static::GetRubricList($post_arr['ID']); while ($post_rub_arr = $post_rub->Fetch()) { $aPostRub[] = $post_rub_arr['ID']; } $subscr = CSubscription::GetList( ['ID' => 'ASC'], [ 'RUBRIC_MULTI' => $aPostRub, 'CONFIRMED' => 'Y', 'ACTIVE' => 'Y', 'FORMAT' => $post_arr['SUBSCR_FORMAT'], 'EMAIL' => $post_arr['EMAIL_FILTER'], ] ); while (($subscr_arr = $subscr->Fetch())) { $aEmail[] = $subscr_arr['EMAIL']; } //send to user groups $aPostGrp = []; $post_grp = static::GetGroupList($post_arr['ID']); while ($post_grp_arr = $post_grp->Fetch()) { $aPostGrp[] = $post_grp_arr['ID']; } if (count($aPostGrp) > 0) { $user = CUser::GetList( 'id', 'asc', ['GROUP_MULTI' => $aPostGrp, 'ACTIVE' => 'Y', 'EMAIL' => $post_arr['EMAIL_FILTER']] ); while (($user_arr = $user->Fetch())) { $aEmail[] = $user_arr['EMAIL']; } } //from additional emails $BCC = $post_arr['BCC_FIELD']; if ($post_arr['DIRECT_SEND'] == 'Y') { $BCC .= ($BCC ? ',' : '') . $post_arr['TO_FIELD']; } if ($BCC <> '') { $BCC = str_replace("\r\n", "\n", $BCC); $BCC = str_replace("\n", ',', $BCC); $aBcc = explode(',', $BCC); foreach ($aBcc as $bccEmail) { $bccEmail = trim($bccEmail); if ($bccEmail !== '') { $aEmail[] = $bccEmail; } } } $aEmail = array_unique($aEmail); return $aEmail; } public static function AutoSend($ID=false, $limit=false, $site_id=false) { if ($ID === false) { //Here is cron job entry $cPosting = new CPosting; $rsPosts = $cPosting->GetList( ['AUTO_SEND_TIME' => 'ASC', 'ID' => 'ASC'], ['STATUS_ID' => 'P', 'AUTO_SEND_TIME_2' => ConvertTimeStamp(false, 'FULL')] ); while ($arPosts = $rsPosts->Fetch()) { if ($limit === true) { $maxcount = COption::GetOptionInt('subscribe', 'subscribe_max_emails_per_hit') - self::$current_emails_per_hit; if ($maxcount <= 0) { break; } } else { $maxcount = 0; } $cPosting->SendMessage($arPosts['ID'], 0, $maxcount); } } else { if ($site_id && $site_id != SITE_ID) { return 'CPosting::AutoSend(' . $ID . ($limit ? ',true' : ',false') . ',"' . $site_id . '");'; } //Here is agent entry if ($limit === true) { $maxcount = COption::GetOptionInt('subscribe', 'subscribe_max_emails_per_hit') - self::$current_emails_per_hit; if ($maxcount <= 0) { return 'CPosting::AutoSend(' . $ID . ',true' . ($site_id ? ',"' . $site_id . '"' : '') . ');'; } } else { $maxcount = 0; } $cPosting = new CPosting; $res = $cPosting->SendMessage($ID, COption::GetOptionString('subscribe', 'posting_interval'), $maxcount, true); if ($res == 'CONTINUE') { return 'CPosting::AutoSend(' . $ID . ($limit ? ',true' : ',false') . ($site_id ? ',"' . $site_id . '"' : '') . ');'; } } return ''; } //Send message public function SendMessage($ID, $timeout=0, $maxcount=0, $check_charset=false) { global $DB, $APPLICATION; $eol = \Bitrix\Main\Mail\Mail::getMailEol(); $ID = intval($ID); $timeout = intval($timeout); $start_time = microtime(1); @set_time_limit(0); $this->LAST_ERROR = ''; $post = static::GetByID($ID); if (!($post_arr = $post->Fetch())) { $this->LAST_ERROR .= GetMessage('class_post_err_notfound'); return false; } if ($post_arr['STATUS'] != 'P') { $this->LAST_ERROR .= GetMessage('class_post_err_status') . '<br>'; return false; } if ( $check_charset && ($post_arr['MSG_CHARSET'] <> '') && (mb_strtoupper($post_arr['MSG_CHARSET']) !== mb_strtoupper(LANG_CHARSET)) ) { return 'CONTINUE'; } if (CPosting::Lock($ID) === false) { return 'CONTINUE'; } if ($post_arr['VERSION'] <> '2') { if (is_string($post_arr['BCC_TO_SEND']) && $post_arr['BCC_TO_SEND'] <> '') { $a = explode(',', $post_arr['BCC_TO_SEND']); foreach ($a as $e) { $e = trim($e, " \t\n\r"); if ($e !== '') { $DB->Query('INSERT INTO b_posting_email (POSTING_ID, STATUS, EMAIL) VALUES (' . $ID . ", 'Y', '" . $DB->ForSQL($e) . "')", true); } } } if (is_string($post_arr['ERROR_EMAIL']) && $post_arr['ERROR_EMAIL'] <> '') { $a = explode(',', $post_arr['ERROR_EMAIL']); foreach ($a as $e) { $e = trim($e, " \t\n\r"); if ($e !== '') { $DB->Query('INSERT INTO b_posting_email (POSTING_ID, STATUS, EMAIL) VALUES (' . $ID . ", 'E', '" . $DB->ForSQL($e) . "')", true); } } } if (is_string($post_arr['SENT_BCC']) && $post_arr['SENT_BCC'] <> '') { $a = explode(',', $post_arr['SENT_BCC']); foreach ($a as $e) { $e = trim($e, " \t\n\r"); if ($e !== '') { $DB->Query('INSERT INTO b_posting_email (POSTING_ID, STATUS, EMAIL) VALUES (' . $ID . ", 'N', '" . $DB->ForSQL($e) . "')", true); } } } $DB->Query("UPDATE b_posting SET VERSION='2', BCC_TO_SEND=null, ERROR_EMAIL=null, SENT_BCC=null WHERE ID=" . $ID); } $tools = new CMailTools; //MIME with attachments if ($post_arr['BODY_TYPE'] == 'html' && COption::GetOptionString('subscribe', 'attach_images') == 'Y') { $post_arr['BODY'] = $tools->ReplaceImages($post_arr['BODY']); } if ($post_arr['CHARSET'] <> '') { $from_charset = $post_arr['MSG_CHARSET'] ?: SITE_CHARSET; $post_arr['BODY'] = $APPLICATION->ConvertCharset($post_arr['BODY'], $from_charset, $post_arr['CHARSET']); $post_arr['SUBJECT'] = $APPLICATION->ConvertCharset($post_arr['SUBJECT'], $from_charset, $post_arr['CHARSET']); $post_arr['FROM_FIELD'] = $APPLICATION->ConvertCharset($post_arr['FROM_FIELD'], $from_charset, $post_arr['CHARSET']); } //Preparing message header, text, subject $sBody = str_replace("\r\n", "\n", $post_arr['BODY']); $sBody = implode( "\n", array_filter( preg_split('/(.{512}[^ ]* )/', $sBody . ' ', -1, PREG_SPLIT_DELIM_CAPTURE) ) ); //Some MTA has 4K limit for fgets function. So we have to split the message body. if (COption::GetOptionString('main', 'CONVERT_UNIX_NEWLINE_2_WINDOWS', 'N') == 'Y') { $sBody = str_replace("\n", "\r\n", $sBody); } if (COption::GetOptionString('subscribe', 'allow_8bit_chars') <> 'Y') { $sSubject = CMailTools::EncodeSubject($post_arr['SUBJECT'], $post_arr['CHARSET']); $sFrom = CMailTools::EncodeHeaderFrom($post_arr['FROM_FIELD'], $post_arr['CHARSET']); } else { $sSubject = $post_arr['SUBJECT']; $sFrom = $post_arr['FROM_FIELD']; } if ($post_arr['BODY_TYPE'] == 'html') { //URN2URI $tmpTools = new CMailTools; $sBody = $tmpTools->ReplaceHrefs($sBody); } $bHasAttachments = false; $sHeader = ''; $sBoundary = ''; if (count($tools->aMatches) > 0) { $bHasAttachments = true; $sBoundary = '----------' . uniqid(''); $sHeader = 'From: ' . $sFrom . $eol . 'X-Bitrix-Posting: ' . $post_arr['ID'] . $eol . 'MIME-Version: 1.0' . $eol . 'Content-Type: multipart/mixed; boundary="' . $sBoundary . '"' . $eol . 'Content-Transfer-Encoding: 8bit'; $sBody = '--' . $sBoundary . $eol . 'Content-Type: ' . ($post_arr['BODY_TYPE'] == 'html' ? 'text/html' : 'text/plain') . ($post_arr['CHARSET'] <> '' ? '; charset=' . $post_arr['CHARSET'] : '') . $eol . 'Content-Transfer-Encoding: 8bit' . $eol . $eol . $sBody . $eol; foreach ($tools->aMatches as $attachment) { if ($post_arr['CHARSET'] <> '') { $from_charset = $post_arr['MSG_CHARSET'] ?: SITE_CHARSET; $attachment['DEST'] = $APPLICATION->ConvertCharset($attachment['DEST'], $from_charset, $post_arr['CHARSET']); } if (COption::GetOptionString('subscribe', 'allow_8bit_chars') <> 'Y') { $name = CMailTools::EncodeSubject($attachment['DEST'], $post_arr['CHARSET']); } else { $name = $attachment['DEST']; } $sBody .= $eol . '--' . $sBoundary . $eol . 'Content-Type: ' . $attachment['CONTENT_TYPE'] . '; name="' . $name . '"' . $eol . 'Content-Transfer-Encoding: base64' . $eol . 'Content-ID: <' . $attachment['ID'] . '>' . $eol . $eol . chunk_split( base64_encode( file_get_contents($attachment['PATH']) ), 72, $eol ); } } $arFiles = []; $maxFileSize = intval(COption::GetOptionInt('subscribe', 'max_file_size')); $rsFile = CPosting::GetFileList($ID); while ($arFile = $rsFile->Fetch()) { if ( $maxFileSize == 0 || $arFile['FILE_SIZE'] <= $maxFileSize ) { $arFiles[] = $arFile; } } if (!empty($arFiles)) { if (!$bHasAttachments) { $bHasAttachments = true; $sBoundary = '----------' . uniqid(''); $sHeader = 'From: ' . $sFrom . $eol . 'X-Bitrix-Posting: ' . $post_arr['ID'] . $eol . 'MIME-Version: 1.0' . $eol . 'Content-Type: multipart/mixed; boundary="' . $sBoundary . '"' . $eol . 'Content-Transfer-Encoding: 8bit'; $sBody = '--' . $sBoundary . $eol . 'Content-Type: ' . ($post_arr['BODY_TYPE'] == 'html' ? 'text/html' : 'text/plain') . ($post_arr['CHARSET'] <> '' ? '; charset=' . $post_arr['CHARSET'] : '') . $eol . 'Content-Transfer-Encoding: 8bit' . $eol . $eol . $sBody . $eol; } foreach ($arFiles as $arFile) { if ($post_arr['CHARSET'] <> '') { $from_charset = $post_arr['MSG_CHARSET'] ?: SITE_CHARSET; $file_name = $APPLICATION->ConvertCharset($arFile['ORIGINAL_NAME'], $from_charset, $post_arr['CHARSET']); } else { $file_name = $arFile['ORIGINAL_NAME']; } $sBody .= $eol . '--' . $sBoundary . $eol . 'Content-Type: ' . $arFile['CONTENT_TYPE'] . '; name="' . $file_name . '"' . $eol . 'Content-Transfer-Encoding: base64' . $eol . 'Content-Disposition: attachment; filename="' . CMailTools::EncodeHeaderFrom($file_name, $post_arr['CHARSET']) . '"' . $eol . $eol; $arTempFile = CFile::MakeFileArray($arFile['ID']); $sBody .= chunk_split( base64_encode( file_get_contents($arTempFile['tmp_name']) ), 72, $eol ); } } if ($bHasAttachments) { $sBody .= $eol . '--' . $sBoundary . '--' . $eol; } else { //plain message without MIME $sHeader = 'From: ' . $sFrom . $eol . 'X-Bitrix-Posting: ' . $post_arr['ID'] . $eol . 'MIME-Version: 1.0' . $eol . 'Content-Type: ' . ($post_arr['BODY_TYPE'] == 'html' ? 'text/html' : 'text/plain') . ($post_arr['CHARSET'] <> '' ? '; charset=' . $post_arr['CHARSET'] : '') . $eol . 'Content-Transfer-Encoding: 8bit'; } $mail_additional_parameters = trim(COption::GetOptionString('subscribe', 'mail_additional_parameters')); $context = new Mail\Context(); $context->setCategory(Mail\Context::CAT_EXTERNAL); $context->setPriority(Mail\Context::PRIORITY_LOW); if ($post_arr['DIRECT_SEND'] == 'Y') { //personal delivery $arEvents = GetModuleEvents('subscribe', 'BeforePostingSendMail', true); $rsEmails = $DB->Query($DB->TopSql(' SELECT * FROM b_posting_email WHERE POSTING_ID = ' . $ID . " AND STATUS='Y' ", $maxcount)); while ($arEmail = $rsEmails->Fetch()) { //Event part $arFields = [ 'POSTING_ID' => $ID, 'EMAIL' => $arEmail['EMAIL'], 'SUBJECT' => $sSubject, 'BODY' => $sBody, 'HEADER' => $sHeader, 'EMAIL_EX' => $arEmail, ]; foreach ($arEvents as $arEvent) { $arFields = ExecuteModuleEventEx($arEvent, [$arFields]); } //Sending if (is_array($arFields)) { $to = CMailTools::EncodeHeaderFrom($arFields['EMAIL'], $post_arr['CHARSET']); $result = bxmail($to, $arFields['SUBJECT'], $arFields['BODY'], $arFields['HEADER'], $mail_additional_parameters, $context); } else { $result = $arFields !== false; } //Result check and iteration if ($result) { $DB->Query("UPDATE b_posting_email SET STATUS='N' WHERE ID = " . $arEmail['ID']); } else { $DB->Query("UPDATE b_posting_email SET STATUS='E' WHERE ID = " . $arEmail['ID']); } if ($timeout > 0 && microtime(1) - $start_time >= $timeout) { break; } self::$current_emails_per_hit++; } } else { //BCC delivery $rsEmails = $DB->Query($DB->TopSql(' SELECT * FROM b_posting_email WHERE POSTING_ID = ' . $ID . " AND STATUS='Y' ", COption::GetOptionString('subscribe', 'max_bcc_count'))); $aStep = []; while ($arEmail = $rsEmails->Fetch()) { $aStep[$arEmail['ID']] = $arEmail['EMAIL']; } if (count($aStep) > 0) { $BCC = implode(',', $aStep); $sHeaderStep = $sHeader . $eol . 'Bcc: ' . $BCC; $result = bxmail($post_arr['TO_FIELD'], $sSubject, $sBody, $sHeaderStep, $mail_additional_parameters, $context); if ($result) { $DB->Query("UPDATE b_posting_email SET STATUS='N' WHERE ID in (" . implode(', ', array_keys($aStep)) . ')'); } else { $DB->Query("UPDATE b_posting_email SET STATUS='E' WHERE ID in (" . implode(', ', array_keys($aStep)) . ')'); $this->LAST_ERROR .= GetMessage('class_post_err_mail') . '<br>'; } } } //set status and delivered and error emails $arStatuses = static::GetEmailStatuses($ID); if (!array_key_exists('Y', $arStatuses)) { $STATUS = array_key_exists('E', $arStatuses) ? 'E' : 'S'; $DATE = $DB->GetNowFunction(); } else { $STATUS = 'P'; $DATE = 'null'; } CPosting::UnLock($ID); $DB->Query("UPDATE b_posting SET STATUS='" . $STATUS . "', DATE_SENT=" . $DATE . ' WHERE ID=' . $ID); return ($STATUS === 'P' ? 'CONTINUE' : true); } public static function GetEmailStatuses($ID) { global $DB; $arStatuses = []; $rs = $DB->Query(' SELECT STATUS, COUNT(*) CNT FROM b_posting_email WHERE POSTING_ID = ' . intval($ID) . ' GROUP BY STATUS '); while ($ar = $rs->Fetch()) { $arStatuses[$ar['STATUS']] = $ar['CNT']; } return $arStatuses; } public static function GetEmailsByStatus($ID, $STATUS) { global $DB; return $DB->Query(' SELECT * FROM b_posting_email WHERE POSTING_ID = ' . intval($ID) . " AND STATUS = '" . $DB->ForSQL($STATUS) . "' ORDER BY EMAIL "); } public function ChangeStatus($ID, $status) { global $DB; $ID = intval($ID); $this->LAST_ERROR = ''; $strSql = 'SELECT STATUS, VERSION FROM b_posting WHERE ID=' . $ID; $db_result = $DB->Query($strSql, false, 'File: ' . __FILE__ . '<br>Line: ' . __LINE__); $arResult = $db_result->Fetch(); if (!$arResult) { $this->LAST_ERROR = GetMessage('class_post_err_notfound') . '<br>'; return false; } if ($arResult['STATUS'] == $status) { return true; } switch ($arResult['STATUS'] . $status) { case 'DP': //BCC_TO_SEND fill $post = static::GetByID($ID); if (!($post_arr = $post->Fetch())) { $this->LAST_ERROR .= GetMessage('class_post_err_notfound') . '<br>'; return false; } $DB->Query('DELETE from b_posting_email WHERE POSTING_ID = ' . $ID); $DB->Query(" INSERT INTO b_posting_email (POSTING_ID, STATUS, EMAIL, SUBSCRIPTION_ID, USER_ID) SELECT DISTINCT PR.POSTING_ID, 'Y', S.EMAIL, min(S.ID), min(S.USER_ID) FROM b_posting_rubric PR INNER JOIN b_subscription_rubric SR ON SR.LIST_RUBRIC_ID = PR.LIST_RUBRIC_ID INNER JOIN b_subscription S ON S.ID = SR.SUBSCRIPTION_ID LEFT JOIN b_user U ON U.ID = S.USER_ID WHERE PR.POSTING_ID = " . $ID . " AND S.CONFIRMED = 'Y' AND S.ACTIVE = 'Y' AND (U.ID IS NULL OR U.ACTIVE = 'Y') " . ($post_arr['SUBSCR_FORMAT'] == '' || $post_arr['SUBSCR_FORMAT'] === 'NOT_REF' ? '' : "AND S.FORMAT = '" . ($post_arr['SUBSCR_FORMAT'] == 'text' ? 'text' : 'html') . "'") . " " . ($post_arr['EMAIL_FILTER'] == '' || $post_arr['EMAIL_FILTER'] === 'NOT_REF' ? '' : 'AND ' . GetFilterQuery('S.EMAIL', $post_arr['EMAIL_FILTER'], 'Y', ['@', '.', '_'])) . " GROUP BY PR.POSTING_ID, S.EMAIL "); //send to user groups $res = $DB->Query('SELECT * FROM b_posting_group WHERE POSTING_ID = ' . $ID . ' AND GROUP_ID = 2'); if ($res->Fetch()) { $DB->Query(' INSERT INTO b_posting_email (POSTING_ID, STATUS, EMAIL, SUBSCRIPTION_ID, USER_ID) SELECT ' . $ID . ", 'Y', U.EMAIL, NULL, MIN(U.ID) FROM b_user U WHERE U.ACTIVE = 'Y' and U.EMAIL IS NOT NULL " . ($post_arr['EMAIL_FILTER'] == '' || $post_arr['EMAIL_FILTER'] === 'NOT_REF' ? '' : 'AND ' . GetFilterQuery('U.EMAIL', $post_arr['EMAIL_FILTER'], 'Y', ['@', '.', '_'])) . ' and U.EMAIL not in (SELECT EMAIL FROM b_posting_email WHERE POSTING_ID = ' . $ID . ') GROUP BY U.EMAIL '); } else { $DB->Query(" INSERT INTO b_posting_email (POSTING_ID, STATUS, EMAIL, SUBSCRIPTION_ID, USER_ID) SELECT PG.POSTING_ID, 'Y', U.EMAIL, NULL, MIN(U.ID) FROM b_posting_group PG INNER JOIN b_user_group UG ON UG.GROUP_ID = PG.GROUP_ID INNER JOIN b_user U ON U.ID = UG.USER_ID WHERE PG.POSTING_ID = " . $ID . ' and (UG.DATE_ACTIVE_FROM is null or UG.DATE_ACTIVE_FROM <= ' . $DB->CurrentTimeFunction() . ') and (UG.DATE_ACTIVE_TO is null or UG.DATE_ACTIVE_TO >= ' . $DB->CurrentTimeFunction() . ") and U.ACTIVE = 'Y' and U.EMAIL IS NOT NULL " . ($post_arr['EMAIL_FILTER'] == '' || $post_arr['EMAIL_FILTER'] === 'NOT_REF' ? '' : 'AND ' . GetFilterQuery('U.EMAIL', $post_arr['EMAIL_FILTER'], 'Y', ['@', '.', '_'])) . ' and U.EMAIL not in (SELECT EMAIL FROM b_posting_email WHERE POSTING_ID = ' . $ID . ') GROUP BY PG.POSTING_ID, U.EMAIL '); } //from additional emails $BCC = $post_arr['BCC_FIELD']; if ($post_arr['DIRECT_SEND'] == 'Y') { $BCC .= ($BCC ? ',' : '') . $post_arr['TO_FIELD']; } $BCC = str_replace("\r\n", "\n", $BCC); $BCC = str_replace("\n", ',', $BCC); $aBcc = explode(',', $BCC); foreach ($aBcc as $email) { $email = trim($email, " \t\n\r"); if ($email !== '') { $DB->Query(" INSERT INTO b_posting_email (POSTING_ID, STATUS, EMAIL, SUBSCRIPTION_ID, USER_ID) SELECT P.ID, 'Y', '" . ($DB->ForSQL($email)) . "', NULL, NULL FROM b_posting P WHERE P.ID = " . $ID . " and '" . ($DB->ForSQL($email)) . "' not in (SELECT EMAIL FROM b_posting_email WHERE POSTING_ID = " . $ID . ') '); } } $res = $DB->Query('SELECT count(*) CNT from b_posting_email WHERE POSTING_ID = ' . $ID); $ar = $res->Fetch(); if ($ar['CNT'] > 0) { $DB->Query("UPDATE b_posting SET STATUS='" . $status . "', VERSION='2', BCC_TO_SEND=null, ERROR_EMAIL=null, SENT_BCC=null WHERE ID=" . $ID); } else { $this->LAST_ERROR .= GetMessage('class_post_err_status4'); return false; } break; case 'PW': case 'WP': case 'PE': case 'PS': $DB->Query("UPDATE b_posting SET STATUS='" . $status . "' WHERE ID=" . $ID); break; case 'EW'://This is the way to resend error e-mails case 'EP': if ($arResult['VERSION'] == '2') { $DB->Query("UPDATE b_posting_email SET STATUS='Y' WHERE POSTING_ID=" . $ID . " AND STATUS='E'"); $DB->Query("UPDATE b_posting SET STATUS='" . $status . "' WHERE ID=" . $ID); } else { //Send it in old fashion way $DB->Query("UPDATE b_posting SET STATUS='" . $status . "', BCC_TO_SEND=ERROR_EMAIL, ERROR_EMAIL=null WHERE ID=" . $ID); } break; case 'ED': case 'SD': case 'WD': $DB->Query("UPDATE b_posting SET STATUS='" . $status . "', VERSION='2', SENT_BCC=null, ERROR_EMAIL=null, BCC_TO_SEND=null, DATE_SENT=null WHERE ID=" . $ID); break; default: $this->LAST_ERROR = GetMessage('class_post_err_status2'); return false; } return true; } public function GetList($aSort=[], $arFilter=[], $arSelect=[], $arNavStartParams=false) { global $DB; $connection = \Bitrix\Main\Application::getConnection(); $helper = $connection->getSqlHelper(); static $arSelectFields = false; if (!$arSelectFields) { $arSelectFields = [ 'STATUS_TITLE' => "case when P.STATUS='S' then '" . $DB->ForSql(GetMessage('POST_STATUS_SENT')) . "' when P.STATUS='P' then '" . $DB->ForSql(GetMessage('POST_STATUS_PART')) . "' when P.STATUS='E' then '" . $DB->ForSql(GetMessage('POST_STATUS_ERROR')) . "' when P.STATUS='W' then '" . $DB->ForSql(GetMessage('POST_STATUS_WAIT')) . "' else '" . $DB->ForSql(GetMessage('POST_STATUS_DRAFT')) . "' end", 'ID' => 'P.ID', 'STATUS' => 'P.STATUS', 'FROM_FIELD' => 'P.FROM_FIELD', 'TO_FIELD' => 'P.TO_FIELD', 'EMAIL_FILTER' => 'P.EMAIL_FILTER', 'SUBJECT' => 'P.SUBJECT', 'BODY_TYPE' => 'P.BODY_TYPE', 'DIRECT_SEND' => 'P.DIRECT_SEND', 'CHARSET' => 'P.CHARSET', 'MSG_CHARSET' => 'P.MSG_CHARSET', 'SUBSCR_FORMAT' => 'P.SUBSCR_FORMAT', 'TIMESTAMP_X' => $DB->DateToCharFunction('P.TIMESTAMP_X'), 'DATE_SENT' => $DB->DateToCharFunction('P.DATE_SENT'), ]; } $this->LAST_ERROR = ''; $arSqlSearch = []; $strSqlSearch = ''; if (is_array($arFilter)) { foreach ($arFilter as $key => $val) { if (!is_array($val) && ((string)$val === '' || $val === 'NOT_REF')) { continue; } switch (strtoupper($key)) { case 'MSG_CHARSET': $arSqlSearch[] = "P.MSG_CHARSET = '" . $DB->ForSql($val) . "'"; break; case 'ID': $arSqlSearch[] = GetFilterQuery('P.ID', $val, 'N'); break; case 'TIMESTAMP_1': if ($DB->IsDate($val)) { $arSqlSearch[] = 'P.TIMESTAMP_X>=' . $DB->CharToDateFunction($val, 'SHORT'); } else { $this->LAST_ERROR .= GetMessage('POST_WRONG_TIMESTAMP_FROM') . '<br>'; } break; case 'TIMESTAMP_2': if ($DB->IsDate($val)) { $arSqlSearch[] = 'P.TIMESTAMP_X < ' . $helper->addDaysToDateTime(1, $DB->CharToDateFunction($val, 'SHORT')); } else { $this->LAST_ERROR .= GetMessage('POST_WRONG_TIMESTAMP_TILL') . '<br>'; } break; case 'DATE_SENT_1': if ($DB->IsDate($val)) { $arSqlSearch[] = 'P.DATE_SENT>=' . $DB->CharToDateFunction($val, 'SHORT'); } else { $this->LAST_ERROR .= GetMessage('POST_WRONG_DATE_SENT_FROM') . '<br>'; } break; case 'DATE_SENT_2': if ($DB->IsDate($val)) { $arSqlSearch[] = 'P.DATE_SENT < ' . $helper->addDaysToDateTime(1, $DB->CharToDateFunction($val, 'SHORT')); } else { $this->LAST_ERROR .= GetMessage('POST_WRONG_DATE_SENT_TILL') . '<br>'; } break; case 'STATUS': $arSqlSearch[] = GetFilterQuery(['P.STATUS', $arSelectFields['STATUS_TITLE']], $val, 'Y', [], 'N', 'N'); break; case 'STATUS_ID': $arSqlSearch[] = GetFilterQuery('P.STATUS', $val, 'N'); break; case 'SUBJECT': $arSqlSearch[] = GetFilterQuery('P.SUBJECT', $val); break; case 'FROM': $arSqlSearch[] = GetFilterQuery('P.FROM_FIELD', $val, 'Y', ['@', '_', '.']); break; case 'TO': $r = GetFilterQuery('PE.EMAIL', $val, 'Y', ['@', '_', '.']); if ($r <> '') { $arSqlSearch[] = "EXISTS (SELECT * FROM b_posting_email PE WHERE PE.POSTING_ID=P.ID AND PE.STATUS='N' AND " . $r . ')'; } break; case 'BODY_TYPE': $arSqlSearch[] = ($val == 'html') ? "P.BODY_TYPE='html'" : "P.BODY_TYPE='text'"; break; case 'RUB_ID': if (is_array($val) && count($val) > 0) { $rub_id = []; foreach ($val as $i => $v) { $v = intval($v); if ($v > 0) { $rub_id[$v] = $v; } } if (count($rub_id)) { $arSqlSearch[] = 'EXISTS (SELECT * from b_posting_rubric PR WHERE PR.POSTING_ID = P.ID AND PR.LIST_RUBRIC_ID in (' . implode(', ', $rub_id) . '))'; } } break; case 'BODY': $arSqlSearch[] = GetFilterQuery('P.BODY', $val); break; case 'AUTO_SEND_TIME_1': if ($DB->IsDate($val, false, false, 'FULL')) { $arSqlSearch[] = '(P.AUTO_SEND_TIME is not null and P.AUTO_SEND_TIME>=' . $DB->CharToDateFunction($val, 'FULL') . ' )'; } elseif ($DB->IsDate($val, false, false, 'SHORT')) { $arSqlSearch[] = '(P.AUTO_SEND_TIME is not null and P.AUTO_SEND_TIME>=' . $DB->CharToDateFunction($val, 'SHORT') . ' )'; } else { $this->LAST_ERROR .= GetMessage('POST_WRONG_AUTO_FROM') . '<br>'; } break; case 'AUTO_SEND_TIME_2': if ($DB->IsDate($val, false, false, 'FULL')) { $arSqlSearch[] = '(P.AUTO_SEND_TIME is not null and P.AUTO_SEND_TIME<=' . $DB->CharToDateFunction($val, 'FULL') . ' )'; } elseif ($DB->IsDate($val, false, false, 'SHORT')) { $arSqlSearch[] = '(P.AUTO_SEND_TIME is not null and P.AUTO_SEND_TIME<=' . $DB->CharToDateFunction($val, 'SHORT') . ' )'; } else { $this->LAST_ERROR .= GetMessage('POST_WRONG_AUTO_TILL') . '<br>'; } break; } } } $arOrder = []; foreach ($aSort as $key => $ord) { $key = mb_strtoupper($key); $ord = (mb_strtoupper($ord) !== 'ASC' ? 'DESC' : 'ASC'); switch ($key) { case 'ID': $arOrder[$key] = 'P.ID ' . $ord; break; case 'TIMESTAMP': $arOrder[$key] = 'P.TIMESTAMP_X ' . $ord; break; case 'SUBJECT': $arOrder[$key] = 'P.SUBJECT ' . $ord; break; case 'BODY_TYPE': $arOrder[$key] = 'P.BODY_TYPE ' . $ord; break; case 'STATUS': $arOrder[$key] = 'P.STATUS ' . $ord; break; case 'DATE_SENT': $arOrder[$key] = 'P.DATE_SENT ' . $ord; break; case 'AUTO_SEND_TIME': $arOrder[$key] = 'P.AUTO_SEND_TIME ' . $ord; break; case 'FROM_FIELD': $arOrder[$key] = 'P.FROM_FIELD ' . $ord; break; case 'TO_FIELD': $arOrder[$key] = 'P.TO_FIELD ' . $ord; break; } } if (!$arOrder) { $arOrder['ID'] = 'P.ID DESC'; } $strSqlOrder = ' ORDER BY ' . implode(', ', $arOrder); if (!is_array($arSelect) || empty($arSelect)) { $arSelect = array_keys($arSelectFields); } $arSqlSelect = []; foreach ($arSelect as $selectField) { if (isset($arSelectFields[$selectField])) { $arSqlSelect[$selectField] = $arSelectFields[$selectField] . ' as ' . $selectField; } } if (!$arSqlSelect) { $arSqlSelect['ID'] = $arSelectFields['ID'] . ' as ID'; } $strSqlSearch = GetFilterSqlSearch($arSqlSearch); $strSql = ' SELECT ' . implode(', ', $arSqlSelect) . ' FROM b_posting P WHERE ' . $strSqlSearch . ' ' . $strSqlOrder; if (is_array($arNavStartParams)) { $nTopCount = (isset($arNavStartParams['nTopCount']) ? (int)$arNavStartParams['nTopCount'] : 0); if ($nTopCount > 0) { $res = $DB->Query($DB->TopSql( $strSql, $nTopCount )); } else { $res_cnt = $DB->Query(' SELECT COUNT(P.ID) as C FROM b_posting P WHERE ' . $strSqlSearch . ' '); $res_cnt = $res_cnt->Fetch(); $res = new CDBResult(); $res->NavQuery($strSql, $res_cnt['C'], $arNavStartParams); } } else { $res = $DB->Query($strSql); } $res->is_filtered = (IsFiltered($strSqlSearch)); return $res; } public static function Lock($ID = 0) { $connection = \Bitrix\Main\Application::getConnection(); return $connection->lock('post_' . $ID); } public static function UnLock($ID = 0) { $connection = \Bitrix\Main\Application::getConnection(); return $connection->unlock('post_' . $ID); } }