public function preProcess() { $this->set('searchFormName', 'Custom'); $this->set('context', 'custom'); $csID = CRM_Utils_Request::retrieve('csid', 'Integer', $this); $ssID = CRM_Utils_Request::retrieve('ssID', 'Integer', $this); $gID = CRM_Utils_Request::retrieve('gid', 'Integer', $this); list($this->_customSearchID, $this->_customSearchClass, $formValues) = CRM_Contact_BAO_SearchCustom::details($csID, $ssID, $gID); if (!$this->_customSearchID) { CRM_Core_Error::fatal('Could not get details for custom search.'); } // stash this as a hidden element so we can potentially go there if the session // is reset but this is available in the POST $this->addElement('hidden', 'csid', $csID); if (!empty($formValues)) { $this->_formValues = $formValues; } // set breadcrumb to return to Custom Search listings page $breadCrumb = array(array('title' => ts('Custom Searches'), 'url' => CRM_Utils_System::url('civicrm/contact/search/custom/list', 'reset=1'))); CRM_Utils_System::appendBreadCrumb($breadCrumb); // use the custom selector self::$_selectorName = 'CRM_Contact_Selector_Custom'; $this->set('customSearchID', $this->_customSearchID); $this->set('customSearchClass', $this->_customSearchClass); parent::preProcess(); // instantiate the new class $this->_customClass = new $this->_customSearchClass($this->_formValues); // CRM-12747 if (isset($this->_customClass->_permissionedComponent) && !self::isPermissioned($this->_customClass->_permissionedComponent)) { CRM_Utils_System::permissionDenied(); } }
public function preProcess() { require_once 'CRM/Contact/BAO/SearchCustom.php'; $csID = CRM_Utils_Request::retrieve('csid', 'Integer', $this); $ssID = CRM_Utils_Request::retrieve('ssID', 'Integer', $this); $gID = CRM_Utils_Request::retrieve('gid', 'Integer', $this); list($this->_customSearchID, $this->_customSearchClass, $formValues) = CRM_Contact_BAO_SearchCustom::details($csID, $ssID, $gID); if (!$this->_customSearchID) { CRM_Core_Error::fatal('Could not get details for custom search.'); } if (!empty($formValues)) { $this->_formValues = $formValues; } // set breadcrumb to return to Custom Search listings page $breadCrumb = array(array('title' => ts('Custom Searches'), 'url' => CRM_Utils_System::url('civicrm/contact/search/custom/list', 'reset=1'))); CRM_Utils_System::appendBreadCrumb($breadCrumb); // use the custom selector require_once 'CRM/Contact/Selector/Custom.php'; $this->_selectorName = 'CRM_Contact_Selector_Custom'; $this->set('customSearchID', $this->_customSearchID); $this->set('customSearchClass', $this->_customSearchClass); parent::preProcess(); // instantiate the new class eval('$this->_customClass = new ' . $this->_customSearchClass . '( $this->_formValues );'); }
/** * Get from where email (whatever that means!). * * @param int $id * * @return array */ public static function fromWhereEmail($id) { $params = self::getSearchParams($id); if ($params) { if (!empty($params['customSearchID'])) { return CRM_Contact_BAO_SearchCustom::fromWhereEmail(NULL, $id); } else { $tables = $whereTables = array('civicrm_contact' => 1, 'civicrm_email' => 1); $where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables); $from = CRM_Contact_BAO_Query::fromClause($whereTables); return array($from, $where); } } else { // fix for CRM-7240 $from = "\nFROM civicrm_contact contact_a\nLEFT JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id AND civicrm_email.is_primary = 1)\n"; $where = " ( 1 ) "; $tables['civicrm_contact'] = $whereTables['civicrm_contact'] = 1; $tables['civicrm_email'] = $whereTables['civicrm_email'] = 1; return array($from, $where); } }
/** * Load the smart group cache for a saved search. * * @param object $group * The smart group that needs to be loaded. * @param bool $force * Should we force a search through. */ public static function load(&$group, $force = FALSE) { $groupID = $group->id; $savedSearchID = $group->saved_search_id; if (array_key_exists($groupID, self::$_alreadyLoaded) && !$force) { return; } // grab a lock so other processes don't compete and do the same query $lock = Civi::lockManager()->acquire("data.core.group.{$groupID}"); if (!$lock->isAcquired()) { // this can cause inconsistent results since we don't know if the other process // will fill up the cache before our calling routine needs it. // however this routine does not return the status either, so basically // its a "lets return and hope for the best" return; } self::$_alreadyLoaded[$groupID] = 1; // we now have the lock, but some other process could have actually done the work // before we got here, so before we do any work, lets ensure that work needs to be // done // we allow hidden groups here since we dont know if the caller wants to evaluate an // hidden group if (!$force && !self::shouldGroupBeRefreshed($groupID, TRUE)) { $lock->release(); return; } $sql = NULL; $idName = 'id'; $customClass = NULL; if ($savedSearchID) { $ssParams = CRM_Contact_BAO_SavedSearch::getSearchParams($savedSearchID); // rectify params to what proximity search expects if there is a value for prox_distance // CRM-7021 if (!empty($ssParams)) { CRM_Contact_BAO_ProximityQuery::fixInputParams($ssParams); } $returnProperties = array(); if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $savedSearchID, 'mapping_id')) { $fv = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); $returnProperties = CRM_Core_BAO_Mapping::returnProperties($fv); } if (isset($ssParams['customSearchID'])) { // if custom search // we split it up and store custom class // so temp tables are not destroyed if they are used // hence customClass is defined above at top of function $customClass = CRM_Contact_BAO_SearchCustom::customClass($ssParams['customSearchID'], $savedSearchID); $searchSQL = $customClass->contactIDs(); $searchSQL = str_replace('ORDER BY contact_a.id ASC', '', $searchSQL); if (!strstr($searchSQL, 'WHERE')) { $searchSQL .= " WHERE ( 1 ) "; } $idName = 'contact_id'; } else { $formValues = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); // CRM-17075 using the formValues in this way imposes extra logic and complexity. // we have the where_clause and where tables stored in the saved_search table // and should use these rather than re-processing the form criteria (which over-works // the link between the form layer & the query layer too). // It's hard to think of when you would want to use anything other than return // properties = array('contact_id' => 1) here as the point would appear to be to // generate the list of contact ids in the group. // @todo review this to use values in saved_search table (preferably for 4.8). $query = new CRM_Contact_BAO_Query($ssParams, $returnProperties, NULL, FALSE, FALSE, 1, TRUE, TRUE, FALSE, CRM_Utils_Array::value('display_relationship_type', $formValues), CRM_Utils_Array::value('operator', $formValues, 'AND')); $query->_useDistinct = FALSE; $query->_useGroupBy = FALSE; $searchSQL = $query->searchQuery(0, 0, NULL, FALSE, FALSE, FALSE, TRUE, TRUE, NULL, NULL, NULL, TRUE); } $groupID = CRM_Utils_Type::escape($groupID, 'Integer'); $sql = $searchSQL . " AND contact_a.id NOT IN (\n SELECT contact_id FROM civicrm_group_contact\n WHERE civicrm_group_contact.status = 'Removed'\n AND civicrm_group_contact.group_id = {$groupID} ) "; } if ($sql) { $sql = preg_replace("/^\\s*SELECT/", "SELECT {$groupID} as group_id, ", $sql); } // lets also store the records that are explicitly added to the group // this allows us to skip the group contact LEFT JOIN $sqlB = "\nSELECT {$groupID} as group_id, contact_id as {$idName}\nFROM civicrm_group_contact\nWHERE civicrm_group_contact.status = 'Added'\n AND civicrm_group_contact.group_id = {$groupID} "; $groupIDs = array($groupID); self::remove($groupIDs); $processed = FALSE; $tempTable = 'civicrm_temp_group_contact_cache' . rand(0, 2000); foreach (array($sql, $sqlB) as $selectSql) { if (!$selectSql) { continue; } $insertSql = "CREATE TEMPORARY TABLE {$tempTable} ({$selectSql});"; $processed = TRUE; CRM_Core_DAO::executeQuery($insertSql); CRM_Core_DAO::executeQuery("INSERT IGNORE INTO civicrm_group_contact_cache (contact_id, group_id)\n SELECT DISTINCT {$idName}, group_id FROM {$tempTable}\n "); CRM_Core_DAO::executeQuery(" DROP TEMPORARY TABLE {$tempTable}"); } self::updateCacheTime($groupIDs, $processed); if ($group->children) { //Store a list of contacts who are removed from the parent group $sql = "\nSELECT contact_id\nFROM civicrm_group_contact\nWHERE civicrm_group_contact.status = 'Removed'\nAND civicrm_group_contact.group_id = {$groupID} "; $dao = CRM_Core_DAO::executeQuery($sql); $removed_contacts = array(); while ($dao->fetch()) { $removed_contacts[] = $dao->contact_id; } $childrenIDs = explode(',', $group->children); foreach ($childrenIDs as $childID) { $contactIDs = CRM_Contact_BAO_Group::getMember($childID, FALSE); //Unset each contact that is removed from the parent group foreach ($removed_contacts as $removed_contact) { unset($contactIDs[$removed_contact]); } $values = array(); foreach ($contactIDs as $contactID => $dontCare) { $values[] = "({$groupID},{$contactID})"; } self::store($groupIDs, $values); } } $lock->release(); }
/** * note that $job_id is used only as a variable in the temp table construction * and does not play a role in the queries generated * @param int $job_id * (misnomer) a nonce value used to name temporary tables. * @param int $mailing_id * @param bool $storeRecipients * @param bool $dedupeEmail * @param null $mode * * @return CRM_Mailing_Event_BAO_Queue|string */ public static function getRecipients($job_id, $mailing_id = NULL, $storeRecipients = FALSE, $dedupeEmail = FALSE, $mode = NULL) { $mailingGroup = new CRM_Mailing_DAO_MailingGroup(); $mailing = CRM_Mailing_BAO_Mailing::getTableName(); $job = CRM_Mailing_BAO_MailingJob::getTableName(); $mg = CRM_Mailing_DAO_MailingGroup::getTableName(); $eq = CRM_Mailing_Event_DAO_Queue::getTableName(); $email = CRM_Core_DAO_Email::getTableName(); if ($mode == 'sms') { $phone = CRM_Core_DAO_Phone::getTableName(); } $contact = CRM_Contact_DAO_Contact::getTableName(); $group = CRM_Contact_DAO_Group::getTableName(); $g2contact = CRM_Contact_DAO_GroupContact::getTableName(); $m = new CRM_Mailing_DAO_Mailing(); $m->id = $mailing_id; $m->find(TRUE); $email_selection_method = $m->email_selection_method; $location_type_id = $m->location_type_id; // Note: When determining the ORDER that results are returned, it's // the record that comes last that counts. That's because we are // INSERT'ing INTO a table with a primary id so that last record // over writes any previous record. switch ($email_selection_method) { case 'location-exclude': $location_filter = "({$email}.location_type_id != {$location_type_id})"; // If there is more than one email that doesn't match the location, // prefer the one marked is_bulkmail, followed by is_primary. $order_by = "ORDER BY {$email}.is_bulkmail, {$email}.is_primary"; break; case 'location-only': $location_filter = "({$email}.location_type_id = {$location_type_id})"; // If there is more than one email of the desired location, prefer // the one marked is_bulkmail, followed by is_primary. $order_by = "ORDER BY {$email}.is_bulkmail, {$email}.is_primary"; break; case 'location-prefer': $location_filter = "({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1 OR {$email}.location_type_id = {$location_type_id})"; // ORDER BY is more complicated because we have to set an arbitrary // order that prefers the location that we want. We do that using // the FIELD function. For more info, see: // https://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_field // We assign the location type we want the value "1" by putting it // in the first position after we name the field. All other location // types are left out, so they will be assigned the value 0. That // means, they will all be equally tied for first place, with our // location being last. $order_by = "ORDER BY FIELD({$email}.location_type_id, {$location_type_id}), {$email}.is_bulkmail, {$email}.is_primary"; break; case 'automatic': // fall through to default // fall through to default default: $location_filter = "({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)"; $order_by = "ORDER BY {$email}.is_bulkmail"; } /* Create a temp table for contact exclusion */ $mailingGroup->query("CREATE TEMPORARY TABLE X_{$job_id}\n (contact_id int primary key)\n ENGINE=HEAP"); /* Add all the members of groups excluded from this mailing to the temp * table */ $excludeSubGroup = "INSERT INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$g2contact}.contact_id\n FROM {$g2contact}\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id AND {$mg}.entity_table = '{$group}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$g2contact}.status = 'Added'\n AND {$mg}.group_type = 'Exclude'"; $mailingGroup->query($excludeSubGroup); /* Add all unsubscribe members of base group from this mailing to the temp * table */ $unSubscribeBaseGroup = "INSERT INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$g2contact}.contact_id\n FROM {$g2contact}\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id AND {$mg}.entity_table = '{$group}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$g2contact}.status = 'Removed'\n AND {$mg}.group_type = 'Base'"; $mailingGroup->query($unSubscribeBaseGroup); /* Add all the (intended) recipients of an excluded prior mailing to * the temp table */ $excludeSubMailing = "INSERT IGNORE INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$eq}.contact_id\n FROM {$eq}\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$mg}.group_type = 'Exclude'"; $mailingGroup->query($excludeSubMailing); // get all the saved searches AND hierarchical groups // and load them in the cache $sql = "\nSELECT {$group}.id, {$group}.cache_date, {$group}.saved_search_id, {$group}.children\nFROM {$group}\nINNER JOIN {$mg} ON {$mg}.entity_id = {$group}.id\nWHERE {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Exclude'\n AND {$mg}.mailing_id = {$mailing_id}\n AND ( saved_search_id != 0\n OR saved_search_id IS NOT NULL\n OR children IS NOT NULL )\n"; $groupDAO = CRM_Core_DAO::executeQuery($sql); while ($groupDAO->fetch()) { if ($groupDAO->cache_date == NULL) { CRM_Contact_BAO_GroupContactCache::load($groupDAO); } $smartGroupExclude = "\nINSERT IGNORE INTO X_{$job_id} (contact_id)\nSELECT c.contact_id\nFROM civicrm_group_contact_cache c\nWHERE c.group_id = {$groupDAO->id}\n"; $mailingGroup->query($smartGroupExclude); } $tempColumn = 'email_id'; if ($mode == 'sms') { $tempColumn = 'phone_id'; } /* Get all the group contacts we want to include */ $mailingGroup->query("CREATE TEMPORARY TABLE I_{$job_id}\n ({$tempColumn} int, contact_id int primary key)\n ENGINE=HEAP"); /* Get the group contacts, but only those which are not in the * exclusion temp table */ $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n\n SELECT DISTINCT {$email}.id as email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n AND {$mg}.entity_table = '{$group}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$mg}.search_id IS NULL\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased <> 1\n AND {$location_filter}\n AND {$email}.email IS NOT NULL\n AND {$email}.email != ''\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n {$order_by}"; if ($mode == 'sms') { $phoneTypes = CRM_Core_OptionGroup::values('phone_type', TRUE, FALSE, FALSE, NULL, 'name'); $query = "REPLACE INTO I_{$job_id} (phone_id, contact_id)\n\n SELECT DISTINCT {$phone}.id as phone_id,\n {$contact}.id as contact_id\n FROM {$phone}\n INNER JOIN {$contact}\n ON {$phone}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n AND {$mg}.entity_table = '{$group}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$mg}.search_id IS NULL\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_sms = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased <> 1\n AND {$phone}.phone_type_id = {$phoneTypes['Mobile']}\n AND {$phone}.phone IS NOT NULL\n AND {$phone}.phone != ''\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($query); /* Query prior mailings */ $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n SELECT DISTINCT {$email}.id as email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$eq}\n ON {$eq}.contact_id = {$contact}.id\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased <> 1\n AND {$location_filter}\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n {$order_by}"; if ($mode == 'sms') { $query = "REPLACE INTO I_{$job_id} (phone_id, contact_id)\n SELECT DISTINCT {$phone}.id as phone_id,\n {$contact}.id as contact_id\n FROM {$phone}\n INNER JOIN {$contact}\n ON {$phone}.contact_id = {$contact}.id\n INNER JOIN {$eq}\n ON {$eq}.contact_id = {$contact}.id\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$contact}.do_not_sms = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased <> 1\n AND {$phone}.phone_type_id = {$phoneTypes['Mobile']}\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($query); $sql = "\nSELECT {$group}.id, {$group}.cache_date, {$group}.saved_search_id, {$group}.children\nFROM {$group}\nINNER JOIN {$mg} ON {$mg}.entity_id = {$group}.id\nWHERE {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$mg}.search_id IS NULL\n AND {$mg}.mailing_id = {$mailing_id}\n AND ( saved_search_id != 0\n OR saved_search_id IS NOT NULL\n OR children IS NOT NULL )\n"; $groupDAO = CRM_Core_DAO::executeQuery($sql); while ($groupDAO->fetch()) { if ($groupDAO->cache_date == NULL) { CRM_Contact_BAO_GroupContactCache::load($groupDAO); } $smartGroupInclude = "\nREPLACE INTO I_{$job_id} (email_id, contact_id)\nSELECT civicrm_email.id as email_id, c.id as contact_id\nFROM civicrm_contact c\nINNER JOIN civicrm_email ON civicrm_email.contact_id = c.id\nINNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = c.id\nLEFT JOIN X_{$job_id} ON X_{$job_id}.contact_id = c.id\nWHERE gc.group_id = {$groupDAO->id}\n AND c.do_not_email = 0\n AND c.is_opt_out = 0\n AND c.is_deceased <> 1\n AND {$location_filter}\n AND civicrm_email.on_hold = 0\n AND X_{$job_id}.contact_id IS null\n{$order_by}\n"; if ($mode == 'sms') { $smartGroupInclude = "\nREPLACE INTO I_{$job_id} (phone_id, contact_id)\nSELECT p.id as phone_id, c.id as contact_id\nFROM civicrm_contact c\nINNER JOIN civicrm_phone p ON p.contact_id = c.id\nINNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = c.id\nLEFT JOIN X_{$job_id} ON X_{$job_id}.contact_id = c.id\nWHERE gc.group_id = {$groupDAO->id}\n AND c.do_not_sms = 0\n AND c.is_opt_out = 0\n AND c.is_deceased <> 1\n AND p.phone_type_id = {$phoneTypes['Mobile']}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($smartGroupInclude); } /** * Construct the filtered search queries */ $query = "\nSELECT search_id, search_args, entity_id\nFROM {$mg}\nWHERE {$mg}.search_id IS NOT NULL\nAND {$mg}.mailing_id = {$mailing_id}\n"; $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { $customSQL = CRM_Contact_BAO_SearchCustom::civiMailSQL($dao->search_id, $dao->search_args, $dao->entity_id); $query = "REPLACE INTO I_{$job_id} ({$tempColumn}, contact_id)\n {$customSQL}"; $mailingGroup->query($query); } /* Get the emails with only location override */ $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n SELECT DISTINCT {$email}.id as local_email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased <> 1\n AND {$location_filter}\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n {$order_by}"; if ($mode == "sms") { $query = "REPLACE INTO I_{$job_id} (phone_id, contact_id)\n SELECT DISTINCT {$phone}.id as phone_id,\n {$contact}.id as contact_id\n FROM {$phone}\n INNER JOIN {$contact}\n ON {$phone}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_sms = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased <> 1\n AND {$phone}.phone_type_id = {$phoneTypes['Mobile']}\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($query); $eq = new CRM_Mailing_Event_BAO_Queue(); list($aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause(); $aclWhere = $aclWhere ? "WHERE {$aclWhere}" : ''; if ($storeRecipients && $mailing_id) { $sql = "\nDELETE\nFROM civicrm_mailing_recipients\nWHERE mailing_id = %1\n"; $params = array(1 => array($mailing_id, 'Integer')); CRM_Core_DAO::executeQuery($sql, $params); // CRM-3975 $groupBy = $groupJoin = ''; if ($dedupeEmail) { $groupJoin = " INNER JOIN civicrm_email e ON e.id = i.email_id"; $groupBy = " GROUP BY e.email "; } $sql = "\nINSERT INTO civicrm_mailing_recipients ( mailing_id, contact_id, {$tempColumn} )\nSELECT %1, i.contact_id, i.{$tempColumn}\nFROM civicrm_contact contact_a\nINNER JOIN I_{$job_id} i ON contact_a.id = i.contact_id\n {$groupJoin}\n {$aclFrom}\n {$aclWhere}\n {$groupBy}\nORDER BY i.contact_id, i.{$tempColumn}\n"; CRM_Core_DAO::executeQuery($sql, $params); // if we need to add all emails marked bulk, do it as a post filter // on the mailing recipients table if (CRM_Core_BAO_Email::isMultipleBulkMail()) { self::addMultipleEmails($mailing_id); } } /* Delete the temp table */ $mailingGroup->reset(); $mailingGroup->query("DROP TEMPORARY TABLE X_{$job_id}"); $mailingGroup->query("DROP TEMPORARY TABLE I_{$job_id}"); return $eq; }
static function fromWhereEmail($id) { $params =& self::getSearchParams($id); if ($params) { if (CRM_Utils_Array::value('customSearchID', $params)) { require_once 'CRM/Contact/BAO/SearchCustom.php'; return CRM_Contact_BAO_SearchCustom::fromWhereEmail(null, $id); } else { $tables = $whereTables = array('civicrm_contact' => 1, 'civicrm_email' => 1); $where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables); $from = CRM_Contact_BAO_Query::fromClause($whereTables); return array($from, $where); } } else { // fix for CRM-7240 $from = "\nFROM civicrm_contact contact_a \nLEFT JOIN civicrm_email ON (contact_a.id = civicrm_email.contact_id AND civicrm_email.is_primary = 1)\n"; $where = " ( 1 ) "; $tables['civicrm_contact'] = $whereTables['civicrm_contact'] = 1; $tables['civicrm_email'] = $whereTables['civicrm_email'] = 1; return array($from, $where); } }
static function &getRecipients($job_id, $mailing_id = NULL, $offset = NULL, $limit = NULL, $storeRecipients = FALSE, $dedupeEmail = FALSE, $mode = NULL) { $mailingGroup = new CRM_Mailing_DAO_MailingGroup(); $mailing = CRM_Mailing_BAO_Mailing::getTableName(); $job = CRM_Mailing_BAO_MailingJob::getTableName(); $mg = CRM_Mailing_DAO_MailingGroup::getTableName(); $eq = CRM_Mailing_Event_DAO_Queue::getTableName(); $ed = CRM_Mailing_Event_DAO_Delivered::getTableName(); $eb = CRM_Mailing_Event_DAO_Bounce::getTableName(); $email = CRM_Core_DAO_Email::getTableName(); if ($mode == 'sms') { $phone = CRM_Core_DAO_Phone::getTableName(); } $contact = CRM_Contact_DAO_Contact::getTableName(); $group = CRM_Contact_DAO_Group::getTableName(); $g2contact = CRM_Contact_DAO_GroupContact::getTableName(); /* Create a temp table for contact exclusion */ $mailingGroup->query("CREATE TEMPORARY TABLE X_{$job_id}\n (contact_id int primary key)\n ENGINE=HEAP"); /* Add all the members of groups excluded from this mailing to the temp * table */ $excludeSubGroup = "INSERT INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$g2contact}.contact_id\n FROM {$g2contact}\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id AND {$mg}.entity_table = '{$group}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$g2contact}.status = 'Added'\n AND {$mg}.group_type = 'Exclude'"; $mailingGroup->query($excludeSubGroup); /* Add all unsubscribe members of base group from this mailing to the temp * table */ $unSubscribeBaseGroup = "INSERT INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$g2contact}.contact_id\n FROM {$g2contact}\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id AND {$mg}.entity_table = '{$group}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$g2contact}.status = 'Removed'\n AND {$mg}.group_type = 'Base'"; $mailingGroup->query($unSubscribeBaseGroup); /* Add all the (intended) recipients of an excluded prior mailing to * the temp table */ $excludeSubMailing = "INSERT IGNORE INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$eq}.contact_id\n FROM {$eq}\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$mg}.group_type = 'Exclude'"; $mailingGroup->query($excludeSubMailing); // get all the saved searches AND hierarchical groups // and load them in the cache $sql = "\nSELECT {$group}.id, {$group}.cache_date, {$group}.saved_search_id, {$group}.children\nFROM {$group}\nINNER JOIN {$mg} ON {$mg}.entity_id = {$group}.id\nWHERE {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Exclude'\n AND {$mg}.mailing_id = {$mailing_id}\n AND ( saved_search_id != 0\n OR saved_search_id IS NOT NULL\n OR children IS NOT NULL )\n"; $groupDAO = CRM_Core_DAO::executeQuery($sql); while ($groupDAO->fetch()) { if ($groupDAO->cache_date == NULL) { CRM_Contact_BAO_GroupContactCache::load($groupDAO); } $smartGroupExclude = "\nINSERT IGNORE INTO X_{$job_id} (contact_id)\nSELECT c.contact_id\nFROM civicrm_group_contact_cache c\nWHERE c.group_id = {$groupDAO->id}\n"; $mailingGroup->query($smartGroupExclude); } $tempColumn = 'email_id'; if ($mode == 'sms') { $tempColumn = 'phone_id'; } /* Get all the group contacts we want to include */ $mailingGroup->query("CREATE TEMPORARY TABLE I_{$job_id}\n ({$tempColumn} int, contact_id int primary key)\n ENGINE=HEAP"); /* Get the group contacts, but only those which are not in the * exclusion temp table */ $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n\n SELECT DISTINCT {$email}.id as email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n AND {$mg}.entity_table = '{$group}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$mg}.search_id IS NULL\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.email IS NOT NULL\n AND {$email}.email != ''\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n ORDER BY {$email}.is_bulkmail"; if ($mode == 'sms') { $phoneTypes = CRM_Core_OptionGroup::values('phone_type', TRUE, FALSE, FALSE, NULL, 'name'); $query = "REPLACE INTO I_{$job_id} (phone_id, contact_id)\n\n SELECT DISTINCT {$phone}.id as phone_id,\n {$contact}.id as contact_id\n FROM {$phone}\n INNER JOIN {$contact}\n ON {$phone}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n AND {$mg}.entity_table = '{$group}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$mg}.search_id IS NULL\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_sms = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND {$phone}.phone_type_id = {$phoneTypes['Mobile']}\n AND {$phone}.phone IS NOT NULL\n AND {$phone}.phone != ''\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($query); /* Query prior mailings */ $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n SELECT DISTINCT {$email}.id as email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$eq}\n ON {$eq}.contact_id = {$contact}.id\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n ORDER BY {$email}.is_bulkmail"; if ($mode == 'sms') { $query = "REPLACE INTO I_{$job_id} (phone_id, contact_id)\n SELECT DISTINCT {$phone}.id as phone_id,\n {$contact}.id as contact_id\n FROM {$phone}\n INNER JOIN {$contact}\n ON {$phone}.contact_id = {$contact}.id\n INNER JOIN {$eq}\n ON {$eq}.contact_id = {$contact}.id\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include')\n AND {$contact}.do_not_sms = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND {$phone}.phone_type_id = {$phoneTypes['Mobile']}\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($query); $sql = "\nSELECT {$group}.id, {$group}.cache_date, {$group}.saved_search_id, {$group}.children\nFROM {$group}\nINNER JOIN {$mg} ON {$mg}.entity_id = {$group}.id\nWHERE {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$mg}.search_id IS NULL\n AND {$mg}.mailing_id = {$mailing_id}\n AND ( saved_search_id != 0\n OR saved_search_id IS NOT NULL\n OR children IS NOT NULL )\n"; $groupDAO = CRM_Core_DAO::executeQuery($sql); while ($groupDAO->fetch()) { if ($groupDAO->cache_date == NULL) { CRM_Contact_BAO_GroupContactCache::load($groupDAO); } $smartGroupInclude = "\nINSERT IGNORE INTO I_{$job_id} (email_id, contact_id)\nSELECT e.id as email_id, c.id as contact_id\nFROM civicrm_contact c\nINNER JOIN civicrm_email e ON e.contact_id = c.id\nINNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = c.id\nLEFT JOIN X_{$job_id} ON X_{$job_id}.contact_id = c.id\nWHERE gc.group_id = {$groupDAO->id}\n AND c.do_not_email = 0\n AND c.is_opt_out = 0\n AND c.is_deceased = 0\n AND (e.is_bulkmail = 1 OR e.is_primary = 1)\n AND e.on_hold = 0\n AND X_{$job_id}.contact_id IS null\nORDER BY e.is_bulkmail\n"; if ($mode == 'sms') { $smartGroupInclude = "\nINSERT IGNORE INTO I_{$job_id} (phone_id, contact_id)\nSELECT p.id as phone_id, c.id as contact_id\nFROM civicrm_contact c\nINNER JOIN civicrm_phone p ON p.contact_id = c.id\nINNER JOIN civicrm_group_contact_cache gc ON gc.contact_id = c.id\nLEFT JOIN X_{$job_id} ON X_{$job_id}.contact_id = c.id\nWHERE gc.group_id = {$groupDAO->id}\n AND c.do_not_sms = 0\n AND c.is_opt_out = 0\n AND c.is_deceased = 0\n AND p.phone_type_id = {$phoneTypes['Mobile']}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($smartGroupInclude); } /** * Construct the filtered search queries */ $query = "\nSELECT search_id, search_args, entity_id\nFROM {$mg}\nWHERE {$mg}.search_id IS NOT NULL\nAND {$mg}.mailing_id = {$mailing_id}\n"; $dao = CRM_Core_DAO::executeQuery($query); while ($dao->fetch()) { $customSQL = CRM_Contact_BAO_SearchCustom::civiMailSQL($dao->search_id, $dao->search_args, $dao->entity_id); $query = "REPLACE INTO I_{$job_id} ({$tempColumn}, contact_id)\n {$customSQL}"; $mailingGroup->query($query); } /* Get the emails with only location override */ $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n SELECT DISTINCT {$email}.id as local_email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n ORDER BY {$email}.is_bulkmail"; if ($mode == "sms") { $query = "REPLACE INTO I_{$job_id} (phone_id, contact_id)\n SELECT DISTINCT {$phone}.id as phone_id,\n {$contact}.id as contact_id\n FROM {$phone}\n INNER JOIN {$contact}\n ON {$phone}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$g2contact}.status = 'Added'\n AND {$contact}.do_not_sms = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND {$phone}.phone_type_id = {$phoneTypes['Mobile']}\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null"; } $mailingGroup->query($query); $results = array(); $eq = new CRM_Mailing_Event_BAO_Queue(); list($aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause(); $aclWhere = $aclWhere ? "WHERE {$aclWhere}" : ''; $limitString = NULL; if ($limit && $offset !== NULL) { $offset = CRM_Utils_Type::escape($offset, 'Int'); $limit = CRM_Utils_Type::escape($limit, 'Int'); $limitString = "LIMIT {$offset}, {$limit}"; } if ($storeRecipients && $mailing_id) { $sql = "\nDELETE\nFROM civicrm_mailing_recipients\nWHERE mailing_id = %1\n"; $params = array(1 => array($mailing_id, 'Integer')); CRM_Core_DAO::executeQuery($sql, $params); // CRM-3975 $groupBy = $groupJoin = ''; if ($dedupeEmail) { $groupJoin = " INNER JOIN civicrm_email e ON e.id = i.email_id"; $groupBy = " GROUP BY e.email "; } $sql = "\nINSERT INTO civicrm_mailing_recipients ( mailing_id, contact_id, {$tempColumn} )\nSELECT %1, i.contact_id, i.{$tempColumn}\nFROM civicrm_contact contact_a\nINNER JOIN I_{$job_id} i ON contact_a.id = i.contact_id\n {$groupJoin}\n {$aclFrom}\n {$aclWhere}\n {$groupBy}\nORDER BY i.contact_id, i.{$tempColumn}\n"; CRM_Core_DAO::executeQuery($sql, $params); // if we need to add all emails marked bulk, do it as a post filter // on the mailing recipients table if (CRM_Core_BAO_Email::isMultipleBulkMail()) { self::addMultipleEmails($mailing_id); } } /* Delete the temp table */ $mailingGroup->reset(); $mailingGroup->query("DROP TEMPORARY TABLE X_{$job_id}"); $mailingGroup->query("DROP TEMPORARY TABLE I_{$job_id}"); return $eq; }
/** * load the smart group cache for a saved search */ static function load(&$group) { $groupID = $group->id; $savedSearchID = $group->saved_search_id; $sql = null; $idName = 'id'; $customClass = null; if ($savedSearchID) { require_once 'CRM/Contact/BAO/SavedSearch.php'; $ssParams =& CRM_Contact_BAO_SavedSearch::getSearchParams($savedSearchID); $returnProperties = array(); if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $savedSearchID, 'mapping_id')) { require_once "CRM/Core/BAO/Mapping.php"; $fv =& CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); $returnProperties = CRM_Core_BAO_Mapping::returnProperties($fv); } if (isset($ssParams['customSearchID'])) { // if custom search require_once 'CRM/Contact/BAO/SearchCustom.php'; // we split it up and store custom class // so temp tables are not destroyed if they are used // hence customClass is defined above at top of function $customClass = CRM_Contact_BAO_SearchCustom::customClass($ssParams['customSearchID'], $savedSearchID); $searchSQL = $customClass->contactIDs(); $idName = 'contact_id'; } else { require_once 'CRM/Contact/BAO/Query.php'; $query = new CRM_Contact_BAO_Query($ssParams, $returnProperties, null, false, false, 1, true, true, false); $query->_useGroupBy = false; $searchSQL =& $query->searchQuery(0, 0, null, false, false, false, true, true, null); } $groupID = CRM_Utils_Type::escape($groupID, 'Integer'); $sql = $searchSQL . " AND contact_a.id NOT IN ( \n SELECT contact_id FROM civicrm_group_contact \n WHERE civicrm_group_contact.status = 'Removed' \n AND civicrm_group_contact.group_id = {$groupID} ) "; } if ($sql) { $sql .= " UNION "; } // lets also store the records that are explicitly added to the group // this allows us to skip the group contact LEFT JOIN $sql .= "\nSELECT contact_id as {$idName}\nFROM civicrm_group_contact\nWHERE civicrm_group_contact.status = 'Added'\n AND civicrm_group_contact.group_id = {$groupID} "; $dao = CRM_Core_DAO::executeQuery($sql); $values = array(); while ($dao->fetch()) { $values[] = "({$groupID},{$dao->{$idName}})"; } $groupIDs = array($groupID); self::remove($groupIDs); self::store($groupIDs, $values); if ($group->children) { require_once 'CRM/Contact/BAO/Group.php'; $childrenIDs = explode(',', $group->children); foreach ($childrenIDs as $childID) { $contactIDs =& CRM_Contact_BAO_Group::getMember($childID, false); $values = array(); foreach ($contactIDs as $contactID => $dontCare) { $values[] = "({$groupID},{$contactID})"; } self::store($groupIDs, $values); } } }
function &getRecipients($job_id, $includeDelivered = false, $mailing_id = null, $offset = NULL, $limit = 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} \n (contact_id int primary key) \n ENGINE=HEAP"); /* Add all the members of groups excluded from this mailing to the temp * table */ $excludeSubGroup = "INSERT INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$g2contact}.contact_id\n FROM {$g2contact}\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id AND {$mg}.entity_table = '{$group}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$g2contact}.status = 'Added'\n AND {$mg}.group_type = 'Exclude'"; $mailingGroup->query($excludeSubGroup); /* Add all unsubscribe members of base group from this mailing to the temp * table */ $unSubscribeBaseGroup = "INSERT INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$g2contact}.contact_id\n FROM {$g2contact}\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id AND {$mg}.entity_table = '{$group}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$g2contact}.status = 'Removed'\n AND {$mg}.group_type = 'Base'"; $mailingGroup->query($unSubscribeBaseGroup); /* Add all the (intended) recipients of an excluded prior mailing to * the temp table */ $excludeSubMailing = "INSERT IGNORE INTO X_{$job_id} (contact_id)\n SELECT DISTINCT {$eq}.contact_id\n FROM {$eq}\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n WHERE\n {$mg}.mailing_id = {$mailing_id}\n AND {$mg}.group_type = 'Exclude'"; $mailingGroup->query($excludeSubMailing); $ss = new CRM_Core_DAO(); $ss->query("SELECT {$group}.saved_search_id as saved_search_id,\n {$group}.id as id\n FROM {$group}\n INNER JOIN {$mg}\n ON {$mg}.entity_id = {$group}.id\n WHERE {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Exclude'\n AND {$mg}.mailing_id = {$mailing_id}\n 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 ( \n SELECT contact_id FROM {$g2contact} \n 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} \n (email_id int, contact_id int primary key)\n 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)\n SELECT DISTINCT {$email}.id as email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n AND {$mg}.entity_table = '{$group}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE \n ({$mg}.group_type = 'Include' OR {$mg}.group_type = 'Base')\n AND {$mg}.search_id IS NULL\n AND {$g2contact}.status = 'Added'\n AND {$g2contact}.email_id IS null\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n ORDER BY {$email}.is_bulkmail"; $mailingGroup->query($query); /* Query prior mailings */ $mailingGroup->query("REPLACE INTO I_{$job_id} (email_id, contact_id)\n SELECT DISTINCT {$email}.id as email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$eq}\n ON {$eq}.contact_id = {$contact}.id\n INNER JOIN {$job}\n ON {$eq}.job_id = {$job}.id\n INNER JOIN {$mg}\n ON {$job}.mailing_id = {$mg}.entity_id AND {$mg}.entity_table = '{$mailing}'\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE\n ({$mg}.group_type = 'Include' OR {$mg}.group_type = 'Base')\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n ORDER BY {$email}.is_bulkmail"); /* Construct the saved-search queries */ $ss->query("SELECT {$group}.saved_search_id as saved_search_id,\n {$group}.id as id\n FROM {$group}\n INNER JOIN {$mg}\n ON {$mg}.entity_id = {$group}.id\n AND {$mg}.entity_table = '{$group}'\n WHERE \n {$mg}.group_type = 'Include'\n AND {$mg}.search_id IS NULL\n AND {$mg}.mailing_id = {$mailing_id}\n 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)\n SELECT DISTINCT {$email}.id as email_id,\n contact_a.id as contact_id \n {$from}\n LEFT JOIN X_{$job_id}\n ON contact_a.id = X_{$job_id}.contact_id\n WHERE \n contact_a.do_not_email = 0\n AND contact_a.is_opt_out = 0\n AND contact_a.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.on_hold = 0\n {$where}\n AND contact_a.id NOT IN ( \n SELECT contact_id FROM {$g2contact} \n WHERE {$g2contact}.group_id = {$ss->id} AND {$g2contact}.status = 'Removed') \n AND X_{$job_id}.contact_id IS null\n ORDER BY {$email}.is_bulkmail"; $mailingGroup->query($ssq); } /** * Construct the filtered search queries */ $query = "\nSELECT search_id, search_args, entity_id\nFROM {$mg}\nWHERE {$mg}.search_id IS NOT NULL\nAND {$mg}.mailing_id = {$mailing_id}\n"; $dao = CRM_Core_DAO::executeQuery($query); require_once 'CRM/Contact/BAO/SearchCustom.php'; while ($dao->fetch()) { $customSQL = CRM_Contact_BAO_SearchCustom::civiMailSQL($dao->search_id, $dao->search_args, $dao->entity_id); $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n {$customSQL}"; $mailingGroup->query($query); } /* Get the emails with only location override */ $query = "REPLACE INTO I_{$job_id} (email_id, contact_id)\n SELECT DISTINCT {$email}.id as local_email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$contact}\n ON {$email}.contact_id = {$contact}.id\n INNER JOIN {$g2contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE \n {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$g2contact}.status = 'Added'\n AND {$g2contact}.email_id is null\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n 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)\n SELECT DISTINCT {$email}.id as email_id,\n {$contact}.id as contact_id\n FROM {$email}\n INNER JOIN {$g2contact}\n ON {$email}.id = {$g2contact}.email_id\n INNER JOIN {$contact}\n ON {$contact}.id = {$g2contact}.contact_id\n INNER JOIN {$mg}\n ON {$g2contact}.group_id = {$mg}.entity_id\n LEFT JOIN X_{$job_id}\n ON {$contact}.id = X_{$job_id}.contact_id\n WHERE \n {$mg}.entity_table = '{$group}'\n AND {$mg}.group_type = 'Include'\n AND {$g2contact}.status = 'Added'\n AND {$g2contact}.email_id IS NOT null\n AND {$contact}.do_not_email = 0\n AND {$contact}.is_opt_out = 0\n AND {$contact}.is_deceased = 0\n AND ({$email}.is_bulkmail = 1 OR {$email}.is_primary = 1)\n AND {$email}.on_hold = 0\n AND {$mg}.mailing_id = {$mailing_id}\n AND X_{$job_id}.contact_id IS null\n ORDER BY {$email}.is_bulkmail"); $results = array(); $eq = new CRM_Mailing_Event_BAO_Queue(); require_once 'CRM/Contact/BAO/Contact/Permission.php'; list($aclFrom, $aclWhere) = CRM_Contact_BAO_Contact_Permission::cacheClause(); $aclWhere = $aclWhere ? "WHERE {$aclWhere}" : ''; $limitString = null; if ($limit && $offset !== null) { $limitString = "LIMIT {$offset}, {$limit}"; } $eq->query("SELECT i.contact_id, i.email_id \n FROM civicrm_contact contact_a\n INNER JOIN I_{$job_id} i ON contact_a.id = i.contact_id\n {$aclFrom}\n {$aclWhere}\n ORDER BY i.contact_id, i.email_id\n {$limitString}"); /* Delete the temp table */ $mailingGroup->reset(); $mailingGroup->query("DROP TEMPORARY TABLE X_{$job_id}"); $mailingGroup->query("DROP TEMPORARY TABLE I_{$job_id}"); return $eq; }
/** * load the smart group cache for a saved search */ static function load(&$group, $fresh = FALSE) { $groupID = $group->id; $savedSearchID = $group->saved_search_id; if (array_key_exists($groupID, self::$_alreadyLoaded) && !$fresh) { return; } self::$_alreadyLoaded[$groupID] = 1; $sql = NULL; $idName = 'id'; $customClass = NULL; if ($savedSearchID) { $ssParams = CRM_Contact_BAO_SavedSearch::getSearchParams($savedSearchID); // rectify params to what proximity search expects if there is a value for prox_distance // CRM-7021 if (!empty($ssParams)) { CRM_Contact_BAO_ProximityQuery::fixInputParams($ssParams); } $returnProperties = array(); if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $savedSearchID, 'mapping_id')) { $fv = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); $returnProperties = CRM_Core_BAO_Mapping::returnProperties($fv); } if (isset($ssParams['customSearchID'])) { // if custom search // we split it up and store custom class // so temp tables are not destroyed if they are used // hence customClass is defined above at top of function $customClass = CRM_Contact_BAO_SearchCustom::customClass($ssParams['customSearchID'], $savedSearchID); $searchSQL = $customClass->contactIDs(); $idName = 'contact_id'; } else { $formValues = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); $query = new CRM_Contact_BAO_Query($ssParams, $returnProperties, NULL, FALSE, FALSE, 1, TRUE, TRUE, FALSE, CRM_Utils_Array::value('display_relationship_type', $formValues), CRM_Utils_Array::value('operator', $formValues, 'AND')); $query->_useDistinct = FALSE; $query->_useGroupBy = FALSE; $searchSQL = $query->searchQuery(0, 0, NULL, FALSE, FALSE, FALSE, TRUE, TRUE, NULL, NULL, NULL, TRUE); } $groupID = CRM_Utils_Type::escape($groupID, 'Integer'); $sql = $searchSQL . " AND contact_a.id NOT IN (\n SELECT contact_id FROM civicrm_group_contact\n WHERE civicrm_group_contact.status = 'Removed'\n AND civicrm_group_contact.group_id = {$groupID} ) "; } if ($sql) { $sql = preg_replace("/^\\s*SELECT/", "SELECT {$groupID} as group_id, ", $sql); } // lets also store the records that are explicitly added to the group // this allows us to skip the group contact LEFT JOIN $sqlB = "\nSELECT {$groupID} as group_id, contact_id as {$idName}\nFROM civicrm_group_contact\nWHERE civicrm_group_contact.status = 'Added'\n AND civicrm_group_contact.group_id = {$groupID} "; $groupIDs = array($groupID); self::remove($groupIDs); foreach (array($sql, $sqlB) as $selectSql) { if (!$selectSql) { continue; } $insertSql = "INSERT IGNORE INTO civicrm_group_contact_cache (group_id,contact_id) ({$selectSql});"; $processed = TRUE; // FIXME $result = CRM_Core_DAO::executeQuery($insertSql); } self::updateCacheTime($groupIDs, $processed); if ($group->children) { //Store a list of contacts who are removed from the parent group $sql = "\nSELECT contact_id\nFROM civicrm_group_contact\nWHERE civicrm_group_contact.status = 'Removed'\nAND civicrm_group_contact.group_id = {$groupID} "; $dao = CRM_Core_DAO::executeQuery($sql); $removed_contacts = array(); while ($dao->fetch()) { $removed_contacts[] = $dao->contact_id; } $childrenIDs = explode(',', $group->children); foreach ($childrenIDs as $childID) { $contactIDs = CRM_Contact_BAO_Group::getMember($childID, FALSE); //Unset each contact that is removed from the parent group foreach ($removed_contacts as $removed_contact) { unset($contactIDs[$removed_contact]); } $values = array(); foreach ($contactIDs as $contactID => $dontCare) { $values[] = "({$groupID},{$contactID})"; } self::store($groupIDs, $values); } } }
static function fromWhereEmail($id) { $params =& self::getSearchParams($id); if ($params) { if (CRM_Utils_Array::value('customSearchID', $params)) { require_once 'CRM/Contact/BAO/SearchCustom.php'; return CRM_Contact_BAO_SearchCustom::fromWhereEmail(null, $id); } else { $tables = $whereTables = array('civicrm_contact' => 1, 'civicrm_email' => 1); $where = CRM_Contact_BAO_SavedSearch::whereClause($id, $tables, $whereTables); $from = CRM_Contact_BAO_Query::fromClause($whereTables); return array($from, $where); } } else { CRM_Core_Error::fatal('No contactID clause'); } }