All pastes #1193084 Raw Edit

Mailing.php

public text v1 · immutable
#1193084 ·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_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_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_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( );
        }
    }



    /**
     * 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;
    }
    
}

?>