All pastes #1193087 Raw Edit

Mailing.php

public php v1 · immutable
#1193087 ·published 2008-09-03 22:13 UTC
rendered paste body
<?php/* +--------------------------------------------------------------------+ | CiviCRM version 2.0                                                | +--------------------------------------------------------------------+ | Copyright CiviCRM LLC (c) 2004-2007                                | +--------------------------------------------------------------------+ | 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.                                       | |                                                                    | | 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 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-2007 * $Id$ * */require_once 'Mail/mime.php';require_once 'CRM/Contact/BAO/SavedSearch.php';require_once 'CRM/Contact/BAO/Query.php';require_once 'CRM/Contact/BAO/Group.php';require_once 'CRM/Mailing/DAO/Mailing.php';require_once 'CRM/Mailing/DAO/Group.php';require_once 'CRM/Mailing/Event/BAO/Queue.php';require_once 'CRM/Mailing/Event/BAO/Delivered.php';require_once 'CRM/Mailing/Event/BAO/Bounce.php';require_once 'CRM/Mailing/BAO/TrackableURL.php';require_once 'CRM/Mailing/BAO/Component.php';require_once 'CRM/Mailing/BAO/Spool.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;    /**     * 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( );    }    /**     * Find all intended recipients of a mailing     *     * @param int  $job_id            Job ID     * @param bool $includeDelivered  Whether to include the recipients who already got the mailing     * @return object                 A DAO loaded with results of the form (email_id, contact_id)     */    function &getRecipientsObject($job_id, $includeDelivered = false) {        $eq = self::getRecipients($job_id, $includeDelivered, $this->id);        return $eq;    }        function &getRecipientsCount($job_id, $includeDelivered = false, $mailing_id = null) {        $eq = self::getRecipients($job_id, $includeDelivered, $mailing_id);        return $eq->N;    }        function &getRecipients($job_id, $includeDelivered = false, $mailing_id = 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();        $contact    = CRM_Contact_DAO_Contact::getTableName();        require_once 'CRM/Contact/DAO/Group.php';        $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 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);                $ss =& new CRM_Core_DAO();        $ss->query(                "SELECT             $group.saved_search_id as saved_search_id,                                    $group.id as id                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             $group.saved_search_id IS NOT null");        $whereTables = array( );        while ( $ss->fetch( ) ) {            /* run the saved search query and dump result contacts into the temp             * table */            $tables = array($contact => 1);            $sql = CRM_Contact_BAO_SavedSearch::contactIDsSQL( $ss->saved_search_id );            $sql = $sql. " AND contact_a.id NOT IN (                               SELECT contact_id FROM $g2contact                               WHERE $g2contact.group_id = {$ss->id} AND $g2contact.status = 'Removed')";                         $mailingGroup->query( "INSERT IGNORE INTO X_$job_id (contact_id) $sql" );        }        /* Get all the group contacts we want to include */                $mailingGroup->query(            "CREATE TEMPORARY TABLE I_$job_id             (email_id int, contact_id int primary key)            ENGINE=HEAP"        );                /* Get the group contacts, but only those which are not in the         * exclusion temp table */        /* Get the emails with no override */                $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             $g2contact.status = 'Added'                        AND             $g2contact.email_id IS null                        AND             $contact.do_not_email = 0                        AND             $contact.is_opt_out = 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";        $mailingGroup->query($query);        /* Query prior mailings */        $mailingGroup->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            ($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");        /* Construct the saved-search queries */        $ss->query("SELECT          $group.saved_search_id as saved_search_id,                                    $group.id as id                    FROM            $group                    INNER JOIN      $mg                            ON      $mg.entity_id = $group.id                                AND $mg.entity_table = '$group'                    WHERE                                                   $mg.group_type = 'Include'                        AND         $mg.mailing_id = {$mailing_id}                        AND         $group.saved_search_id IS NOT null");        $whereTables = array( );        while ($ss->fetch()) {            $tables = array($contact => 1, $location => 1, $email => 1);            list( $from, $where ) = CRM_Contact_BAO_SavedSearch::fromWhereEmail( $ss->saved_search_id );            $where = trim( $where );            if ( $where ) {                $where = " AND $where ";            }            $ssq = "INSERT IGNORE INTO  I_$job_id (email_id, contact_id)                    SELECT DISTINCT     $email.id as email_id,                                        contact_a.id as contact_id                     $from                    LEFT JOIN           $email                            ON          $email.contact_id = contact_a.id                    LEFT JOIN           X_$job_id                            ON          contact_a.id = X_$job_id.contact_id                    WHERE                                                   contact_a.do_not_email = 0                        AND             contact_a.is_opt_out = 0                        AND             ($email.is_bulkmail = 1 OR $email.is_primary = 1)                        AND             $email.on_hold = 0                                        $where                        AND             contact_a.id NOT IN (                                           SELECT contact_id FROM $g2contact                                           WHERE $g2contact.group_id = {$ss->id} AND $g2contact.status = 'Removed')                         AND             X_$job_id.contact_id IS null                    ORDER BY $email.is_bulkmail";            $mailingGroup->query($ssq);        }                /* 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             $g2contact.email_id is null                        AND             $contact.do_not_email = 0                        AND             $contact.is_opt_out = 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";        $mailingGroup->query($query);                            /* Get the emails with full override */        $mailingGroup->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          $g2contact                            ON          $email.id = $g2contact.email_id                    INNER JOIN          $contact                            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             $g2contact.email_id IS NOT null                        AND             $contact.do_not_email = 0                        AND             $contact.is_opt_out = 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");                                $results = array();        $eq =& new CRM_Mailing_Event_BAO_Queue();                $eq->query("SELECT contact_id, email_id                     FROM I_$job_id                     ORDER BY contact_id, email_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)     {        $funcStruct = array('type' => null,'token' => $token);        $matches = array();        if ( ( preg_match('/^href/i',$token) || preg_match('/^http/i',$token) ) && $this->url_tracking ) {            // 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);                }            } else {                $funcStruct['type'] = 'url';            }                    } else if ( preg_match('/^\{(domain)\.(\w+)\}$/',$token, $matches) ) {                        $funcStruct['type'] = $matches[1];            $funcStruct['token'] = $matches[2];                    } else if ( preg_match('/^\{(action)\.(\w+)\}$/',$token, $matches) ) {                      $funcStruct['type'] = $matches[1];            $funcStruct['token'] = $matches[2];                    } else if ( preg_match('/^\{(mailing)\.(\w+)\}$/',$token,$matches) ) {                        $funcStruct['type'] = $matches[1];            $funcStruct['token'] = $matches[2];                    } else if ( preg_match('/^\{(contact)\.(\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['text'] = $this->getPatterns();            $templates = $this->getTemplates();                        $this->preparedTemplates = array();                        foreach (array('html','text') 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( )    {        require_once('CRM/Utils/String.php');        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 tepalte from the html template if the texttempalte did not exist              // this way we ensure that every recipient will receive n 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'] );              }          }      }      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() );                        if ($this->body_html) {                $this->_getTokens('html');            }                        if ($this->body_text) {                $this->_getTokens('text');            }                    }        return $this->tokens;          }        /**     *     *  _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();        $matches = array();        preg_match_all( '/(?<!\{|\\\\)\{(\w+\.\w+)\}(?!\})/',                        $templates[$prop],                        $matches,                        PREG_PATTERN_ORDER);                if ( $matches[1] ) {            foreach ( $matches[1] as $token ) {                list($type,$name) = split( '\.', $token, 2 );                if ( $name ) {                    if ( ! isset( $this->tokens[$prop][$type] ) ) {                        $this->tokens[$prop][$type] = array( );                    }                    $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)     {        $session    =& CRM_Core_Session::singleton();                if ($testParams['test_email']) {            /* First, find out if the contact already exists */              $query = "                  SELECT DISTINCT contact_a.id as contact_id                   FROM civicrm_contact contact_a                   LEFT JOIN civicrm_email      ON contact_a.id = civicrm_email.contact_id                      WHERE LOWER(civicrm_email.email) = %1";                        $params = array( 1 => array( $testParams['test_email'], 'String' ) );            $dao =& CRM_Core_DAO::executeQuery( $query, $params );            $id = array( );            // lets just use the first contact id we got            if ( $dao->fetch( ) ) {                $contact_id = $dao->contact_id;            }            $dao->free( );                        $userID = $session->get('userID');            $params = array( 1 => array($testParams['test_email'], 'String' ) );                        if ( ! $contact_id ) {                $query = "INSERT INTO   civicrm_email (contact_id, email) values ($userID,%1)";                 CRM_Core_DAO::executeQuery( $query, $params );                $contact_id = $userID;            }             $query = "SELECT        civicrm_email.id                       FROM civicrm_email                      WHERE         civicrm_email.email = %1";                        $dao =& CRM_Core_DAO::executeQuery( $query, $params);            if ($dao->fetch( ) ) {                $email_id = $dao->id;            }            $dao->free( );            $params = array(                            'job_id'        => $testParams['job_id'],                            'email_id'      => $email_id,                            'contact_id'    => $contact_id                            );            CRM_Mailing_Event_BAO_Queue::create($params);          }                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_bulkmailFROM civicrm_emailINNER JOIN civicrm_contact ON civicrm_email.contact_id = civicrm_contact.idWHERE civicrm_email.is_bulkmail = 1AND civicrm_contact.id = {$contact->contact_id}AND civicrm_contact.do_not_email =0AND civicrm_email.on_hold = 0AND 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_bulkmailFROM civicrm_emailINNER JOIN civicrm_contact ON civicrm_email.contact_id = civicrm_contact.idWHERE civicrm_email.is_primary = 1AND civicrm_contact.id = {$contact->contact_id}AND civicrm_contact.do_not_email =0AND civicrm_email.on_hold = 0AND 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( );        }    }    /**     * 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()        require_once 'CRM/Core/BAO/Domain.php';        $config =& CRM_Core_Config::singleton();        $bao =& new CRM_Mailing_BAO_Mailing();        $bao->domain_id = $config->domainID();        $bao->_domain =& CRM_Core_BAO_Domain::getDomainByID($bao->domain_id);        $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 )    {        $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( );        foreach (array('reply', 'bounce', 'unsubscribe', 'resubscribe', 'optOut') as $key) {            $verp[$key] = implode($config->verpSeparator,                                  array(                                        $key,                                         $this->domain_id,                                        $job_id,                                         $event_queue_id,                                        $hash                                        )                                  ) . '@' . $this->_domain->email_domain;        }        $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),                      );        $headers = array(                         'Reply-To'  => CRM_Utils_Verp::encode($verp['reply'], $email),                         'Return-Path' => CRM_Utils_Verp::encode($verp['bounce'], $email),                         'From'      => "\"{$this->from_name}\" <{$this->from_email}>",                         'Subject'   => $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?     * @return object               The mail object     * @access public     */    public function &compose($job_id, $event_queue_id, $hash, $contactId,                              $email, &$recipient, $test = false,                              $contactDetails = null )     {                require_once 'api/v2/Contact.php';        require_once 'CRM/Utils/Token.php';        $config =& CRM_Core_Config::singleton( );        $knownTokens = $this->getTokens();                if ($this->_domain == null) {            require_once 'CRM/Core/BAO/Domain.php';            $this->_domain =& CRM_Core_BAO_Domain::getDomainByID($this->domain_id);        }        list( $verp, $urls, $headers) = $this->getVerpAndUrlsAndHeaders($job_id, $event_queue_id, $hash, $email);        if ( $contactDetails ) {            $contact = $contactDetails;        } else {            $params  = array( 'contact_id' => $contactId );            $contact =& civicrm_contact_get( $params );            if ( is_a( $contact, 'CRM_Core_Error' ) ) {                return null;            }        }        $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 ($text && ( $test || $contact['preferred_mail_format'] == 'Text' ||                       $contact['preferred_mail_format'] == 'Both' ||                       ( $contact['preferred_mail_format'] == 'HTML' && !array_key_exists('html',$pEmails) ) ) ) {            $message->setTxtBody( wordwrap(join( '', $text ),200) );        }                if ( $html && ( $test ||  ( $contact['preferred_mail_format'] == 'HTML' ||                                    $contact['preferred_mail_format'] == 'Both') ) ) {            $message->setHTMLBody( wordwrap(join( '', $html ),200) );        }        $recipient = "\"{$contact['display_name']}\" <$email>";        $headers['To'] = $recipient;                $mailMimeParams = array(                                'text_encoding' => '8bit',                                'html_encoding' => '8bit',                                'head_charset'  => 'utf-8',                                'text_charset'  => 'utf-8',                                'html_charset'  => 'utf-8',                                );                $message->get($mailMimeParams);        $message->headers($headers);                // 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 )    {        require_once 'CRM/Core/BAO/Domain.php';        $domain =& CRM_Core_BAO_Domain::getDomainByID($mailing->domain_id);        foreach ( array('text', 'html') as $type ) {            require_once 'CRM/Utils/Token.php';            $tokens = $mailing->getTokens();            $mailing->templates[$type] =  CRM_Utils_Token::replaceSubscribeInviteTokens($mailing->templates[$type]);            $mailing->templates[$type] = CRM_Utils_Token::replaceDomainTokens($mailing->templates[$type], $domain, null, $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;        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];            }            $data = CRM_Mailing_BAO_TrackableURL::getTrackerURL($url, $this->id, $event_queue_id);                    } else if ( $type == 'url' ) {            $data = CRM_Mailing_BAO_TrackableURL::getTrackerURL($token, $this->id, $event_queue_id);        } else if ( $type == 'contact' ) {          $data = CRM_Utils_Token::getContactTokenReplacement($token, $contact);        } else if ( $type == 'action' ) {          $data = CRM_Utils_Token::getActionTokenReplacement($token, $verp, $urls, $html);        }         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.name 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;        }        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 )    {        require_once 'CRM/Utils/Hook.php';                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->domain_id = CRM_Core_Config::domainID( );        $mailing->id = CRM_Utils_Array::value( 'mailing_id', $ids );                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 )     {        require_once 'CRM/Core/Transaction.php';        $transaction = new CRM_Core_Transaction( );                $mailing = self::add($params, $ids);        if( is_a( $mailing, 'CRM_Core_Error') ) {            $transaction->rollback( );            return $mailing;        }                require_once 'CRM/Contact/BAO/Group.php';        /* Create the mailing group record */        $mg =& new CRM_Mailing_DAO_Group();        foreach( array( 'groups', 'mailings' ) as $entity ) {            foreach( array( 'include', 'exclude' ) as $type ) {                                if( is_array( $params[$entity][$type] ) ) {                                        foreach( $params[$entity][$type] as $entityId ) {                        $mg->reset( );                        $mg->mailing_id = $mailing->id;                                                $mg->entity_table   = ( $entity == 'groups' )                                             ? CRM_Contact_BAO_Group::getTableName( )                                            : CRM_Mailing_BAO_Mailing::getTableName( );                        $mg->entity_id = $entityId;                        $mg->group_type = $type;                        $mg->save( );                    }                }            }        }        $transaction->commit( );        return $mailing;    }    /**     * Generate a report.  Fetch event count information, mailing data, and job     * status.     *     * @param int $id       The mailing id to report     * @return array        Associative array of reporting data     * @access public     * @static     */    public static function &report($id) {        $mailing_id = CRM_Utils_Type::escape($id, 'Integer');                $mailing =& new CRM_Mailing_BAO_Mailing();        require_once 'CRM/Mailing/Event/BAO/Opened.php';        require_once 'CRM/Mailing/Event/BAO/Reply.php';        require_once 'CRM/Mailing/Event/BAO/Unsubscribe.php';        require_once 'CRM/Mailing/Event/BAO/Forward.php';        require_once 'CRM/Mailing/Event/BAO/TrackableURLOpen.php';        require_once 'CRM/Mailing/BAO/Spool.php';        $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();                        /* FIXME: put some permissioning in here */        /* Get the mailing info */        $mailing->query("            SELECT          {$t['mailing']}.*            FROM            {$t['mailing']}            WHERE           {$t['mailing']}.id = $mailing_id");                    $mailing->fetch();                $report['mailing'] = array();        foreach (array_keys(self::fields()) as $field) {            $report['mailing'][$field] = $mailing->$field;        }        /* 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['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());        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']}");            }                        if ($mailing->group_type == 'Include') {                $report['group']['include'][] = $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', 'bounce', 'spool' );        // initialize various counters        foreach ( $elements as $field ) {            $report['event_totals'][$field] = 0;        }        $report['event_totals']['opened'] = $report['event_totals']['unsubscribe'] = 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 );            $report['event_totals']['unsubscribe'] += $row['unsubscribe'];                        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;            } else {                $row['delivered_rate'] = 0;                $row['bounce_rate'] = 0;                $row['unsubscribe_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;        }        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'];        } else {            $report['event_totals']['delivered_rate'] = 0;            $report['event_totals']['bounce_rate'] = 0;            $report['event_totals']['unsubscribe_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"            ),        );        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->domain_id = $session->get('domainID');                $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( ) {        $mailingACL = " ( 0 ) ";        $mailingIDs =& self::mailingACLIDs( );        if ( ! empty( $mailingIDs ) ) {            $mailingIDs = implode( ',', $mailingIDs );            $mailingACL = " civicrm_mailing.id IN ( $mailingIDs ) ";        }        return $mailingACL;    }    static function &mailingACLIDs( ) {        $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 ) );            // get all the mailings that are in this subset of groups            $query = "SELECT DISTINCT( m.id ) as id  FROM civicrm_mailing m,       civicrm_mailing_group g WHERE g.mailing_id   = m.id   AND g.entity_table = 'civicrm_group'   AND g.entity_id IN ( $groupIDs )";            $dao = CRM_Core_DAO::executeQuery( $query, CRM_Core_DAO::$_nullArray );            $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();        $domain_id  = $session->get('domainID');        $mailingACL = self::mailingACL( );        $query = "            SELECT      $mailing.id,                        $mailing.name,                         $job.status,                         MIN($job.scheduled_date) as scheduled_date,                         MIN($job.start_date) as start_date,                        MAX($job.end_date) as end_date,                        from_email, from_name            FROM        $mailing                         LEFT JOIN $job ON ( $job.mailing_id = $mailing.id AND $job.is_test = 0)            WHERE       $mailing.domain_id = $domain_id              AND       $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'        => CRM_Mailing_BAO_Job::status($dao->status),                             '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),                            'from_email'    => $dao->from_email,                            'from_name'     => $dao->from_name,                            );        }        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( );        }        $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'] );        }        $returnProperties = array( );        $returnProperties['display_name'] =             $returnProperties['contact_id'] = $returnProperties['preferred_mail_format'] = 1;        foreach ( $properties as $p ) {            $returnProperties[$p] = 1;        }        return $returnProperties;    }        /**     * gives required details of contacts      *     * @param  array  $contactIds       of conatcts     * @param  array  $returnProperties of required properties     *     * @return array     * @access public     */    function getDetails($contactIds, $returnProperties)     {        $params = array( );        foreach ( $contactIds  as $key => $contactID ) {            $params[] = array( CRM_Core_Form::CB_PREFIX . $contactID,                               '=', 1, 0, 1);        }                $custom = array( );        foreach ( $returnProperties as $name => $dontCare ) {            $cfID = CRM_Core_BAO_CustomField::getKeyID( $name );            if ( $cfID ) {                $custom[] = $cfID;            }        }        //get the total number of contacts to fetch from database.        $numberofContacts = count( $contactIds );                require_once 'CRM/Contact/BAO/Query.php';                $query   =& new CRM_Contact_BAO_Query( $params, $returnProperties );        $details = $query->apiQuery( $params, $returnProperties, NULL, NULL, 0, $numberofContacts );                $contactDetails =& $details[0];                 foreach ( $contactIds as $key => $contactID ) {            if ( CRM_Utils_Array::value('preferred_communication_method',$returnProperties) == 1 ) {                                require_once 'CRM/Core/PseudoConstant.php';                $pcm = CRM_Core_PseudoConstant::pcm();                                // communication Prefferance                                require_once 'CRM/Core/BAO/CustomOption.php';                $contactPcm = explode(CRM_Core_BAO_CustomOption::VALUE_SEPERATOR,$contactDetails[$contactID]['preferred_communication_method']);                                foreach ( $contactPcm as $key => $val) {                    if ($val) {                        $result[$val] = $pcm[$val];                    }                 }                                $contactDetails[$contactID]['preferred_communication_method'] = implode(', ',$result);                unset($result);            }                        foreach ( $custom as $cfID ) {                if ( isset ( $contactDetails[$contactID]["custom_{$cfID}"] ) ) {                    $contactDetails[$contactID]["custom_{$cfID}"] =                         CRM_Core_BAO_CustomField::getDisplayValue( $contactDetails[$contactID]["custom_{$cfID}"],$cfID, $details[1] );                }            }        }        return $details;    }    }?>