Array('self' => 'edit'), 'OnSaveSelected' => Array('self' => 'view'), 'OnProcessEmailQueue' => Array('self' => 'add|edit'), ); $this->permMapping = array_merge($this->permMapping, $permissions); } /** * Changes permission section to one from REQUEST, not from config * * @param kEvent $event */ function CheckPermission(&$event) { $module = $this->Application->GetVar('module'); if (strlen($module) > 0) { // checking permission when lising module email events in separate section $module = explode(':', $module, 2); if (count($module) == 1) { $main_prefix = $this->Application->findModule('Name', $module[0], 'Var'); } else { $exceptions = Array('Category' => 'c', 'Users' => 'u'); $main_prefix = $exceptions[ $module[1] ]; } $section = $this->Application->getUnitOption($main_prefix.'.email', 'PermSection'); $event->setEventParam('PermSection', $section); } // checking permission when listing all email events when editing language return parent::CheckPermission($event); } /** * Apply any custom changes to list's sql query * * @param kEvent $event * @access protected * @see OnListBuild */ function SetCustomQuery(&$event) { $object =& $event->getObject(); /* @var $object kDBList */ if ($event->Special == 'module') { $module = $this->Application->GetVar('module'); $object->addFilter('module_filter', '%1$s.Module = '.$this->Conn->qstr($module)); } if (!$event->Special && !$this->Application->isDebugMode()) { // no special $object->addFilter('enabled_filter', '%1$s.Enabled <> ' . STATUS_DISABLED); } } /** * Sets event id * * @param kEvent $event */ function OnPreCreate(&$event) { parent::OnPreCreate($event); $object =& $event->getObject(); /* @var $object kDBItem */ $object->SetDBField('Headers', $this->Application->ConfigValue('Smtp_DefaultHeaders')); } /** * Sets status Front-End Only to selected email events * * @param kEvent $event */ function OnFrontOnly(&$event) { if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = erFAIL; return ; } $ids = implode(',', $this->StoreSelectedIDs($event)); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'UPDATE '.$table_name.' SET FrontEndOnly = 1 WHERE EventId IN ('.$ids.')'; $this->Conn->Query($sql); $this->clearSelectedIDs($event); } /** * Sets selected user to email events selected * * @param kEvent $event */ function OnSelectUser(&$event) { if ($event->Special != 'module') { parent::OnSelectUser($event); return ; } if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) { $event->status = erFAIL; return ; } $items_info = $this->Application->GetVar('u'); if ($items_info) { $user_id = array_shift( array_keys($items_info) ); $selected_ids = $this->getSelectedIDs($event, true); $ids = $this->Application->RecallVar($event->getPrefixSpecial().'_selected_ids'); $id_field = $this->Application->getUnitOption($event->Prefix, 'IDField'); $table_name = $this->Application->getUnitOption($event->Prefix, 'TableName'); $sql = 'UPDATE '.$table_name.' SET '.$this->Application->RecallVar('dst_field').' = '.$user_id.' WHERE '.$id_field.' IN ('.$ids.')'; $this->Conn->Query($sql); } $this->finalizePopup($event); } /** * Saves selected ids to session * * @param kEvent $event */ function OnSaveSelected(&$event) { $this->StoreSelectedIDs($event); } /** * Returns email event object based on given kEvent object * * @param kEvent $event * @return kDBItem */ function &_getEmailEvent(&$event) { $false = false; $name = $event->getEventParam('EmailEventName'); $type = $event->getEventParam('EmailEventType'); $object =& $event->getObject( Array('skip_autoload' => true) ); /* @var $object kDBItem */ if (!$object->isLoaded() || (($object->GetDBField('Event') != $name) && ($object->GetDBField('Type') != $type))) { // get event parameters by name & type $load_keys = Array ('Event' => $name, 'Type' => $type); $object->Load($load_keys); if (!$object->isLoaded() || ($object->GetDBField('Enabled') == STATUS_DISABLED)) { // event record not found OR is disabled return $false; } if ($object->GetDBField('FrontEndOnly') && $this->Application->isAdmin) { return $false; } } return $object; } /** * Returns sender & recipients ids plus event_id (as parameter by reference) * * @param kEvent $event * * @return mixed */ function GetMessageRecipients(&$event) { $object =& $this->_getEmailEvent($event); /* @var $object kDBItem */ // initial values $to_user_id = $event->getEventParam('EmailEventToUserId'); if ( !is_numeric($to_user_id) ) { $to_user_id = -1; // when not specified, then send to "root" } $from_user_id = $object->GetDBField('FromUserId'); if ($object->GetDBField('Type') == EVENT_TYPE_ADMIN) { // For type "Admin" recipient is a user from field FromUserId which means From/To user in Email events list if ($to_user_id == -1) { $to_user_id = $from_user_id; } $from_user_id = -1; } return Array ($from_user_id, $to_user_id); } /** * Returns user name, email by id, or ones, that specified in $direct_params * * @param int $user_id * @param string $user_type type of user = {to,from} * @param Array $direct_params * @return Array */ function GetRecipientInfo($user_id, $user_type, $direct_params = null) { // load user, because it can be addressed from email template tags $user =& $this->Application->recallObject('u.email-'.$user_type, null, Array('skip_autoload' => true)); /* @var $user UsersItem */ $email = $name = ''; $result = $user_id > 0 ? $user->Load( (int)$user_id ) : $user->Clear(); if ($user->IsLoaded()) { $email = $user->GetDBField('Email'); $name = trim($user->GetDBField('FirstName').' '.$user->GetDBField('LastName')); } if (is_array($direct_params)) { if (isset($direct_params[$user_type.'_email'])) { $email = $direct_params[$user_type.'_email']; } if (isset($direct_params[$user_type.'_name'])) { $name = $direct_params[$user_type.'_name']; } } if (!$email) { // if email is empty, then use admins email $email = $this->Application->ConfigValue('Smtp_AdminMailFrom'); } if (!$name) { $name = $user_type == 'from' ? strip_tags($this->Application->ConfigValue('Site_Name')) : $email; } return Array ($email, $name); } /** * Returns email event message by ID (headers & body in one piece) * * @param kEvent $event * @param int $language_id */ function GetMessageBody(&$event, $language_id = null) { if (!isset($language_id)) { $language_id = $this->Application->GetVar('m_lang'); } $object =& $this->_getEmailEvent($event); // 1. get message body $message_body = $this->_formMessageBody($object, $language_id, $object->GetDBField('MessageType')); // 2. replace tags if needed $default_replacement_tags = Array ( ' ' ' 'GetDBField('ReplacementTags'); $replacement_tags = $replacement_tags ? unserialize($replacement_tags) : Array (); $replacement_tags = array_merge_recursive2($default_replacement_tags, $replacement_tags); foreach ($replacement_tags as $replace_from => $replace_to) { $message_body = str_replace($replace_from, $replace_to, $message_body); } return $message_body; } /** * Prepare email message body * * @param kDBItem $object * @param int $language_id * @return string */ function _formMessageBody(&$object, $language_id) { $default_language_id = $this->Application->GetDefaultLanguageId(); $fields_hash = Array ( 'Headers' => $object->GetDBField('Headers'), ); // prepare subject $subject = $object->GetDBField('l' . $language_id . '_Subject'); if (!$subject) { $subject = $object->GetDBField('l' . $default_language_id . '_Subject'); } $fields_hash['Subject'] = $subject; // prepare body $body = $object->GetDBField('l' . $language_id . '_Body'); if (!$body) { $body = $object->GetDBField('l' . $default_language_id . '_Body'); } $fields_hash['Body'] = $body; $email_message_helper =& $this->Application->recallObject('EmailMessageHelper'); /* @var $email_message_helper EmailMessageHelper */ $ret = $email_message_helper->buildTemplate($fields_hash); // add footer $footer = $this->_getFooter($language_id, $object->GetDBField('MessageType')); if ($ret && $footer) { $ret .= "\r\n" . $footer; } return $ret; } /** * Returns email footer * * @param int $language_id * @param string $message_type * @return string */ function _getFooter($language_id, $message_type) { static $footer = null; if (!isset($footer)) { $default_language_id = $this->Application->GetDefaultLanguageId(); $sql = 'SELECT l' . $language_id . '_Body, l' . $default_language_id . '_Body FROM ' . $this->Application->getUnitOption('emailevents', 'TableName') . ' em WHERE Event = "COMMON.FOOTER"'; $footer_data = $this->Conn->GetRow($sql); $footer = $footer_data['l' . $language_id . '_Body']; if (!$footer) { $footer = $footer_data['l' . $default_language_id . '_Body']; } if ($message_type == 'text') { $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $footer = $esender->ConvertToText($footer); } } return $footer; } /** * Parse message template and return headers (as array) and message body part * * @param string $message * @param Array $direct_params * @return Array */ function ParseMessageBody($message, $direct_params = null) { $message_language = $this->_getSendLanguage($direct_params); $this->_changeLanguage($message_language); $direct_params['message_text'] = isset($direct_params['message']) ? $direct_params['message'] : ''; // parameter alias // 1. parse template $this->Application->InitParser(); $parser_params = $this->Application->Parser->Params; // backup parser params $this->Application->Parser->SetParams( array_merge_recursive2($parser_params, $direct_params) ); $message = implode('&|&', explode("\n\n", $message, 2)); // preserves double \n in case when tag is located in subject field $message = $this->Application->Parser->Parse($message, 'email_template', 0); $this->Application->Parser->SetParams($parser_params); // restore parser params // 2. replace line endings, that are send with data submitted via request $message = str_replace("\r\n", "\n", $message); // possible case $message = str_replace("\r", "\n", $message); // impossible case, but just in case replace this too // 3. separate headers from body $message_headers = Array (); list($headers, $message_body) = explode('&|&', $message, 2); $category_helper =& $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ $message_body = $category_helper->replacePageIds($message_body); $headers = explode("\n", $headers); foreach ($headers as $header) { $header = explode(':', $header, 2); $message_headers[ trim($header[0]) ] = trim($header[1]); } $this->_changeLanguage(); return Array ($message_headers, $message_body); } /** * Raised when email message shoul be sent * * @param kEvent $event */ function OnEmailEvent(&$event) { $email_event_name = $event->getEventParam('EmailEventName'); if (strpos($email_event_name, '_') !== false) { trigger_error('Invalid email event name '.$email_event_name.'. Use only UPPERCASE characters and dots as email event names', E_USER_ERROR); } $object =& $this->_getEmailEvent($event); if (!is_object($object)) { // email event not found OR it's won't be send under given circumstances return false; } // additional parameters from kApplication->EmailEvent $send_params = $event->getEventParam('DirectSendParams'); // 1. get information about message sender and recipient $recipients = $this->GetMessageRecipients($event); list ($from_id, $to_id) = $recipients; list ($from_email, $from_name) = $this->GetRecipientInfo($from_id, 'from', $send_params); list ($to_email, $to_name) = $this->GetRecipientInfo($to_id, 'to', $send_params); // 2. prepare message to be sent $message_language = $this->_getSendLanguage($send_params); $message_template = $this->GetMessageBody($event, $message_language); if (!trim($message_template)) { trigger_error('Message template is empty', E_USER_WARNING); return false; } list ($message_headers, $message_body) = $this->ParseMessageBody($message_template, $send_params); if (!trim($message_body)) { trigger_error('Message template is empty after parsing', E_USER_WARNING); return false; } // 3. set headers & send message $esender =& $this->Application->recallObject('EmailSender'); /* @var $esender kEmailSendingHelper */ $esender->SetFrom($from_email, $from_name); $esender->AddTo($to_email, $to_name); $message_subject = isset($message_headers['Subject']) ? $message_headers['Subject'] : 'Mail message'; $esender->SetSubject($message_subject); if ($this->Application->isDebugMode()) { // set special header with event name, so it will be easier to determite what's actually was received $message_headers['X-Event-Name'] = $email_event_name . ' - ' . ($event->getEventParam('EmailEventType') == EVENT_TYPE_ADMIN ? 'ADMIN' : 'USER'); } foreach ($message_headers as $header_name => $header_value) { $esender->SetEncodedHeader($header_name, $header_value); } $esender->CreateTextHtmlPart($message_body, $object->GetDBField('MessageType') == 'html'); $event->status = $esender->Deliver() ? erSUCCESS : erFAIL; if ($event->status == erSUCCESS) { // all keys, that are not used in email sending are written to log record $send_keys = Array ('from_email', 'from_name', 'to_email', 'to_name', 'message'); foreach ($send_keys as $send_key) { unset($send_params[$send_key]); } $fields_hash = Array ( 'fromuser' => $from_name.' ('.$from_email.')', 'addressto' => $to_name.' ('.$to_email.')', 'subject' => $message_subject, 'timestamp' => adodb_mktime(), 'event' => $email_event_name, 'EventParams' => serialize($send_params), ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'EmailLog'); } $this->Application->removeObject('u.email-from'); $this->Application->removeObject('u.email-to'); } function _getSendLanguage($send_params) { if ($send_params && array_key_exists('language_id', $send_params)) { return $send_params['language_id']; } return $this->Application->GetVar('m_lang'); } function _changeLanguage($language_id = null) { static $prev_language_id = null; if (!isset($language_id)) { // restore language $language_id = $prev_language_id; } $this->Application->SetVar('m_lang', $language_id); $language =& $this->Application->recallObject('lang.current'); /* @var $lang_object kDBItem */ $language->Load($language_id); $this->Application->Phrases->LanguageId = $language_id; $this->Application->Phrases->Phrases = Array(); $prev_language_id = $language_id; // for restoring it later } /** * Process emails from queue * * @param kEvent $event * @todo Move to MailingList */ function OnProcessEmailQueue(&$event) { $deliver_count = $event->getEventParam('deliver_count'); if ($deliver_count === false) { $deliver_count = $this->Application->ConfigValue('MailingListSendPerStep'); if ($deliver_count === false) { $deliver_count = 10; // 10 emails per script run (if not specified directly) } } $processing_type = $this->Application->GetVar('type'); if ($processing_type = 'return_progress') { $email_queue_progress = $this->Application->RecallVar('email_queue_progress'); if ($email_queue_progress === false) { $emails_sent = 0; $sql = 'SELECT COUNT(*) FROM ' . TABLE_PREFIX . 'EmailQueue WHERE (SendRetries < 5) AND (LastSendRetry < ' . strtotime('-2 hours') . ')'; $total_emails = $this->Conn->GetOne($sql); $this->Application->StoreVar('email_queue_progress', $emails_sent.':'.$total_emails); } else { list ($emails_sent, $total_emails) = explode(':', $email_queue_progress); } } $sql = 'SELECT * FROM '.TABLE_PREFIX.'EmailQueue WHERE (SendRetries < 5) AND (LastSendRetry < ' . strtotime('-2 hours') . ') LIMIT 0,' . $deliver_count; $messages = $this->Conn->Query($sql); $message_count = count($messages); if (!$message_count) { // no messages left to send in queue if ($processing_type = 'return_progress') { $this->Application->RemoveVar('email_queue_progress'); $this->Application->Redirect($this->Application->GetVar('finish_template')); } return ; } $mailing_list_helper =& $this->Application->recallObject('MailingListHelper'); /* @var $mailing_list_helper MailingListHelper */ $mailing_list_helper->processQueue($messages); if ($processing_type = 'return_progress') { $emails_sent += $message_count; if ($emails_sent >= $total_emails) { $this->Application->RemoveVar('email_queue_progress'); $this->Application->Redirect($this->Application->GetVar('finish_template')); } $this->Application->StoreVar('email_queue_progress', $emails_sent.':'.$total_emails); $event->status = erSTOP; echo ($emails_sent / $total_emails) * 100; } } /** * Prefills module dropdown * * @param kEvent $event */ function OnAfterConfigRead(&$event) { parent::OnAfterConfigRead($event); $options = Array ('Core:Users' => 'Core - Users', 'Core:Category' => 'Core - Categories'); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { if (($module_name == 'In-Portal') || ($module_name == 'Core')) { continue; } $options[$module_name] = $module_name; } $fields = $this->Application->getUnitOption($event->Prefix, 'Fields'); $fields['Module']['options'] = $options; $this->Application->setUnitOption($event->Prefix, 'Fields', $fields); // make grid on "Email Events" tab in "Regional" section show columns of editable language, not admin language $language_id = $this->Application->GetVar('lang_id'); if ($language_id) { $grids = $this->Application->getUnitOption($event->Prefix, 'Grids'); // used by column picker to track column position $grids['Default']['Fields']['Description']['formatter_renamed'] = true; array_rename_key($grids['Default']['Fields'], 'Description', 'l' . $language_id . '_Description'); $this->Application->setUnitOption($event->Prefix, 'Grids', $grids); } } }