Skip to content

Instantly share code, notes, and snippets.

@xurizaemon
Created April 25, 2012 21:22
Show Gist options
  • Save xurizaemon/2493563 to your computer and use it in GitHub Desktop.
Save xurizaemon/2493563 to your computer and use it in GitHub Desktop.
CRM_Mailing_BAO_Mailing::getRecipients()
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.1 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2011 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2011
* $Id$
*
*/
require_once 'Mail/mime.php';
class CRM_Mailing_BAO_Mailing extends CRM_Mailing_DAO_Mailing
{
/**
* An array that holds the complete templates
* including any headers or footers that need to be prepended
* or appended to the body
*/
private $preparedTemplates = null;
/**
* An array that holds the complete templates
* including any headers or footers that need to be prepended
* or appended to the body
*/
private $templates = null;
/**
* An array that holds the tokens that are specifically found in our text and html bodies
*/
private $tokens = null;
/**
* An array that holds the tokens that are specifically found in our text and html bodies
*/
private $flattenedTokens = null;
/**
* The header associated with this mailing
*/
private $header = null;
/**
* The footer associated with this mailing
*/
private $footer = null;
/**
* The HTML content of the message
*/
private $html = null;
/**
* The text content of the message
*/
private $text = null;
/**
* Cached BAO for the domain
*/
private $_domain = null;
/**
* class constructor
*/
function __construct( )
{
parent::__construct( );
}
function &getRecipientsCount($job_id, $mailing_id = null, $mode = null)
{
// need this for backward compatibility, so we can get count for old mailings
// please do not use this function if possible
$eq = self::getRecipients($job_id, $mailing_id);
return $eq->N;
}
// note that $job_id is used only as a variable in the temp table construction
// and does not play a role in the queries generated
function &getRecipients($job_id, $mailing_id = null,
$offset = NULL, $limit = NULL,
$storeRecipients = false,
$dedupeEmail = false,
$mode = null)
{
$mailingGroup = new CRM_Mailing_DAO_Group();
$mailing = CRM_Mailing_BAO_Mailing::getTableName();
$job = CRM_Mailing_BAO_Job::getTableName();
$mg = CRM_Mailing_DAO_Group::getTableName();
$eq = CRM_Mailing_Event_DAO_Queue::getTableName();
$ed = CRM_Mailing_Event_DAO_Delivered::getTableName();
$eb = CRM_Mailing_Event_DAO_Bounce::getTableName();
$email = CRM_Core_DAO_Email::getTableName();
if ( $mode == 'sms' ) {
$phone = CRM_Core_DAO_Phone::getTableName();
}
$contact = CRM_Contact_DAO_Contact::getTableName();
$group = CRM_Contact_DAO_Group::getTableName();
$g2contact = CRM_Contact_DAO_GroupContact::getTableName();
/* Create a temp table for contact exclusion */
$mailingGroup->query(
"CREATE TEMPORARY TABLE X_$job_id
(contact_id int primary key)
ENGINE=HEAP"
);
/* Add all the members of groups excluded from this mailing to the temp
* table */
$excludeSubGroup =
"INSERT INTO X_$job_id (contact_id)
SELECT DISTINCT $g2contact.contact_id
FROM $g2contact
INNER JOIN $mg
ON $g2contact.group_id = $mg.entity_id AND $mg.entity_table = '$group'
WHERE
$mg.mailing_id = {$mailing_id}
AND $g2contact.status = 'Added'
AND $mg.group_type = 'Exclude'";
$mailingGroup->query($excludeSubGroup);
/* Add all unsubscribe members of base group from this mailing to the temp
* table */
$unSubscribeBaseGroup =
"INSERT INTO X_$job_id (contact_id)
SELECT DISTINCT $g2contact.contact_id
FROM $g2contact
INNER JOIN $mg
ON $g2contact.group_id = $mg.entity_id AND $mg.entity_table = '$group'
WHERE
$mg.mailing_id = {$mailing_id}
AND $g2contact.status = 'Removed'
AND $mg.group_type = 'Base'";
$mailingGroup->query($unSubscribeBaseGroup);
/* Add all the (intended) recipients of an excluded prior mailing to
* the temp table */
$excludeSubMailing =
"INSERT IGNORE INTO X_$job_id (contact_id)
SELECT DISTINCT $eq.contact_id
FROM $eq
INNER JOIN $job
ON $eq.job_id = $job.id
INNER JOIN $mg
ON $job.mailing_id = $mg.entity_id AND $mg.entity_table = '$mailing'
WHERE
$mg.mailing_id = {$mailing_id}
AND $mg.group_type = 'Exclude'";
$mailingGroup->query($excludeSubMailing);
// get all the saved searches AND hierarchical groups
// and load them in the cache
$sql = "
SELECT $group.id, $group.cache_date, $group.saved_search_id, $group.children
FROM $group
INNER JOIN $mg ON $mg.entity_id = $group.id
WHERE $mg.entity_table = '$group'
AND $mg.group_type = 'Exclude'
AND $mg.mailing_id = {$mailing_id}
AND ( saved_search_id != 0
OR saved_search_id IS NOT NULL
OR children IS NOT NULL )
";
$groupDAO = CRM_Core_DAO::executeQuery( $sql );
while ( $groupDAO->fetch( ) ) {
if ( $groupDAO->cache_date == null ) {
CRM_Contact_BAO_GroupContactCache::load( $groupDAO );
}
$smartGroupExclude = "
INSERT IGNORE INTO X_$job_id (contact_id)
SELECT c.contact_id
FROM civicrm_group_contact_cache c
WHERE c.group_id = {$groupDAO->id}
";
$mailingGroup->query($smartGroupExclude);
}
$tempColumn = 'email_id';
if ( $mode == 'sms' ) {
$tempColumn = 'phone_id';
}
/* Get all the group contacts we want to include */
$mailingGroup->query(
"CREATE TEMPORARY TABLE I_$job_id
($tempColumn int, contact_id int primary key)
ENGINE=HEAP"
);
/* Get the group contacts, but only those which are not in the
* exclusion temp table */
$query = "REPLACE INTO I_$job_id (email_id, contact_id)
SELECT DISTINCT $email.id as email_id,
$contact.id as contact_id
FROM $email
INNER JOIN $contact
ON $email.contact_id = $contact.id
INNER JOIN $g2contact
ON $contact.id = $g2contact.contact_id
INNER JOIN $mg
ON $g2contact.group_id = $mg.entity_id
AND $mg.entity_table = '$group'
LEFT JOIN X_$job_id
ON $contact.id = X_$job_id.contact_id
WHERE
($mg.group_type = 'Include')
AND $mg.search_id IS NULL
AND $g2contact.status = 'Added'
AND $contact.do_not_email = 0
AND $contact.is_opt_out = 0
AND $contact.is_deceased = 0
AND ($email.is_bulkmail = 1 OR $email.is_primary = 1)
AND $email.email IS NOT NULL
AND $email.email != ''
AND $email.on_hold = 0
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null
ORDER BY $email.is_bulkmail";
if ( $mode == 'sms' ) {
$phoneTypes = CRM_Core_PseudoConstant::phoneType();
$phoneTypes = array_flip($phoneTypes);
$query = "REPLACE INTO I_$job_id (phone_id, contact_id)
SELECT DISTINCT $phone.id as phone_id,
$contact.id as contact_id
FROM $phone
INNER JOIN $contact
ON $phone.contact_id = $contact.id
INNER JOIN $g2contact
ON $contact.id = $g2contact.contact_id
INNER JOIN $mg
ON $g2contact.group_id = $mg.entity_id
AND $mg.entity_table = '$group'
LEFT JOIN X_$job_id
ON $contact.id = X_$job_id.contact_id
WHERE
($mg.group_type = 'Include')
AND $mg.search_id IS NULL
AND $g2contact.status = 'Added'
AND $contact.do_not_sms = 0
AND $contact.is_opt_out = 0
AND $contact.is_deceased = 0
AND $phone.phone_type_id = {$phoneTypes['Mobile']}
AND $phone.phone IS NOT NULL
AND $phone.phone != ''
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null";
}
$mailingGroup->query($query);
/* Query prior mailings */
$query = "REPLACE INTO I_$job_id (email_id, contact_id)
SELECT DISTINCT $email.id as email_id,
$contact.id as contact_id
FROM $email
INNER JOIN $contact
ON $email.contact_id = $contact.id
INNER JOIN $eq
ON $eq.contact_id = $contact.id
INNER JOIN $job
ON $eq.job_id = $job.id
INNER JOIN $mg
ON $job.mailing_id = $mg.entity_id AND $mg.entity_table = '$mailing'
LEFT JOIN X_$job_id
ON $contact.id = X_$job_id.contact_id
WHERE
($mg.group_type = 'Include')
AND $contact.do_not_email = 0
AND $contact.is_opt_out = 0
AND $contact.is_deceased = 0
AND ($email.is_bulkmail = 1 OR $email.is_primary = 1)
AND $email.on_hold = 0
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null
ORDER BY $email.is_bulkmail";
if ( $mode == 'sms' ) {
$query =
"REPLACE INTO I_$job_id (phone_id, contact_id)
SELECT DISTINCT $phone.id as phone_id,
$contact.id as contact_id
FROM $phone
INNER JOIN $contact
ON $phone.contact_id = $contact.id
INNER JOIN $eq
ON $eq.contact_id = $contact.id
INNER JOIN $job
ON $eq.job_id = $job.id
INNER JOIN $mg
ON $job.mailing_id = $mg.entity_id AND $mg.entity_table = '$mailing'
LEFT JOIN X_$job_id
ON $contact.id = X_$job_id.contact_id
WHERE
($mg.group_type = 'Include')
AND $contact.do_not_sms = 0
AND $contact.is_opt_out = 0
AND $contact.is_deceased = 0
AND $phone.phone_type_id = {$phoneTypes['Mobile']}
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null";
}
$mailingGroup->query( $query );
$sql = "
SELECT $group.id, $group.cache_date, $group.saved_search_id, $group.children
FROM $group
INNER JOIN $mg ON $mg.entity_id = $group.id
WHERE $mg.entity_table = '$group'
AND $mg.group_type = 'Include'
AND $mg.search_id IS NULL
AND $mg.mailing_id = {$mailing_id}
AND ( saved_search_id != 0
OR saved_search_id IS NOT NULL
OR children IS NOT NULL )
";
$groupDAO = CRM_Core_DAO::executeQuery( $sql );
while ( $groupDAO->fetch( ) ) {
if ( $groupDAO->cache_date == null ) {
CRM_Contact_BAO_GroupContactCache::load( $groupDAO );
}
$smartGroupInclude = "
INSERT IGNORE INTO I_$job_id (email_id, contact_id)
SELECT e.id as email_id, c.id as contact_id
FROM civicrm_contact c
INNER JOIN civicrm_email e ON e.contact_id = c.id
INNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = c.id
LEFT JOIN X_$job_id ON X_$job_id.contact_id = c.id
WHERE gc.group_id = {$groupDAO->id}
AND c.do_not_email = 0
AND c.is_opt_out = 0
AND c.is_deceased = 0
AND (e.is_bulkmail = 1 OR e.is_primary = 1)
AND e.on_hold = 0
AND X_$job_id.contact_id IS null
ORDER BY e.is_bulkmail
";
if ( $mode == 'sms' ) {
$smartGroupInclude = "
INSERT IGNORE INTO I_$job_id (phone_id, contact_id)
SELECT p.id as phone_id, c.id as contact_id
FROM civicrm_contact c
INNER JOIN civicrm_phone e ON p.contact_id = c.id
INNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = c.id
LEFT JOIN X_$job_id ON X_$job_id.contact_id = c.id
WHERE gc.group_id = {$groupDAO->id}
AND c.do_not_sms = 0
AND c.is_opt_out = 0
AND c.is_deceased = 0
AND p.phone_type_id = {$phoneTypes['Mobile']}
AND X_$job_id.contact_id IS null";
}
$mailingGroup->query($smartGroupInclude);
}
/**
* Construct the filtered search queries
*/
$query = "
SELECT search_id, search_args, entity_id
FROM $mg
WHERE $mg.search_id IS NOT NULL
AND $mg.mailing_id = {$mailing_id}
";
$dao = CRM_Core_DAO::executeQuery( $query );
while ( $dao->fetch( ) ) {
$customSQL = CRM_Contact_BAO_SearchCustom::civiMailSQL( $dao->search_id,
$dao->search_args,
$dao->entity_id );
$query = "REPLACE INTO I_$job_id ({$tempColumn}, contact_id)
$customSQL";
$mailingGroup->query($query);
}
/* Get the emails with only location override */
$query = "REPLACE INTO I_$job_id (email_id, contact_id)
SELECT DISTINCT $email.id as local_email_id,
$contact.id as contact_id
FROM $email
INNER JOIN $contact
ON $email.contact_id = $contact.id
INNER JOIN $g2contact
ON $contact.id = $g2contact.contact_id
INNER JOIN $mg
ON $g2contact.group_id = $mg.entity_id
LEFT JOIN X_$job_id
ON $contact.id = X_$job_id.contact_id
WHERE
$mg.entity_table = '$group'
AND $mg.group_type = 'Include'
AND $g2contact.status = 'Added'
AND $contact.do_not_email = 0
AND $contact.is_opt_out = 0
AND $contact.is_deceased = 0
AND ($email.is_bulkmail = 1 OR $email.is_primary = 1)
AND $email.on_hold = 0
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null
ORDER BY $email.is_bulkmail";
if ( $mode == "sms" ) {
$query =
"REPLACE INTO I_$job_id (phone_id, contact_id)
SELECT DISTINCT $phone.id as phone_id,
$contact.id as contact_id
FROM $phone
INNER JOIN $contact
ON $phone.contact_id = $contact.id
INNER JOIN $g2contact
ON $contact.id = $g2contact.contact_id
INNER JOIN $mg
ON $g2contact.group_id = $mg.entity_id
LEFT JOIN X_$job_id
ON $contact.id = X_$job_id.contact_id
WHERE
$mg.entity_table = '$group'
AND $mg.group_type = 'Include'
AND $g2contact.status = 'Added'
AND $contact.do_not_sms = 0
AND $contact.is_opt_out = 0
AND $contact.is_deceased = 0
AND $phone.phone_type_id = {$phoneTypes['Mobile']}
AND $mg.mailing_id = {$mailing_id}
AND X_$job_id.contact_id IS null";
}
$mailingGroup->query($query);
$results = array();
$eq = new CRM_Mailing_Event_BAO_Queue();
list( $aclFrom, $aclWhere ) = CRM_Contact_BAO_Contact_Permission::cacheClause( );
$aclWhere = $aclWhere ? "WHERE {$aclWhere}" : '';
$limitString = null;
if ( $limit && $offset !== null) {
$limitString = "LIMIT $offset, $limit";
}
if ( $storeRecipients &&
$mailing_id ) {
$sql = "
DELETE
FROM civicrm_mailing_recipients
WHERE mailing_id = %1
";
$params = array( 1 => array( $mailing_id, 'Integer' ) );
CRM_Core_DAO::executeQuery( $sql, $params );
// CRM-3975
$groupBy = $groupJoin = '';
if ( $dedupeEmail ) {
$groupJoin = " INNER JOIN civicrm_email e ON e.id = i.email_id";
$groupBy = " GROUP BY e.email ";
}
$sql = "
INSERT INTO civicrm_mailing_recipients ( mailing_id, contact_id, {$tempColumn} )
SELECT %1, i.contact_id, i.{$tempColumn}
FROM civicrm_contact contact_a
INNER JOIN I_$job_id i ON contact_a.id = i.contact_id
$groupJoin
{$aclFrom}
{$aclWhere}
$groupBy
ORDER BY i.contact_id, i.{$tempColumn}
";
CRM_Core_DAO::executeQuery( $sql, $params );
// if we need to add all emails marked bulk, do it as a post filter
// on the mailing recipients table
if ( CRM_Core_BAO_Email::isMultipleBulkMail( ) ) {
self::addMultipleEmails( $mailing_id );
}
}
/* Delete the temp table */
$mailingGroup->reset();
$mailingGroup->query("DROP TEMPORARY TABLE X_$job_id");
$mailingGroup->query("DROP TEMPORARY TABLE I_$job_id");
return $eq;
}
private function _getMailingGroupIds( $type = 'Include' )
{
$mailingGroup = new CRM_Mailing_DAO_Group();
$group = CRM_Contact_DAO_Group::getTableName();
if ( ! isset( $this->id ) ) {
// we're just testing tokens, so return any group
$query = "SELECT id AS entity_id
FROM $group
ORDER BY id
LIMIT 1";
} else {
$query = "SELECT entity_id
FROM $mg
WHERE mailing_id = {$this->id}
AND group_type = '$type'
AND entity_table = '$group'";
}
$mailingGroup->query( $query );
$groupIds = array( );
while ( $mailingGroup->fetch( ) ) {
$groupIds[] = $mailingGroup->entity_id;
}
return $groupIds;
}
/**
*
* Returns the regex patterns that are used for preparing the text and html templates
*
* @access private
*
**/
private function &getPatterns($onlyHrefs = false)
{
$patterns = array();
$protos = '(https?|ftp)';
$letters = '\w';
$gunk = '\{\}/#~:.?+=&;%@!\,\-';
$punc = '.:?\-';
$any = "{$letters}{$gunk}{$punc}";
if ( $onlyHrefs ) {
$pattern = "\\bhref[ ]*=[ ]*([\"'])?(($protos:[$any]+?(?=[$punc]*[^$any]|$)))([\"'])?";
} else {
$pattern = "\\b($protos:[$any]+?(?=[$punc]*[^$any]|$))";
}
$patterns[] = $pattern;
$patterns[] = '\\\\\{\w+\.\w+\\\\\}|\{\{\w+\.\w+\}\}';
$patterns[] = '\{\w+\.\w+\}';
$patterns = '{'.join('|',$patterns).'}im';
return $patterns;
}
/**
* returns an array that denotes the type of token that we are dealing with
* we use the type later on when we are doing a token replcement lookup
*
* @param string $token The token for which we will be doing adata lookup
*
* @return array $funcStruct An array that holds the token itself and the type.
* the type will tell us which function to use for the data lookup
* if we need to do a lookup at all
*/
function &getDataFunc($token)
{
static $_categories = null;
static $_categoryString = null;
if ( ! $_categories ) {
$_categories = array( 'domain' => null,
'action' => null,
'mailing' => null,
'contact' => null );
CRM_Utils_Hook::tokens( $_categories );
$_categoryString = implode( '|', array_keys( $_categories ) );
}
$funcStruct = array('type' => null,'token' => $token);
$matches = array();
if ( ( preg_match('/^href/i',$token) || preg_match('/^http/i',$token) ) ) {
// it is a url so we need to check to see if there are any tokens embedded
// if so then call this function again to get the token dataFunc
// and assign the type 'embedded' so that the data retrieving function
// will know what how to handle this token.
if ( preg_match_all('/(\{\w+\.\w+\})/', $token, $matches) ) {
$funcStruct['type'] = 'embedded_url';
$funcStruct['embed_parts'] = $funcStruct['token'] = array( );
foreach ( $matches[1] as $match ) {
$preg_token = '/'.preg_quote($match,'/').'/';
$list = preg_split($preg_token,$token,2);
$funcStruct['embed_parts'][] = $list[0];
$token = $list[1];
$funcStruct['token'][] = $this->getDataFunc($match);
}
// fixed truncated url, CRM-7113
if ( $token ) {
$funcStruct['embed_parts'][] = $token;
}
} else {
$funcStruct['type'] = 'url';
}
} else if ( preg_match('/^\{(' . $_categoryString . ')\.(\w+)\}$/', $token, $matches) ) {
$funcStruct['type'] = $matches[1];
$funcStruct['token'] = $matches[2];
} else if(preg_match('/\\\\\{(\w+\.\w+)\\\\\}|\{\{(\w+\.\w+)\}\}/', $token, $matches) ) {
// we are an escaped token
// so remove the escape chars
$unescaped_token = preg_replace('/\{\{|\}\}|\\\\\{|\\\\\}/','',$matches[0]);
$funcStruct['token'] = '{'.$unescaped_token.'}';
}
return $funcStruct;
}
/**
*
* Prepares the text and html templates
* for generating the emails and returns a copy of the
* prepared templates
*
* @access private
*
**/
private function getPreparedTemplates( )
{
if ( !$this->preparedTemplates ) {
$patterns['html'] = $this->getPatterns(true);
$patterns['subject'] = $patterns['text'] = $this->getPatterns();
$templates = $this->getTemplates();
$this->preparedTemplates = array();
foreach (array('html','text', 'subject') as $key) {
if (!isset($templates[$key])) {
continue;
}
$matches = array();
$tokens = array();
$split_template = array();
$email = $templates[$key];
preg_match_all($patterns[$key],$email,$matches,PREG_PATTERN_ORDER);
foreach ($matches[0] as $idx => $token) {
$preg_token = '/'.preg_quote($token,'/').'/im';
list($split_template[],$email) = preg_split($preg_token,$email,2);
array_push($tokens, $this->getDataFunc($token));
}
if ($email) {
$split_template[] = $email;
}
$this->preparedTemplates[$key]['template'] = $split_template;
$this->preparedTemplates[$key]['tokens'] = $tokens;
}
}
return($this->preparedTemplates);
}
/**
*
* Retrieve a ref to an array that holds the email and text templates for this email
* assembles the complete template including the header and footer
* that the user has uploaded or declared (if they have dome that)
*
*
* @return array reference to an assoc array
* @access private
*
**/
private function &getTemplates( )
{
if (!$this->templates) {
$this->getHeaderFooter();
$this->templates = array( );
if ( $this->body_text ) {
$template = array();
if ( $this->header ) {
$template[] = $this->header->body_text;
}
$template[] = $this->body_text;
if ( $this->footer ) {
$template[] = $this->footer->body_text;
}
$this->templates['text'] = join("\n",$template);
}
if ( $this->body_html ) {
$template = array();
if ( $this->header ) {
$template[] = $this->header->body_html;
}
$template[] = $this->body_html;
if ( $this->footer ) {
$template[] = $this->footer->body_html;
}
$this->templates['html'] = join("\n",$template);
// this is where we create a text template from the html template if the text template did not exist
// this way we ensure that every recipient will receive an email even if the pref is set to text and the
// user uploads an html email only
if ( !$this->body_text ) {
$this->templates['text'] = CRM_Utils_String::htmlToText( $this->templates['html'] );
}
}
if ( $this->subject ) {
$template = array();
$template[] = $this->subject;
$this->templates['subject'] = join("\n",$template);
}
}
return $this->templates;
}
/**
*
* Retrieve a ref to an array that holds all of the tokens in the email body
* where the keys are the type of token and the values are ordinal arrays
* that hold the token names (even repeated tokens) in the order in which
* they appear in the body of the email.
*
* note: the real work is done in the _getTokens() function
*
* this function needs to have some sort of a body assigned
* either text or html for this to have any meaningful impact
*
* @return array reference to an assoc array
* @access public
*
**/
public function &getTokens( )
{
if (! $this->tokens) {
$this->tokens = array( 'html' => array(), 'text' => array(), 'subject' => array() );
if ($this->body_html) {
$this->_getTokens('html');
}
if ($this->body_text) {
$this->_getTokens('text');
}
if ($this->subject) {
$this->_getTokens('subject');
}
}
return $this->tokens;
}
/**
* Returns the token set for all 3 parts as one set. This allows it to be sent to the
* hook in one call and standardizes it across other token workflows
*
* @return array reference to an assoc array
* @access public
*
**/
public function &getFlattenedTokens( )
{
if ( ! $this->flattenedTokens ) {
$tokens = $this->getTokens( );
$this->flattenedTokens = CRM_Utils_Token::flattenTokens( $tokens );
}
return $this->flattenedTokens;
}
/**
*
* _getTokens parses out all of the tokens that have been
* included in the html and text bodies of the email
* we get the tokens and then separate them into an
* internal structure named tokens that has the same
* form as the static tokens property(?) of the CRM_Utils_Token class.
* The difference is that there might be repeated token names as we want the
* structures to represent the order in which tokens were found from left to right, top to bottom.
*
*
* @param str $prop name of the property that holds the text that we want to scan for tokens (html, text)
* @access private
* @return void
*/
private function _getTokens( $prop )
{
$templates = $this->getTemplates();
$newTokens = CRM_Utils_Token::getTokens( $templates[$prop] );
foreach ( $newTokens as $type => $names ) {
if ( ! isset( $this->tokens[$prop][$type] ) ) {
$this->tokens[$prop][$type] = array( );
}
foreach ( $names as $key => $name ) {
$this->tokens[$prop][$type][] = $name;
}
}
}
/**
* Generate an event queue for a test job
*
* @params array $params contains form values
* @return void
* @access public
*/
public function getTestRecipients($testParams)
{
if (array_key_exists($testParams['test_group'], CRM_Core_PseudoConstant::group())) {
$group = new CRM_Contact_DAO_Group();
$group->id = $testParams['test_group'];
$contacts = CRM_Contact_BAO_GroupContact::getGroupContacts($group);
foreach ($contacts as $contact) {
$query =
"SELECT DISTINCT civicrm_email.id AS email_id, civicrm_email.is_primary as is_primary,
civicrm_email.is_bulkmail as is_bulkmail
FROM civicrm_email
INNER JOIN civicrm_contact ON civicrm_email.contact_id = civicrm_contact.id
WHERE civicrm_email.is_bulkmail = 1
AND civicrm_contact.id = {$contact->contact_id}
AND civicrm_contact.do_not_email = 0
AND civicrm_contact.is_deceased = 0
AND civicrm_email.on_hold = 0
AND civicrm_contact.is_opt_out =0";
$dao = CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray);
if ($dao->fetch( ) ) {
$params = array(
'job_id' => $testParams['job_id'],
'email_id' => $dao->email_id,
'contact_id' => $contact->contact_id
);
$queue = CRM_Mailing_Event_BAO_Queue::create($params);
} else {
$query =
"SELECT DISTINCT civicrm_email.id AS email_id, civicrm_email.is_primary as is_primary,
civicrm_email.is_bulkmail as is_bulkmail
FROM civicrm_email
INNER JOIN civicrm_contact ON civicrm_email.contact_id = civicrm_contact.id
WHERE civicrm_email.is_primary = 1
AND civicrm_contact.id = {$contact->contact_id}
AND civicrm_contact.do_not_email =0
AND civicrm_contact.is_deceased = 0
AND civicrm_email.on_hold = 0
AND civicrm_contact.is_opt_out =0";
$dao = CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray);
if ($dao->fetch( ) ) {
$params = array(
'job_id' => $testParams['job_id'],
'email_id' => $dao->email_id,
'contact_id' => $contact->contact_id
);
$queue = CRM_Mailing_Event_BAO_Queue::create($params);
}
}
}
}
}
/**
* Retrieve the header and footer for this mailing
*
* @param void
* @return void
* @access private
*/
private function getHeaderFooter()
{
if (!$this->header and $this->header_id) {
$this->header = new CRM_Mailing_BAO_Component();
$this->header->id = $this->header_id;
$this->header->find(true);
$this->header->free( );
}
if (!$this->footer and $this->footer_id) {
$this->footer = new CRM_Mailing_BAO_Component();
$this->footer->id = $this->footer_id;
$this->footer->find(true);
$this->footer->free( );
}
}
/**
* Given and array of headers and a prefix, job ID, event queue ID, and hash,
* add a Message-ID header if needed.
*
* i.e. if the global includeMessageId is set and there isn't already a
* Message-ID in the array.
* The message ID is structured the same way as a verp. However no interpretation
* is placed on the values received, so they do not need to follow the verp
* convention.
*
* @param array $headers Array of message headers to update, in-out
* @param string $prefix Prefix for the message ID, use same prefixes as verp
* wherever possible
* @param string $job_id Job ID component of the generated message ID
* @param string $event_queue_id Event Queue ID component of the generated message ID
* @param string $hash Hash component of the generated message ID.
* @return void
*/
static function addMessageIdHeader ( &$headers, $prefix, $job_id, $event_queue_id, $hash )
{
$config = CRM_Core_Config::singleton( );
$localpart = CRM_Core_BAO_MailSettings::defaultLocalpart();
$emailDomain = CRM_Core_BAO_MailSettings::defaultDomain();
$includeMessageId = CRM_Core_BAO_MailSettings::includeMessageId();
if ( $includeMessageId && ( ! array_key_exists ( 'Message-ID', $headers ) ) ) {
$headers['Message-ID'] = '<' .
implode($config->verpSeparator,
array(
$localpart . $prefix,
$job_id,
$event_queue_id,
$hash
)
) . "@{$emailDomain}>";
}
}
/**
* static wrapper for getting verp and urls
*
* @param int $job_id ID of the Job associated with this message
* @param int $event_queue_id ID of the EventQueue
* @param string $hash Hash of the EventQueue
* @param string $email Destination address
* @return (reference) array array ref that hold array refs to the verp info and urls
*/
static function getVerpAndUrls($job_id, $event_queue_id, $hash, $email)
{
// create a skeleton object and set its properties that are required by getVerpAndUrlsAndHeaders()
$config = CRM_Core_Config::singleton();
$bao = new CRM_Mailing_BAO_Mailing();
$bao->_domain = CRM_Core_BAO_Domain::getDomain( );
$bao->from_name = $bao->from_email = $bao->subject = '';
// use $bao's instance method to get verp and urls
list($verp, $urls, $_) = $bao->getVerpAndUrlsAndHeaders($job_id, $event_queue_id, $hash, $email);
return array($verp, $urls);
}
/**
* get verp, urls and headers
*
* @param int $job_id ID of the Job associated with this message
* @param int $event_queue_id ID of the EventQueue
* @param string $hash Hash of the EventQueue
* @param string $email Destination address
* @return (reference) array array ref that hold array refs to the verp info, urls, and headers
* @access private
*/
private function getVerpAndUrlsAndHeaders( $job_id, $event_queue_id, $hash, $email, $isForward = false )
{
$config = CRM_Core_Config::singleton( );
/**
* Inbound VERP keys:
* reply: user replied to mailing
* bounce: email address bounced
* unsubscribe: contact opts out of all target lists for the mailing
* resubscribe: contact opts back into all target lists for the mailing
* optOut: contact unsubscribes from the domain
*/
$verp = array( );
$verpTokens = array( 'reply' => 'r' ,
'bounce' => 'b' ,
'unsubscribe' => 'u' ,
'resubscribe' => 'e',
'optOut' => 'o');
$localpart = CRM_Core_BAO_MailSettings::defaultLocalpart();
$emailDomain = CRM_Core_BAO_MailSettings::defaultDomain();
foreach ($verpTokens as $key => $value ) {
$verp[$key] = implode($config->verpSeparator,
array(
$localpart . $value,
$job_id,
$event_queue_id,
$hash
)
) . "@$emailDomain";
}
//handle should override VERP address.
$skipEncode = false;
if ( $job_id &&
self::overrideVerp( $job_id ) ) {
$verp['reply'] = "\"{$this->from_name}\" <{$this->from_email}>";
}
$urls = array(
'forward' => CRM_Utils_System::url('civicrm/mailing/forward',
"reset=1&jid={$job_id}&qid={$event_queue_id}&h={$hash}",
true, null, true, true),
'unsubscribeUrl' => CRM_Utils_System::url('civicrm/mailing/unsubscribe',
"reset=1&jid={$job_id}&qid={$event_queue_id}&h={$hash}",
true, null, true, true),
'resubscribeUrl' => CRM_Utils_System::url('civicrm/mailing/resubscribe',
"reset=1&jid={$job_id}&qid={$event_queue_id}&h={$hash}",
true, null, true, true),
'optOutUrl' => CRM_Utils_System::url('civicrm/mailing/optout',
"reset=1&jid={$job_id}&qid={$event_queue_id}&h={$hash}",
true, null, true, true),
'subscribeUrl' => CRM_Utils_System::url( 'civicrm/mailing/subscribe',
'reset=1',
true, null, true, true )
);
$headers = array(
'Reply-To' => $verp['reply'],
'Return-Path' => $verp['bounce'],
'From' => "\"{$this->from_name}\" <{$this->from_email}>",
'Subject' => $this->subject,
'List-Unsubscribe' => "<mailto:{$verp['unsubscribe']}>",
);
self::addMessageIdHeader( $headers, 'm', $job_id, $event_queue_id, $hash );
if( $isForward ) {
$headers['Subject'] = "[Fwd:{$this->subject}]";
}
return array( &$verp, &$urls, &$headers );
}
/**
* Compose a message
*
* @param int $job_id ID of the Job associated with this message
* @param int $event_queue_id ID of the EventQueue
* @param string $hash Hash of the EventQueue
* @param string $contactId ID of the Contact
* @param string $email Destination address
* @param string $recipient To: of the recipient
* @param boolean $test Is this mailing a test?
* @param boolean $isForward Is this mailing compose for forward?
* @param string $fromEmail email address of who is forwardinf it.
* @return object The mail object
* @access public
*/
public function &compose($job_id, $event_queue_id, $hash, $contactId,
$email, &$recipient, $test,
$contactDetails, &$attachments, $isForward = false,
$fromEmail = null, $replyToEmail = null )
{
$config = CRM_Core_Config::singleton( );
$knownTokens = $this->getTokens();
if ($this->_domain == null) {
$this->_domain = CRM_Core_BAO_Domain::getDomain( );
}
list( $verp, $urls, $headers) = $this->getVerpAndUrlsAndHeaders($job_id,
$event_queue_id,
$hash,
$email,
$isForward );
//set from email who is forwarding it and not original one.
if ( $fromEmail ) {
unset( $headers['From'] );
$headers['From'] = "<{$fromEmail}>";
}
if ( $replyToEmail && ( $fromEmail != $replyToEmail ) ) {
$headers['Reply-To'] = "{$replyToEmail}";
}
if ( defined( 'CIVICRM_MAIL_SMARTY' ) &&
CIVICRM_MAIL_SMARTY ) {
CRM_Core_Smarty::registerStringResource( );
}
if ( $contactDetails ) {
$contact = $contactDetails;
} else {
$params = array(array('contact_id', '=', $contactId, 0, 0));
list($contact, $_) = CRM_Contact_BAO_Query::apiQuery($params);
//CRM-4524
$contact = reset( $contact );
if ( ! $contact || is_a( $contact, 'CRM_Core_Error' ) ) {
CRM_Core_Error::debug_log_message( ts( 'CiviMail will not send email to a non-existent contact: %1',
array( 1 => $contactId ) ) );
return null;
}
// also call the hook to get contact details
CRM_Utils_Hook::tokenValues( $contact, $contactId, $job_id );
}
$pTemplates = $this->getPreparedTemplates();
$pEmails = array( );
foreach( $pTemplates as $type => $pTemplate ) {
$html = ($type == 'html') ? true : false;
$pEmails[$type] = array( );
$pEmail =& $pEmails[$type];
$template =& $pTemplates[$type]['template'];
$tokens =& $pTemplates[$type]['tokens'];
$idx = 0;
if ( !empty( $tokens ) ) {
foreach ($tokens as $idx => $token) {
$token_data = $this->getTokenData($token, $html, $contact, $verp, $urls, $event_queue_id);
array_push($pEmail, $template[$idx]);
array_push($pEmail, $token_data);
}
} else {
array_push($pEmail, $template[$idx]);
}
if ( isset( $template[($idx + 1)] ) ) {
array_push($pEmail, $template[($idx + 1)]);
}
}
$html = null;
if ( isset( $pEmails['html'] ) && is_array( $pEmails['html'] ) && count( $pEmails['html'] ) ) {
$html = &$pEmails['html'];
}
$text = null;
if ( isset( $pEmails['text'] ) && is_array( $pEmails['text'] ) && count( $pEmails['text'] ) ){
$text = &$pEmails['text'];
}
// push the tracking url on to the html email if necessary
if ($this->open_tracking && $html ) {
array_push($html,"\n".'<img src="' . $config->userFrameworkResourceURL .
"extern/open.php?q=$event_queue_id\" width='1' height='1' alt='' border='0'>");
}
$message = new Mail_mime("\n");
if ( defined( 'CIVICRM_MAIL_SMARTY' ) &&
CIVICRM_MAIL_SMARTY ) {
$smarty = CRM_Core_Smarty::singleton( );
// also add the contact tokens to the template
$smarty->assign_by_ref( 'contact', $contact );
}
$mailParams = $headers;
if ($text && ( $test || $contact['preferred_mail_format'] == 'Text' ||
$contact['preferred_mail_format'] == 'Both' ||
( $contact['preferred_mail_format'] == 'HTML' && !array_key_exists('html',$pEmails) ) ) ) {
$textBody = join( '', $text );
if ( defined( 'CIVICRM_MAIL_SMARTY' ) &&
CIVICRM_MAIL_SMARTY ) {
$smarty->security = true;
$textBody = $smarty->fetch( "string:$textBody" );
$smarty->security = false;
}
$mailParams['text'] = $textBody;
}
if ( $html && ( $test || ( $contact['preferred_mail_format'] == 'HTML' ||
$contact['preferred_mail_format'] == 'Both') ) ) {
$htmlBody = join( '', $html );
if ( defined( 'CIVICRM_MAIL_SMARTY' ) &&
CIVICRM_MAIL_SMARTY ) {
$smarty->security = true;
$htmlBody = $smarty->fetch( "string:$htmlBody" );
$smarty->security = false;
}
$mailParams['html'] = $htmlBody;
}
if ( empty( $mailParams['text'] ) &&
empty( $mailParams['html'] ) ) {
// CRM-9833
// something went wrong, lets log it and return null
CRM_Core_Error::debug_log_message( ts( 'CiviMail will not send an empty mail body, Skipping: %1',
array( 1 => $email ) ) );
return null;
}
$mailParams['attachments'] = $attachments;
$mailingSubject = CRM_Utils_Array::value( 'subject', $pEmails );
if ( is_array( $mailingSubject ) ) {
$mailingSubject = join( '', $mailingSubject );
}
$mailParams['Subject'] = $mailingSubject;
$mailParams['toName' ] = CRM_Utils_Array::value( 'display_name',
$contact );
$mailParams['toEmail'] = $email;
CRM_Utils_Hook::alterMailParams( $mailParams, 'civimail' );
//cycle through mailParams and set headers array
foreach ( $mailParams as $paramKey => $paramValue ) {
//exclude values not intended for the header
if ( !in_array( $paramKey, array('text','html','attachments','toName', 'toEmail') ) ) {
$headers[$paramKey] = $paramValue;
}
}
if ( ! empty( $mailParams['text'] ) ) {
$message->setTxtBody( $mailParams['text'] );
}
if ( ! empty( $mailParams['html'] ) ) {
$message->setHTMLBody( $mailParams['html'] );
}
if ( ! empty( $mailParams['attachments'] ) ) {
foreach ( $mailParams['attachments'] as $fileID => $attach ) {
$message->addAttachment( $attach['fullPath'],
$attach['mime_type'],
$attach['cleanName'] );
}
}
//pickup both params from mail params.
$toName = trim( $mailParams['toName'] );
$toEmail = trim( $mailParams['toEmail'] );
if ( $toName == $toEmail ||
strpos( $toName, '@' ) !== false ) {
$toName = null;
} else {
$toName = CRM_Utils_Mail::formatRFC2822Name( $toName );
}
$headers['To'] = "$toName <$toEmail>";
$headers['Precedence'] = 'bulk';
// Will test in the mail processor if the X-VERP is set in the bounced email.
// (As an option to replace real VERP for those that can't set it up)
$headers['X-CiviMail-Bounce'] = $verp['bounce'];
//CRM-5058
//token replacement of subject
$headers['Subject'] = $mailingSubject;
CRM_Utils_Mail::setMimeParams( $message );
$headers = $message->headers( $headers );
//get formatted recipient
$recipient = $headers['To'];
// make sure we unset a lot of stuff
unset( $verp );
unset( $urls );
unset( $params );
unset( $contact );
unset( $ids );
return $message;
}
/**
*
* get mailing object and replaces subscribeInvite,
* domain and mailing tokens
*
*/
function tokenReplace( &$mailing )
{
$domain = CRM_Core_BAO_Domain::getDomain( );
foreach ( array('text', 'html') as $type ) {
$tokens = $mailing->getTokens();
if ( isset( $mailing->templates[$type] ) ) {
$mailing->templates[$type] =
CRM_Utils_Token::replaceSubscribeInviteTokens($mailing->templates[$type]);
$mailing->templates[$type] =
CRM_Utils_Token::replaceDomainTokens( $mailing->templates[$type],
$domain,
$type == 'html' ? true : false,
$tokens[$type] );
$mailing->templates[$type] =
CRM_Utils_Token::replaceMailingTokens($mailing->templates[$type], $mailing, null, $tokens[$type]);
}
}
}
/**
*
* getTokenData receives a token from an email
* and returns the appropriate data for the token
*
*/
private function getTokenData(&$token_a, $html = false, &$contact, &$verp, &$urls, $event_queue_id)
{
$type = $token_a['type'];
$token = $token_a['token'];
$data = $token;
$escapeSmarty = defined( 'CIVICRM_MAIL_SMARTY' ) && CIVICRM_MAIL_SMARTY ? true : false;
if ($type == 'embedded_url') {
$embed_data = array( );
foreach ( $token as $t ) {
$embed_data[] = $this->getTokenData($t, $html = false, $contact, $verp, $urls, $event_queue_id);
}
$numSlices = count( $embed_data );
$url = '';
for ( $i = 0; $i < $numSlices; $i++ ) {
$url .= "{$token_a['embed_parts'][$i]}{$embed_data[$i]}";
}
if ( isset( $token_a['embed_parts'][$numSlices] ) ) {
$url .= $token_a['embed_parts'][$numSlices];
}
// add trailing quote since we've gobbled it up in a previous regex
// function getPatterns, line 431
if ( preg_match( '/^href[ ]*=[ ]*\'/', $url ) ) {
$url .= "'";
} else if ( preg_match( '/^href[ ]*=[ ]*\"/', $url ) ) {
$url .= '"';
}
$data = $url;
} else if ( $type == 'url' ) {
if ( $this->url_tracking ) {
$data = CRM_Mailing_BAO_TrackableURL::getTrackerURL($token, $this->id, $event_queue_id);
} else {
$data = $token;
}
} else if ( $type == 'contact' ) {
$data = CRM_Utils_Token::getContactTokenReplacement($token, $contact, false, false, $escapeSmarty );
} else if ( $type == 'action' ) {
$data = CRM_Utils_Token::getActionTokenReplacement($token, $verp, $urls, $html);
} else if ( $type == 'domain' ) {
$domain = CRM_Core_BAO_Domain::getDomain( );
$data = CRM_Utils_Token::getDomainTokenReplacement($token, $domain, $html);
} else if( $type == 'mailing') {
if ( $token == 'name' ) {
$data = $this->name ;
} else if ( $token == 'group' ) {
$groups = $this->getGroupNames( );
$data = implode(', ', $groups);
}
} else {
$data = CRM_Utils_Array::value( "{$type}.{$token}", $contact );
}
return $data;
}
/**
* Return a list of group names for this mailing. Does not work with
* prior-mailing targets.
*
* @return array Names of groups receiving this mailing
* @access public
*/
public function &getGroupNames() {
if (! isset($this->id)) {
return array();
}
$mg = new CRM_Mailing_DAO_Group();
$mgtable = CRM_Mailing_DAO_Group::getTableName();
$group = CRM_Contact_BAO_Group::getTableName();
$mg->query("SELECT $group.title as name FROM $mgtable
INNER JOIN $group ON $mgtable.entity_id = $group.id
WHERE $mgtable.mailing_id = {$this->id}
AND $mgtable.entity_table = '$group'
AND $mgtable.group_type = 'Include'
ORDER BY $group.name");
$groups = array();
while ($mg->fetch()) {
$groups[] = $mg->name;
}
$mg->free( );
return $groups;
}
/**
* function to add the mailings
*
* @param array $params reference array contains the values submitted by the form
* @param array $ids reference array contains the id
*
* @access public
* @static
* @return object
*/
static function add( &$params, &$ids )
{
if ( CRM_Utils_Array::value( 'mailing', $ids ) ) {
CRM_Utils_Hook::pre( 'edit', 'Mailing', $ids['mailing_id'], $params );
} else {
CRM_Utils_Hook::pre( 'create', 'Mailing', null, $params );
}
$mailing = new CRM_Mailing_DAO_Mailing( );
$mailing->id = CRM_Utils_Array::value( 'mailing_id', $ids );
$mailing->domain_id = CRM_Utils_Array::value( 'domain_id', $params, CRM_Core_Config::domainID( ) );
if ( ! isset( $params['replyto_email'] ) &&
isset( $params['from_email'] ) ) {
$params['replyto_email'] = $params['from_email'];
}
$mailing->copyValues( $params );
$result = $mailing->save( );
if ( CRM_Utils_Array::value( 'mailing', $ids ) ) {
CRM_Utils_Hook::post( 'edit', 'Mailing', $mailing->id, $mailing );
} else {
CRM_Utils_Hook::post( 'create', 'Mailing', $mailing->id, $mailing );
}
return $result;
}
/**
* Construct a new mailing object, along with job and mailing_group
* objects, from the form values of the create mailing wizard.
*
* @params array $params Form values
* @return object $mailing The new mailing object
* @access public
* @static
*/
public static function create( &$params, &$ids )
{
$transaction = new CRM_Core_Transaction( );
$mailing = self::add($params, $ids);
if( is_a( $mailing, 'CRM_Core_Error') ) {
$transaction->rollback( );
return $mailing;
}
$groupTableName = CRM_Contact_BAO_Group::getTableName( );
$mailingTableName = CRM_Mailing_BAO_Mailing::getTableName( );
/* Create the mailing group record */
$mg = new CRM_Mailing_DAO_Group();
foreach( array( 'groups', 'mailings' ) as $entity ) {
foreach( array( 'include', 'exclude', 'base' ) as $type ) {
if( CRM_Utils_Array::value( $type, $params[$entity] ) && is_array( $params[$entity][$type] ) ) {
foreach( $params[$entity][$type] as $entityId ) {
$mg->reset( );
$mg->mailing_id = $mailing->id;
$mg->entity_table =
( $entity == 'groups' )
? $groupTableName
: $mailingTableName;
$mg->entity_id = $entityId;
$mg->group_type = $type;
$mg->save( );
}
}
}
}
if ( ! empty( $params['search_id'] ) &&
! empty( $params['group_id'] ) ) {
$mg->reset( );
$mg->mailing_id = $mailing->id;
$mg->entity_table = $groupTableName;
$mg->entity_id = $params['group_id'];
$mg->search_id = $params['search_id'];
$mg->search_args = $params['search_args'];
$mg->group_type = 'Include';
$mg->save( );
}
// check and attach and files as needed
CRM_Core_BAO_File::processAttachment( $params,
'civicrm_mailing',
$mailing->id );
$transaction->commit( );
return $mailing;
}
/**
* Generate a report. Fetch event count information, mailing data, and job
* status.
*
* @param int $id The mailing id to report
* @param boolean $skipDetails whether return all detailed report
* @return array Associative array of reporting data
* @access public
* @static
*/
public static function &report( $id, $skipDetails = false, $isSMS = false ) {
$mailing_id = CRM_Utils_Type::escape($id, 'Integer');
$mailing = new CRM_Mailing_BAO_Mailing();
$t = array(
'mailing' => self::getTableName(),
'mailing_group' => CRM_Mailing_DAO_Group::getTableName(),
'group' => CRM_Contact_BAO_Group::getTableName(),
'job' => CRM_Mailing_BAO_Job::getTableName(),
'queue' => CRM_Mailing_Event_BAO_Queue::getTableName(),
'delivered' => CRM_Mailing_Event_BAO_Delivered::getTableName(),
'opened' => CRM_Mailing_Event_BAO_Opened::getTableName(),
'reply' => CRM_Mailing_Event_BAO_Reply::getTableName(),
'unsubscribe' =>
CRM_Mailing_Event_BAO_Unsubscribe::getTableName(),
'bounce' => CRM_Mailing_Event_BAO_Bounce::getTableName(),
'forward' => CRM_Mailing_Event_BAO_Forward::getTableName(),
'url' => CRM_Mailing_BAO_TrackableURL::getTableName(),
'urlopen' =>
CRM_Mailing_Event_BAO_TrackableURLOpen::getTableName(),
'component' => CRM_Mailing_BAO_Component::getTableName(),
'spool' => CRM_Mailing_BAO_Spool::getTableName()
);
$report = array();
$additionalWhereClause = " AND ";
if( !$isSMS ) {
$additionalWhereClause .= " {$t['mailing']}.sms_provider_id IS NULL ";
} else {
$additionalWhereClause .= " {$t['mailing']}.sms_provider_id IS NOT NULL ";
}
/* Get the mailing info */
$mailing->query("
SELECT {$t['mailing']}.*
FROM {$t['mailing']}
WHERE {$t['mailing']}.id = $mailing_id {$additionalWhereClause}");
$mailing->fetch();
$report['mailing'] = array();
foreach (array_keys(self::fields()) as $field) {
$report['mailing'][$field] = $mailing->$field;
}
//get the campaign
if ( $campaignId = CRM_Utils_Array::value( 'campaign_id', $report['mailing'] ) ) {
$campaigns = CRM_Campaign_BAO_Campaign::getCampaigns( $campaignId );
$report['mailing']['campaign'] = $campaigns[$campaignId];
}
//mailing report is called by activity
//we dont need all detail report
if ( $skipDetails ) {
return $report;
}
/* Get the component info */
$query = array();
$components = array(
'header' => ts('Header'),
'footer' => ts('Footer'),
'reply' => ts('Reply'),
'unsubscribe' => ts('Unsubscribe'),
'optout' => ts('Opt-Out')
);
foreach(array_keys($components) as $type) {
$query[] = "SELECT {$t['component']}.name as name,
'$type' as type,
{$t['component']}.id as id
FROM {$t['component']}
INNER JOIN {$t['mailing']}
ON {$t['mailing']}.{$type}_id =
{$t['component']}.id
WHERE {$t['mailing']}.id = $mailing_id";
}
$q = '(' . implode(') UNION (', $query) . ')';
$mailing->query($q);
$report['component'] = array();
while ($mailing->fetch()) {
$report['component'][] = array(
'type' => $components[$mailing->type],
'name' => $mailing->name,
'link' =>
CRM_Utils_System::url('civicrm/mailing/component',
"reset=1&action=update&id={$mailing->id}"),
);
}
/* Get the recipient group info */
$mailing->query("
SELECT {$t['mailing_group']}.group_type as group_type,
{$t['group']}.id as group_id,
{$t['group']}.title as group_title,
{$t['group']}.is_hidden as group_hidden,
{$t['mailing']}.id as mailing_id,
{$t['mailing']}.name as mailing_name
FROM {$t['mailing_group']}
LEFT JOIN {$t['group']}
ON {$t['mailing_group']}.entity_id = {$t['group']}.id
AND {$t['mailing_group']}.entity_table =
'{$t['group']}'
LEFT JOIN {$t['mailing']}
ON {$t['mailing_group']}.entity_id =
{$t['mailing']}.id
AND {$t['mailing_group']}.entity_table =
'{$t['mailing']}'
WHERE {$t['mailing_group']}.mailing_id = $mailing_id
");
$report['group'] = array('include' => array(), 'exclude' => array(), 'base' => array());
while ($mailing->fetch()) {
$row = array();
if (isset($mailing->group_id)) {
$row['id'] = $mailing->group_id;
$row['name'] = $mailing->group_title;
$row['link'] = CRM_Utils_System::url('civicrm/group/search',
"reset=1&force=1&context=smog&gid={$row['id']}");
} else {
$row['id'] = $mailing->mailing_id;
$row['name'] = $mailing->mailing_name;
$row['mailing'] = true;
$row['link'] = CRM_Utils_System::url('civicrm/mailing/report',
"mid={$row['id']}");
}
/* Rename hidden groups */
if($mailing->group_hidden == 1) {
$row['name'] = "Search Results";
}
if ($mailing->group_type == 'Include') {
$report['group']['include'][] = $row;
} else if($mailing->group_type == 'Base') {
$report['group']['base'][] = $row;
} else {
$report['group']['exclude'][] = $row;
}
}
/* Get the event totals, grouped by job (retries) */
$mailing->query("
SELECT {$t['job']}.*,
COUNT(DISTINCT {$t['queue']}.id) as queue,
COUNT(DISTINCT {$t['delivered']}.id) as delivered,
COUNT(DISTINCT {$t['reply']}.id) as reply,
COUNT(DISTINCT {$t['forward']}.id) as forward,
COUNT(DISTINCT {$t['bounce']}.id) as bounce,
COUNT(DISTINCT {$t['urlopen']}.id) as url,
COUNT(DISTINCT {$t['spool']}.id) as spool
FROM {$t['job']}
LEFT JOIN {$t['queue']}
ON {$t['queue']}.job_id = {$t['job']}.id
LEFT JOIN {$t['reply']}
ON {$t['reply']}.event_queue_id = {$t['queue']}.id
LEFT JOIN {$t['forward']}
ON {$t['forward']}.event_queue_id = {$t['queue']}.id
LEFT JOIN {$t['bounce']}
ON {$t['bounce']}.event_queue_id = {$t['queue']}.id
LEFT JOIN {$t['delivered']}
ON {$t['delivered']}.event_queue_id = {$t['queue']}.id
AND {$t['bounce']}.id IS null
LEFT JOIN {$t['urlopen']}
ON {$t['urlopen']}.event_queue_id = {$t['queue']}.id
LEFT JOIN {$t['spool']}
ON {$t['spool']}.job_id = {$t['job']}.id
WHERE {$t['job']}.mailing_id = $mailing_id
AND {$t['job']}.is_test = 0
GROUP BY {$t['job']}.id");
$report['jobs'] = array();
$report['event_totals'] = array();
$elements = array( 'queue', 'delivered', 'url', 'forward',
'reply', 'unsubscribe', 'optout', 'opened', 'bounce', 'spool' );
// initialize various counters
foreach ( $elements as $field ) {
$report['event_totals'][$field] = 0;
}
while ($mailing->fetch()) {
$row = array();
foreach ( $elements as $field ) {
if ( isset( $mailing->$field ) ) {
$row[$field] = $mailing->$field;
$report['event_totals'][$field] += $mailing->$field;
}
}
// compute open total separately to discount duplicates
// CRM-1258
$row['opened'] = CRM_Mailing_Event_BAO_Opened::getTotalCount( $mailing_id, $mailing->id, true );
$report['event_totals']['opened'] += $row['opened'];
// compute unsub total separately to discount duplicates
// CRM-1783
$row['unsubscribe'] = CRM_Mailing_Event_BAO_Unsubscribe::getTotalCount( $mailing_id, $mailing->id, true, true );
$report['event_totals']['unsubscribe'] += $row['unsubscribe'];
$row['optout'] = CRM_Mailing_Event_BAO_Unsubscribe::getTotalCount( $mailing_id, $mailing->id, true, false );
$report['event_totals']['optout'] += $row['optout'];
foreach ( array_keys(CRM_Mailing_BAO_Job::fields( ) ) as $field ) {
$row[$field] = $mailing->$field;
}
if ($mailing->queue) {
$row['delivered_rate'] = (100.0 * $mailing->delivered ) /
$mailing->queue;
$row['bounce_rate'] = (100.0 * $mailing->bounce ) /
$mailing->queue;
$row['unsubscribe_rate'] = (100.0 * $row['unsubscribe'] ) /
$mailing->queue;
$row['optout_rate'] = (100.0 * $row['optout'] ) /
$mailing->queue;
} else {
$row['delivered_rate'] = 0;
$row['bounce_rate'] = 0;
$row['unsubscribe_rate'] = 0;
$row['optout_rate'] = 0;
}
$row['links'] = array(
'clicks' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=click&mid=$mailing_id&jid={$mailing->id}"
),
'queue' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=queue&mid=$mailing_id&jid={$mailing->id}"
),
'delivered' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=delivered&mid=$mailing_id&jid={$mailing->id}"
),
'bounce' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=bounce&mid=$mailing_id&jid={$mailing->id}"
),
'unsubscribe' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=unsubscribe&mid=$mailing_id&jid={$mailing->id}"
),
'forward' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=forward&mid=$mailing_id&jid={$mailing->id}"
),
'reply' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=reply&mid=$mailing_id&jid={$mailing->id}"
),
'opened' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=opened&mid=$mailing_id&jid={$mailing->id}"
),
);
foreach (array('scheduled_date', 'start_date', 'end_date') as $key) {
$row[$key] = CRM_Utils_Date::customFormat($row[$key]);
}
$report['jobs'][] = $row;
}
$newTableSize = CRM_Mailing_BAO_Recipients::mailingSize( $mailing_id );
// we need to do this for backward compatibility, since old mailings did not
// use the mailing_recipients table
if ( $newTableSize > 0 ) {
$report['event_totals']['queue'] = $newTableSize;
} else {
$report['event_totals']['queue'] = self::getRecipientsCount( $mailing_id, $mailing_id );
}
if (CRM_Utils_Array::value('queue',$report['event_totals'] )) {
$report['event_totals']['delivered_rate'] = (100.0 * $report['event_totals']['delivered']) / $report['event_totals']['queue'];
$report['event_totals']['bounce_rate'] = (100.0 * $report['event_totals']['bounce']) / $report['event_totals']['queue'];
$report['event_totals']['unsubscribe_rate'] = (100.0 * $report['event_totals']['unsubscribe']) / $report['event_totals']['queue'];
$report['event_totals']['optout_rate'] = (100.0 * $report['event_totals']['optout']) / $report['event_totals']['queue'];
} else {
$report['event_totals']['delivered_rate'] = 0;
$report['event_totals']['bounce_rate'] = 0;
$report['event_totals']['unsubscribe_rate'] = 0;
$report['event_totals']['optout_rate'] = 0;
}
/* Get the click-through totals, grouped by URL */
$mailing->query("
SELECT {$t['url']}.url,
{$t['url']}.id,
COUNT({$t['urlopen']}.id) as clicks,
COUNT(DISTINCT {$t['queue']}.id) as unique_clicks
FROM {$t['url']}
LEFT JOIN {$t['urlopen']}
ON {$t['urlopen']}.trackable_url_id = {$t['url']}.id
LEFT JOIN {$t['queue']}
ON {$t['urlopen']}.event_queue_id = {$t['queue']}.id
LEFT JOIN {$t['job']}
ON {$t['queue']}.job_id = {$t['job']}.id
WHERE {$t['url']}.mailing_id = $mailing_id
AND {$t['job']}.is_test = 0
GROUP BY {$t['url']}.id");
$report['click_through'] = array();
while ($mailing->fetch()) {
$report['click_through'][] = array(
'url' => $mailing->url,
'link' =>
CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=click&mid=$mailing_id&uid={$mailing->id}"),
'link_unique' =>
CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=click&mid=$mailing_id&uid={$mailing->id}&distinct=1"),
'clicks' => $mailing->clicks,
'unique' => $mailing->unique_clicks,
'rate' => CRM_Utils_Array::value('delivered',$report['event_totals']) ? (100.0 * $mailing->unique_clicks) / $report['event_totals']['delivered'] : 0
);
}
$report['event_totals']['links'] = array(
'clicks' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=click&mid=$mailing_id"
),
'clicks_unique' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=click&mid=$mailing_id&distinct=1"
),
'queue' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=queue&mid=$mailing_id"
),
'delivered' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=delivered&mid=$mailing_id"
),
'bounce' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=bounce&mid=$mailing_id"
),
'unsubscribe' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=unsubscribe&mid=$mailing_id"
),
'forward' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=forward&mid=$mailing_id"
),
'reply' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=reply&mid=$mailing_id"
),
'opened' => CRM_Utils_System::url(
'civicrm/mailing/report/event',
"reset=1&event=opened&mid=$mailing_id"
),
);
$actionLinks = array( CRM_Core_Action::VIEW => array( 'name' => ts('Report'), ), );
if ( CRM_Core_Permission::check( 'view all contacts' ) ) {
$actionLinks[CRM_Core_Action::ADVANCED] =
array( 'name' => ts('Advanced Search'),
'url' => 'civicrm/contact/search/advanced', );
}
$action = array_sum(array_keys($actionLinks));
$report['event_totals']['actionlinks'] = array();
foreach ( array('clicks', 'clicks_unique', 'queue', 'delivered', 'bounce', 'unsubscribe',
'forward', 'reply', 'opened', 'optout' ) as $key ) {
$url = 'mailing/detail';
$reportFilter = "reset=1&mailing_id_value={$mailing_id}";
$searchFilter = "force=1&mailing_id={$mailing_id}";
switch ( $key ) {
case 'delivered':
$reportFilter .= "&delivery_status_value=successful";
$searchFilter .= "&mailing_delivery_status=Y";
break;
case 'bounce':
$url = "mailing/bounce";
$searchFilter .= "&mailing_delivery_status=N";
break;
case 'forward':
$reportFilter .= "&is_forwarded_value=1";
$searchFilter .= "&mailing_forward=1";
break;
case 'reply':
$reportFilter .= "&is_replied_value=1";
$searchFilter .= "&mailing_reply_status=Y";
break;
case 'unsubscribe':
$reportFilter .= "&is_unsubscribed_value=1";
$searchFilter .= "&mailing_unsubscribe=1";
break;
case 'optout':
$reportFilter .= "&is_optout_value=1";
$searchFilter .= "&mailing_optout=1";
break;
case 'opened':
$url = "mailing/opened";
$searchFilter .= "&mailing_open_status=Y";
break;
case 'clicks':
case 'clicks_unique':
$url = "mailing/clicks";
$searchFilter .= "&mailing_click_status=Y";
break;
}
$actionLinks[CRM_Core_Action::VIEW]['url'] =
CRM_Report_Utils_Report::getNextUrl( $url, $reportFilter, false, true );
if ( array_key_exists(CRM_Core_Action::ADVANCED, $actionLinks) ) {
$actionLinks[CRM_Core_Action::ADVANCED]['qs'] = $searchFilter;
}
$report['event_totals']['actionlinks'][$key] =
CRM_Core_Action::formLink($actionLinks, $action, array());
}
return $report;
}
/**
* Get the count of mailings
*
* @param
* @return int Count
* @access public
*/
public function getCount() {
$this->selectAdd();
$this->selectAdd('COUNT(id) as count');
$session = CRM_Core_Session::singleton();
$this->find(true);
return $this->count;
}
static function checkPermission( $id ) {
if ( ! $id ) {
return;
}
$mailingIDs = CRM_Mailing_BAO_Mailing::mailingACLIDs( );
if ( ! in_array( $id,
$mailingIDs ) ) {
CRM_Core_Error::fatal( ts( 'You do not have permission to access this mailing report' ) );
}
return;
}
static function mailingACL( $alias = null ) {
$mailingACL = " ( 0 ) ";
$mailingIDs = self::mailingACLIDs( );
if ( ! empty( $mailingIDs ) ) {
$mailingIDs = implode( ',', $mailingIDs );
$tableName = !$alias ? self::getTableName( ) : $alias;
$mailingACL = " $tableName.id IN ( $mailingIDs ) ";
}
return $mailingACL;
}
static function &mailingACLIDs( $count = false, $condition = null ) {
$mailingIDs = array( );
// get all the groups that this user can access
// if they dont have universal access
$groups = CRM_Core_PseudoConstant::group( );
if ( ! empty( $groups ) ) {
$groupIDs = implode( ',',
array_keys( $groups ) );
$selectClause = ( $count ) ? 'COUNT( DISTINCT m.id) as count' : 'DISTINCT( m.id ) as id';
// get all the mailings that are in this subset of groups
$query = "
SELECT $selectClause
FROM civicrm_mailing m
LEFT JOIN civicrm_mailing_group g ON g.mailing_id = m.id
WHERE ( ( g.entity_table = 'civicrm_group' AND g.entity_id IN ( $groupIDs ) )
OR ( g.entity_table IS NULL AND g.entity_id IS NULL ) )
$condition";
$dao = CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray );
if ( $count ) {
$dao->fetch( );
return $dao->count;
}
$mailingIDs = array( );
while ( $dao->fetch( ) ) {
$mailingIDs[] = $dao->id;
}
}
return $mailingIDs;
}
/**
* Get the rows for a browse operation
*
* @param int $offset The row number to start from
* @param int $rowCount The nmber of rows to return
* @param string $sort The sql string that describes the sort order
*
* @return array The rows
* @access public
*/
public function &getRows($offset, $rowCount, $sort, $additionalClause = null, $additionalParams = null ) {
$mailing = self::getTableName();
$job = CRM_Mailing_BAO_Job::getTableName();
$group = CRM_Mailing_DAO_Group::getTableName( );
$session = CRM_Core_Session::singleton();
$mailingACL = self::mailingACL( );
//get all campaigns.
$allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns( null, null, false, false, false, true );
// we only care about parent jobs, since that holds all the info on
// the mailing
$query = "
SELECT $mailing.id,
$mailing.name,
$job.status,
$mailing.approval_status_id,
MIN($job.scheduled_date) as scheduled_date,
MIN($job.start_date) as start_date,
MAX($job.end_date) as end_date,
createdContact.sort_name as created_by,
scheduledContact.sort_name as scheduled_by,
$mailing.created_id as created_id,
$mailing.scheduled_id as scheduled_id,
$mailing.is_archived as archived,
$mailing.created_date as created_date,
campaign_id,
$mailing.sms_provider_id as sms_provider_id
FROM $mailing
LEFT JOIN $job ON ( $job.mailing_id = $mailing.id AND $job.is_test = 0 AND $job.parent_id IS NULL )
LEFT JOIN civicrm_contact createdContact ON ( civicrm_mailing.created_id = createdContact.id )
LEFT JOIN civicrm_contact scheduledContact ON ( civicrm_mailing.scheduled_id = scheduledContact.id )
WHERE $mailingACL $additionalClause
GROUP BY $mailing.id ";
if ($sort) {
$orderBy = trim( $sort->orderBy() );
if ( ! empty( $orderBy ) ) {
$query .= " ORDER BY $orderBy";
}
}
if ($rowCount) {
$query .= " LIMIT $offset, $rowCount ";
}
if ( ! $additionalParams ) {
$additionalParams = array( );
}
$dao = CRM_Core_DAO::executeQuery( $query, $additionalParams );
$rows = array();
while ($dao->fetch()) {
$rows[] = array(
'id' => $dao->id,
'name' => $dao->name,
'status' => $dao->status ? $dao->status : 'Not scheduled',
'created_date' => CRM_Utils_Date::customFormat($dao->created_date),
'scheduled' => CRM_Utils_Date::customFormat($dao->scheduled_date),
'scheduled_iso' => $dao->scheduled_date,
'start' => CRM_Utils_Date::customFormat($dao->start_date),
'end' => CRM_Utils_Date::customFormat($dao->end_date),
'created_by' => $dao->created_by,
'scheduled_by' => $dao->scheduled_by,
'created_id' => $dao->created_id,
'scheduled_id' => $dao->scheduled_id,
'archived' => $dao->archived,
'approval_status_id' => $dao->approval_status_id,
'campaign_id' => $dao->campaign_id,
'campaign' => empty($dao->campaign_id) ? NULL : $allCampaigns[$dao->campaign_id],
'sms_provider_id' => $dao->sms_provider_id
);
}
return $rows;
}
/**
* Function to show detail Mailing report
*
* @param int $id
*
* @static
* @access public
*/
static function showEmailDetails( $id )
{
return CRM_Utils_System::url('civicrm/mailing/report', "mid=$id");
}
/**
* Delete Mails and all its associated records
*
* @param int $id id of the mail to delete
*
* @return void
* @access public
* @static
*/
public static function del($id) {
if ( empty( $id ) ) {
CRM_Core_Error::fatal( );
}
// delete all file attachments
CRM_Core_BAO_File::deleteEntityFile( 'civicrm_mailing',
$id );
$dao = new CRM_Mailing_DAO_Mailing();
$dao->id = $id;
$dao->delete( );
CRM_Core_Session::setStatus(ts('Selected mailing has been deleted.'));
}
/**
* Delete Jobss and all its associated records
* related to test Mailings
*
* @param int $id id of the Job to delete
*
* @return void
* @access public
* @static
*/
public static function delJob($id) {
if ( empty( $id ) ) {
CRM_Core_Error::fatal( );
}
$dao = new CRM_Mailing_BAO_Job();
$dao->id = $id;
$dao->delete();
}
function getReturnProperties( ) {
$tokens =& $this->getTokens( );
$properties = array( );
if ( isset( $tokens['html'] ) &&
isset( $tokens['html']['contact'] ) ) {
$properties = array_merge( $properties, $tokens['html']['contact'] );
}
if ( isset( $tokens['text'] ) &&
isset( $tokens['text']['contact'] ) ) {
$properties = array_merge( $properties, $tokens['text']['contact'] );
}
if ( isset( $tokens['subject'] ) &&
isset( $tokens['subject']['contact'] ) ) {
$properties = array_merge( $properties, $tokens['subject']['contact'] );
}
$returnProperties = array( );
$returnProperties['display_name'] =
$returnProperties['contact_id'] = $returnProperties['preferred_mail_format'] = $returnProperties['hash'] = 1;
foreach ( $properties as $p ) {
$returnProperties[$p] = 1;
}
return $returnProperties;
}
/**
* Function to build the compose mail form
* @param $form
* @return None
* @access public
*/
public function commonCompose ( &$form )
{
//get the tokens.
$tokens = CRM_Core_SelectValues::contactTokens( );
//token selector for subject
//CRM-5058
$form->add( 'select', 'token3', ts( 'Insert Token' ),
$tokens , false,
array(
'size' => "5",
'multiple' => true,
'onclick' => "return tokenReplText(this);"
)
);
$className = CRM_Utils_System::getClassName( $form );
if ( $className == 'CRM_Mailing_Form_Upload' ) {
$tokens = array_merge( CRM_Core_SelectValues::mailingTokens( ), $tokens );
} elseif ( $className == 'CRM_Admin_Form_ScheduleReminders' ) {
$tokens = array_merge( CRM_Core_SelectValues::activityTokens( ), $tokens );
$tokens = array_merge( CRM_Core_SelectValues::eventTokens( ), $tokens );
$tokens = array_merge( CRM_Core_SelectValues::membershipTokens( ), $tokens );
} elseif ( $className == 'CRM_Event_Form_ManageEvent_ScheduleReminders' ) {
$tokens = array_merge( CRM_Core_SelectValues::eventTokens( ), $tokens );
}
//sorted in ascending order tokens by ignoring word case
natcasesort($tokens);
$form->assign( 'tokens', json_encode( $tokens ) );
$form->add( 'select', 'token1', ts( 'Insert Tokens' ),
$tokens , false,
array(
'size' => "5",
'multiple' => true,
'onclick' => "return tokenReplText(this);"
)
);
$form->add( 'select', 'token2', ts( 'Insert Tokens' ),
$tokens , false,
array(
'size' => "5",
'multiple' => true,
'onclick' => "return tokenReplHtml(this);"
)
);
$form->_templates = CRM_Core_BAO_MessageTemplates::getMessageTemplates( false );
if ( !empty( $form->_templates ) ) {
$form->assign('templates', true);
$form->add('select', 'template', ts('Use Template'),
array( '' => ts( '- select -' ) ) + $form->_templates, false,
array('onChange' => "selectValue( this.value );") );
$form->add('checkbox','updateTemplate',ts('Update Template'), null);
}
$form->add('checkbox','saveTemplate',ts('Save As New Template'), null,false,
array( 'onclick' => "showSaveDetails(this);" ));
$form->add('text','saveTemplateName',ts('Template Title'));
//insert message Text by selecting "Select Template option"
$form->add( 'textarea',
'text_message',
ts('Plain-text format'),
array('cols' => '80', 'rows' => '8',
'onkeyup' => "return verify(this)"));
if ( $className != 'CRM_SMS_Form_Upload' && $className != 'CRM_Contact_Form_Task_SMS' &&
$className != 'CRM_Contact_Form_Task_SMS' ) {
$form->addWysiwyg( 'html_message',
ts('HTML format'),
array('cols' => '80', 'rows' => '8',
'onkeyup' =>"return verify(this)" ) );
}
}
/**
* Function to build the compose PDF letter form
* @param $form
* @return None
* @access public
*/
public function commonLetterCompose ( &$form )
{
//get the tokens.
$tokens = CRM_Core_SelectValues::contactTokens( );
if ( CRM_Utils_System::getClassName( $form ) == 'CRM_Mailing_Form_Upload' ) {
$tokens = array_merge( CRM_Core_SelectValues::mailingTokens( ), $tokens );
}
if ( CRM_Utils_System::getClassName( $form ) == 'CRM_Contribute_Form_Task_PDFLetter' ) {
$tokens = array_merge( CRM_Core_SelectValues::contributionTokens( ), $tokens );
}
//sorted in ascending order tokens by ignoring word case
natcasesort($tokens);
$form->assign( 'tokens', json_encode( $tokens ) );
$form->add( 'select', 'token1', ts( 'Insert Tokens' ),
$tokens , false,
array(
'size' => "5",
'multiple' => true,
'onchange' => "return tokenReplHtml(this);"
)
);
$form->_templates = CRM_Core_BAO_MessageTemplates::getMessageTemplates( false );
if ( !empty( $form->_templates ) ) {
$form->assign('templates', true);
$form->add('select', 'template', ts('Select Template'),
array( '' => ts( '- select -' ) ) + $form->_templates, false,
array('onChange' => "selectValue( this.value );") );
$form->add('checkbox','updateTemplate',ts('Update Template'), null);
}
$form->add('checkbox','saveTemplate',ts('Save As New Template'), null,false,
array( 'onclick' => "showSaveDetails(this);" ));
$form->add('text','saveTemplateName',ts('Template Title'));
$form->addWysiwyg( 'html_message',
ts('Your Letter'),
array('cols' => '80', 'rows' => '8',
'onkeyup' =>"return verify(this)" ) );
$action = CRM_Utils_Request::retrieve( 'action', 'String', $this, false );
if ( ( CRM_Utils_System::getClassName( $form ) == 'CRM_Contact_Form_Task_PDF' )&&
$action == CRM_Core_Action::VIEW ) {
$form->freeze( 'html_message' );
}
}
/**
* Get the search based mailing Ids
*
* @return array $mailingIDs, searched base mailing ids.
* @access public
*/
public function searchMailingIDs( )
{
$group = CRM_Mailing_DAO_Group::getTableName( );
$mailing = self::getTableName();
$query = "
SELECT $mailing.id as mailing_id
FROM $mailing, $group
WHERE $group.mailing_id = $mailing.id
AND $group.group_type = 'Base'";
$searchDAO = CRM_Core_DAO::executeQuery( $query );
$mailingIDs = array( );
while ( $searchDAO->fetch( ) ) {
$mailingIDs[] = $searchDAO->mailing_id;
}
return $mailingIDs;
}
/**
* Get the content/components of mailing based on mailing Id
*
* @param $report array of mailing report
*
* @param $form reference of this
*
* @return $report array content/component.
* @access public
*/
public function getMailingContent( &$report, &$form, $isSMS = false )
{
$htmlHeader = $textHeader = null;
$htmlFooter = $textFooter = null;
if( !$isSMS ) {
if ($report['mailing']['header_id']) {
$header = new CRM_Mailing_BAO_Component();
$header->id = $report['mailing']['header_id'];
$header->find(true);
$htmlHeader = $header->body_html;
$textHeader = $header->body_text;
}
if ($report['mailing']['footer_id']) {
$footer = new CRM_Mailing_BAO_Component();
$footer->id = $report['mailing']['footer_id'];
$footer->find(true);
$htmlFooter = $footer->body_html;
$textFooter = $footer->body_text;
}
}
$text = CRM_Utils_Request::retrieve( 'text', 'Boolean', $form );
if ( $text ) {
echo "<pre>{$textHeader}</br>{$report['mailing']['body_text']}</br>{$textFooter}</pre>";
CRM_Utils_System::civiExit( );
}
if( !$isSMS ) {
$html = CRM_Utils_Request::retrieve( 'html', 'Boolean', $form );
if ( $html ) {
echo $htmlHeader . $report['mailing']['body_html'] . $htmlFooter;
CRM_Utils_System::civiExit( );
}
}
if ( ! empty( $report['mailing']['body_text'] ) ) {
$url = CRM_Utils_System::url( 'civicrm/mailing/report', 'reset=1&text=1&mid=' . $form->_mailing_id );
$popup = "javascript:popUp(\"$url\");";
$form->assign( 'textViewURL' , $popup );
}
if( !$isSMS ) {
if ( ! empty( $report['mailing']['body_html'] ) ) {
$url = CRM_Utils_System::url( 'civicrm/mailing/report', 'reset=1&html=1&mid=' . $form->_mailing_id );
$popup = "javascript:popUp(\"$url\");";
$form->assign( 'htmlViewURL' , $popup );
}
}
if ( !$isSMS ) {
$report['mailing']['attachment'] = CRM_Core_BAO_File::attachmentInfo( 'civicrm_mailing',
$form->_mailing_id );
}
return $report;
}
static function overrideVerp( $jobID ) {
static $_cache = array( );
if ( ! isset( $_cache[$jobID] ) ) {
$query = "
SELECT override_verp
FROM civicrm_mailing
INNER JOIN civicrm_mailing_job ON civicrm_mailing.id = civicrm_mailing_job.mailing_id
WHERE civicrm_mailing_job.id = %1
";
$params = array( 1 => array( $jobID, 'Integer' ) );
$_cache[$jobID] = CRM_Core_DAO::singleValueQuery( $query, $params );
}
return $_cache[$jobID];
}
static function processQueue( $mode = null )
{
$config =& CRM_Core_Config::singleton();
CRM_Core_Error::debug_log_message( "Beginning processQueue run: {$config->mailerJobsMax}, {$config->mailerJobSize}" );
if ( $mode == null && CRM_Core_BAO_MailSettings::defaultDomain() == "FIXME.ORG") {
CRM_Core_Error::fatal( ts( 'The <a href="%1">default mailbox</a> has not been configured. You will find <a href="%2">more info in the online user and administrator guide</a>', array( 1 => CRM_Utils_System::url('civicrm/admin/mailSettings', 'reset=1'), 2=> "http://book.civicrm.org/user/basic-setup/email-system-configuration")));
}
// check if we are enforcing number of parallel cron jobs
// CRM-8460
$gotCronLock = false;
if ( $config->mailerJobsMax &&
$config->mailerJobsMax > 1 ) {
$lockArray = range( 1, $config->mailerJobsMax );
shuffle( $lockArray );
// check if we are using global locks
$serverWideLock = CRM_Core_BAO_Setting::getItem( CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
'civimail_server_wide_lock' );
foreach ( $lockArray as $lockID ) {
$cronLock = new CRM_Core_Lock( "civimail.cronjob.{$lockID}", null, $serverWideLock );
if ( $cronLock->isAcquired( ) ) {
$gotCronLock = true;
break;
}
}
// exit here since we have enuf cronjobs running
if ( ! $gotCronLock ) {
CRM_Core_Error::debug_log_message( 'Returning early, since max number of cronjobs running' );
return true;
}
}
// load bootstrap to call hooks
// Split up the parent jobs into multiple child jobs
CRM_Mailing_BAO_Job::runJobs_pre($config->mailerJobSize, $mode);
CRM_Mailing_BAO_Job::runJobs(null, $mode);
CRM_Mailing_BAO_Job::runJobs_post($mode);
// lets release the global cron lock if we do have one
if ( $gotCronLock ) {
$cronLock->release( );
}
CRM_Core_Error::debug_log_message( 'Ending processQueue run' );
return true;
}
private function addMultipleEmails( $mailingID ) {
$sql = "
INSERT INTO civicrm_mailing_recipients
(mailing_id, email_id, contact_id)
SELECT %1, e.id, e.contact_id FROM civicrm_email e
WHERE e.on_hold = 0
AND e.is_bulkmail = 1
AND e.contact_id IN
( SELECT contact_id FROM civicrm_mailing_recipients mr WHERE mailing_id = %1 )
AND e.id NOT IN ( SELECT email_id FROM civicrm_mailing_recipients mr WHERE mailing_id = %1 )
";
$params = array( 1 => array( $mailingID, 'Integer' ) );
$dao = CRM_Core_DAO::executeQuery( $sql, $params );
}
function getMailingsList( $isSMS = false ) {
static $list = array( );
$where = " WHERE ";
if( !$isSMS ) {
$where .= " civicrm_mailing.sms_provider_id IS NULL ";
} else {
$where .= " civicrm_mailing.sms_provider_id IS NOT NULL ";
}
if ( empty($list) ) {
$query = "
SELECT civicrm_mailing.id, civicrm_mailing.name, civicrm_mailing_job.end_date
FROM civicrm_mailing
INNER JOIN civicrm_mailing_job ON civicrm_mailing.id = civicrm_mailing_job.mailing_id {$where}
ORDER BY civicrm_mailing.name";
$mailing = CRM_Core_DAO::executeQuery($query);
while($mailing->fetch()) {
$list[mysql_real_escape_string($mailing->id)] = "{$mailing->name} :: {$mailing->end_date}";
}
}
return $list;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment