Index: branches/5.2.x/core/units/email_events/email_events_event_handler.php =================================================================== diff -u -N -r15145 -r15215 --- branches/5.2.x/core/units/email_events/email_events_event_handler.php (.../email_events_event_handler.php) (revision 15145) +++ branches/5.2.x/core/units/email_events/email_events_event_handler.php (.../email_events_event_handler.php) (revision 15215) @@ -1,6 +1,6 @@ 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; - } - - /** - * Processes email sender - * - * @param kEvent $event - * @param Array $direct_params - */ - function _processSender($event, $direct_params = Array ()) - { - $this->Application->removeObject('u.email-from'); - - $object =& $this->_getEmailEvent($event); - /* @var $object kDBItem */ - - $email = $name = ''; - - // set defaults from event - if ($object->GetDBField('CustomSender')) { - $address = $object->GetDBField('SenderAddress'); - $address_type = $object->GetDBField('SenderAddressType'); - - switch ($address_type) { - case EmailEvent::ADDRESS_TYPE_EMAIL: - $email = $address; - break; - - case EmailEvent::ADDRESS_TYPE_USER: - $sql = 'SELECT FirstName, LastName, Email, PortalUserId - FROM ' . TABLE_PREFIX . 'Users - WHERE Username = ' . $this->Conn->qstr($address); - $user_info = $this->Conn->GetRow($sql); - - if ($user_info) { - // user still exists - $email = $user_info['Email']; - $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); - - $user = $this->Application->recallObject('u.email-from', null, Array('skip_autoload' => true)); - /* @var $user UsersItem */ - - $user->Load($user_info['PortalUserId']); - } - break; - } - - if ($object->GetDBField('SenderName')) { - $name = $object->GetDBField('SenderName'); - } - } - - // update with custom data given during event execution - if (array_key_exists('from_email', $direct_params)) { - $email = $direct_params['from_email']; - } - - if (array_key_exists('from_name', $direct_params)) { - $name = $direct_params['from_name']; - } - - // still nothing, set defaults - if (!$email) { - $email = $this->Application->ConfigValue('DefaultEmailSender'); - } - - if (!$name) { - $name = strip_tags( $this->Application->ConfigValue('Site_Name') ); - } - - $esender = $this->Application->recallObject('EmailSender'); - /* @var $esender kEmailSendingHelper */ - - $esender->SetFrom($email, $name); - - return Array ($email, $name); - } - - /** - * Processes email recipients - * - * @param kEvent $event - * @param Array $direct_params - * @return Array - */ - function _processRecipients($event, &$direct_params = Array ()) - { - $this->Application->removeObject('u.email-to'); - - $object =& $this->_getEmailEvent($event); - /* @var $object kDBItem */ - - $to_email = $to_name = ''; - $all_recipients = Array (); - - $this->_addRecipientsFromXml($all_recipients, $object->GetDBField('Recipients')); - - if ( !array_key_exists(EmailEvent::RECIPIENT_TYPE_TO, $all_recipients) ) { - $all_recipients[EmailEvent::RECIPIENT_TYPE_TO] = Array (); - } - - // remove all "To" recipients, when not allowed - $overwrite_to_email = array_key_exists('overwrite_to_email', $direct_params) ? $direct_params['overwrite_to_email'] : false; - - if ( !$object->GetDBField('CustomRecipient') || $overwrite_to_email ) { - $all_recipients[EmailEvent::RECIPIENT_TYPE_TO] = Array (); - } - - // update with custom data given during event execution (user_id) - $to_user_id = $event->getEventParam('EmailEventToUserId'); - - if ( $to_user_id > 0 ) { - $language_field = $event->getEventParam('EmailEventType') == EmailEvent::EVENT_TYPE_FRONTEND ? 'FrontLanguage' : 'AdminLanguage'; - - $sql = 'SELECT FirstName, LastName, Email, ' . $language_field . ' AS Language - FROM ' . TABLE_PREFIX . 'Users - WHERE PortalUserId = ' . $to_user_id; - $user_info = $this->Conn->GetRow($sql); - - if ( $user_info ) { - $add_recipient = Array ( - 'RecipientAddressType' => EmailEvent::ADDRESS_TYPE_EMAIL, - 'RecipientAddress' => $user_info['Email'], - 'RecipientName' => trim($user_info['FirstName'] . ' ' . $user_info['LastName']), - ); - - if ( $user_info['Language'] && !isset($direct_params['language_id']) ) { - $direct_params['language_id'] = $user_info['Language']; - } - - array_unshift($all_recipients[EmailEvent::RECIPIENT_TYPE_TO], $add_recipient); - - $user = $this->Application->recallObject('u.email-to', null, Array('skip_autoload' => true)); - /* @var $user UsersItem */ - - $user->Load($to_user_id); - } - } - elseif ( is_numeric($to_user_id) ) { - // recipient is system user with negative ID (root, guest, etc.) -> send to admin - $this->_addDefaultRecipient($all_recipients); - } - - // update with custom data given during event execution (email + name) - $add_recipient = Array (); - - if ( array_key_exists('to_email', $direct_params) ) { - $add_recipient['RecipientName'] = ''; - $add_recipient['RecipientAddressType'] = EmailEvent::ADDRESS_TYPE_EMAIL; - $add_recipient['RecipientAddress'] = $direct_params['to_email']; - } - - if ( array_key_exists('to_name', $direct_params) ) { - $add_recipient['RecipientName'] = $direct_params['to_name']; - } - - if ( $add_recipient ) { - array_unshift($all_recipients[EmailEvent::RECIPIENT_TYPE_TO], $add_recipient); - } - - if ( ($object->GetDBField('Type') == EmailEvent::EVENT_TYPE_ADMIN) && !$all_recipients[EmailEvent::RECIPIENT_TYPE_TO] ) { - // admin email event without direct recipient -> send to admin - $this->_addDefaultRecipient($all_recipients); - } - - $esender = $this->Application->recallObject('EmailSender'); - /* @var $esender kEmailSendingHelper */ - - $header_mapping = Array ( - EmailEvent::RECIPIENT_TYPE_TO => 'To', - EmailEvent::RECIPIENT_TYPE_CC => 'Cc', - EmailEvent::RECIPIENT_TYPE_BCC => 'Bcc', - ); - - $default_email = $this->Application->ConfigValue('DefaultEmailSender'); - - foreach ($all_recipients as $recipient_type => $recipients) { - // add recipients to email - $pairs = Array (); - - foreach ($recipients as $recipient) { - $address = $recipient['RecipientAddress']; - $address_type = $recipient['RecipientAddressType']; - $recipient_name = $recipient['RecipientName']; - - switch ($address_type) { - case EmailEvent::ADDRESS_TYPE_EMAIL: - $pairs[] = Array ('email' => $address, 'name' => $recipient_name); - break; - - case EmailEvent::ADDRESS_TYPE_USER: - $sql = 'SELECT FirstName, LastName, Email - FROM ' . TABLE_PREFIX . 'Users - WHERE Username = ' . $this->Conn->qstr($address); - $user_info = $this->Conn->GetRow($sql); - - if ($user_info) { - // user still exists - $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); - - $pairs[] = Array ( - 'email' => $user_info['Email'], - 'name' => $name ? $name : $recipient_name, - ); - } - break; - - case EmailEvent::ADDRESS_TYPE_GROUP: - $sql = 'SELECT u.FirstName, u.LastName, u.Email - FROM ' . TABLE_PREFIX . 'UserGroups g - JOIN ' . TABLE_PREFIX . 'UserGroupRelations ug ON ug.GroupId = g.GroupId - JOIN ' . TABLE_PREFIX . 'Users u ON u.PortalUserId = ug.PortalUserId - WHERE g.Name = ' . $this->Conn->qstr($address); - $users = $this->Conn->Query($sql); - - foreach ($users as $user_info) { - $name = trim($user_info['FirstName'] . ' ' . $user_info['LastName']); - - $pairs[] = Array ( - 'email' => $user_info['Email'], - 'name' => $name ? $name : $recipient_name, - ); - } - break; - } - } - - if ( !$pairs ) { - continue; - } - - if ( $recipient_type == EmailEvent::RECIPIENT_TYPE_TO ) { - $to_email = $pairs[0]['email'] ? $pairs[0]['email'] : $default_email; - $to_name = $pairs[0]['name'] ? $pairs[0]['name'] : $to_email; - } - - $header_name = $header_mapping[$recipient_type]; - - foreach ($pairs as $pair) { - $email = $pair['email'] ? $pair['email'] : $default_email; - $name = $pair['name'] ? $pair['name'] : $email; - - $esender->AddRecipient($header_name, $email, $name); - } - } - - return Array ($to_email, $to_name); - } - - /** - * This is default recipient, when we can't determine actual one - * - * @param Array $recipients - * @return void - */ - function _addDefaultRecipient(&$recipients) - { - $xml = $this->Application->ConfigValue('DefaultEmailRecipients'); - - if ( !$this->_addRecipientsFromXml($recipients, $xml) ) { - $recipient = Array ( - 'RecipientName' => $this->Application->ConfigValue('DefaultEmailSender'), - 'RecipientAddressType' => EmailEvent::ADDRESS_TYPE_EMAIL, - 'RecipientAddress' => $this->Application->ConfigValue('DefaultEmailSender'), - ); - - array_unshift($recipients[EmailEvent::RECIPIENT_TYPE_TO], $recipient); - } - } - - /** - * Adds multiple recipients from an XML - * - * @param Array $recipients - * @param string $xml - * @return bool - * @access protected - */ - protected function _addRecipientsFromXml(&$recipients, $xml) - { - if ( !$xml ) { - return false; - } - - $minput_helper = $this->Application->recallObject('MInputHelper'); - /* @var $minput_helper MInputHelper */ - - // group recipients by type - $records = $minput_helper->parseMInputXML($xml); - - foreach ($records as $record) { - $recipient_type = $record['RecipientType']; - - if ( !array_key_exists($recipient_type, $recipients) ) { - $recipients[$recipient_type] = Array (); - } - - $recipients[$recipient_type][] = $record; - } - - return true; - } - - /** - * Returns email event message by ID (headers & body in one piece) - * - * @param kEvent $event - * @param int $language_id - * @return string - */ - 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($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 = Array ()) - { - $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($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 should be sent - * - * @param kEvent $event - * @return void - * @access protected - */ - protected function OnEmailEvent($event) - { - $email_event_name = $event->getEventParam('EmailEventName'); - if ( strpos($email_event_name, '_') !== false ) { - throw new Exception('Invalid email event name ' . $email_event_name . '. Use only UPPERCASE characters and dots as email event names'); - } - - $object =& $this->_getEmailEvent($event); - - if ( !is_object($object) ) { - // email event not found OR it's won't be send under given circumstances - return ; - } - - // additional parameters from kApplication->EmailEvent - $send_params = $event->getEventParam('DirectSendParams'); - - // 1. get information about message sender and recipient - list ($from_email, $from_name) = $this->_processSender($event, $send_params); - list ($to_email, $to_name) = $this->_processRecipients($event, $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 ; - } - - 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 ; - } - - // 3. set headers & send message - $esender = $this->Application->recallObject('EmailSender'); - /* @var $esender kEmailSendingHelper */ - - $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 determine what's actually was received - $message_headers['X-Event-Name'] = $email_event_name . ' - ' . ($object->GetDBField('Type') == EmailEvent::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'); - - $log_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( $this->removeSendingParams($send_params) ), - ); - - $esender->setLogData($log_fields_hash); - $event->status = $esender->Deliver() ? kEvent::erSUCCESS : kEvent::erFAIL; - } - - /** - * Removes parameters, used during e-mail sending - * - * @param Array $params - * @return Array - * @access protected - */ - protected function removeSendingParams($params) - { - $send_keys = Array ('from_email', 'from_name', 'to_email', 'to_name', 'message'); - - foreach ($send_keys as $send_key) { - unset($params[$send_key]); - } - - return $params; - } - - function _getSendLanguage($send_params) - { - if (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 $language LanguagesItem */ - - $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