X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FCore%2FBAO%2FActionSchedule.php;h=780da240d8fe1c38d2f931164bfa8ad11e76415e;hb=018d5e02328b7082c40482ac2bf5e0698d2fc906;hp=667f3b4d32d133bd60f2ffd1968d6ee461aa4cb9;hpb=018d15a6ee9cbee80cecca1c61b88a62faa0bc60;p=civicrm-core.git diff --git a/CRM/Core/BAO/ActionSchedule.php b/CRM/Core/BAO/ActionSchedule.php index 667f3b4d32..780da240d8 100755 --- a/CRM/Core/BAO/ActionSchedule.php +++ b/CRM/Core/BAO/ActionSchedule.php @@ -1,7 +1,7 @@ Mapping $mapping). */ - public static function getMapping($id = NULL) { + public static function getMappings($filters = NULL) { static $_action_mapping; - if ($id && !is_null($_action_mapping) && isset($_action_mapping[$id])) { - return $_action_mapping[$id]; + if ($_action_mapping === NULL) { + $event = \Civi\Core\Container::singleton()->get('dispatcher') + ->dispatch(\Civi\ActionSchedule\Events::MAPPINGS, + new \Civi\ActionSchedule\Event\MappingRegisterEvent()); + $_action_mapping = $event->getMappings(); } - $dao = new CRM_Core_DAO_ActionMapping(); - if ($id) { - $dao->id = $id; + if (empty($filters)) { + return $_action_mapping; } - $dao->find(); - - $mapping = array(); - while ($dao->fetch()) { - $defaults = array(); - CRM_Core_DAO::storeValues($dao, $defaults); - $mapping[$dao->id] = $defaults; + elseif (isset($filters['id'])) { + return array( + $filters['id'] => $_action_mapping[$filters['id']], + ); + } + else { + throw new CRM_Core_Exception("getMappings() called with unsupported filter: " . implode(', ', array_keys($filters))); } - $_action_mapping = $mapping; - - return $mapping; } /** - * Get all fields of the type Date. + * @param string|int $id + * @return \Civi\ActionSchedule\Mapping|NULL */ - public static function getDateFields() { - $allFields = CRM_Core_BAO_CustomField::getFields(''); - $dateFields = array( - 'birth_date' => ts('Birth Date'), - 'created_date' => ts('Created Date'), - 'modified_date' => ts('Modified Date'), - ); - foreach ($allFields as $fieldID => $field) { - if ($field['data_type'] == 'Date') { - $dateFields["custom_$fieldID"] = $field['label']; - } - } - return $dateFields; + public static function getMapping($id) { + $mappings = self::getMappings(); + return isset($mappings[$id]) ? $mappings[$id] : NULL; } /** @@ -95,227 +86,62 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule { * associated array of all the drop downs in the form */ public static function getSelection($id = NULL) { - $mapping = self::getMapping(); - $activityStatus = CRM_Core_PseudoConstant::activityStatus(); - $activityType = CRM_Core_PseudoConstant::activityType(TRUE, TRUE); - - $participantStatus = CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label'); - $event = CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )"); - $eventType = CRM_Event_PseudoConstant::eventType(); - $eventTemplate = CRM_Event_PseudoConstant::eventTemplates(); - $autoRenew = CRM_Core_OptionGroup::values('auto_renew_options'); - $membershipType = CRM_Member_PseudoConstant::membershipType(); - $dateFieldParams = array('data_type' => 'Date'); - $dateFields = self::getDateFields(); - $contactOptions = CRM_Core_OptionGroup::values('contact_date_reminder_options'); - - asort($activityType); - - $sel1 = $sel2 = $sel3 = $sel4 = $sel5 = array(); - $options = array( - 'manual' => ts('Choose Recipient(s)'), - 'group' => ts('Select Group'), - ); - - $entityMapping = array(); - $recipientMapping = array_combine(array_keys($options), array_keys($options)); - - if (!$id) { - $id = 1; + $mappings = CRM_Core_BAO_ActionSchedule::getMappings(); + $selectedMapping = $mappings[$id ? $id : 1]; + + $entityValueLabels = array(); + foreach ($mappings as $mapping) { + /** @var \Civi\ActionSchedule\Mapping $mapping */ + $entityValueLabels[$mapping->getId()] = $mapping->getValueLabels(); + $valueLabel = array('- ' . strtolower($mapping->getValueHeader()) . ' -'); + $entityValueLabels[$mapping->getId()] = $valueLabel + $entityValueLabels[$mapping->getId()]; } - foreach ($mapping as $value) { - $entityValue = CRM_Utils_Array::value('entity_value', $value); - $entityStatus = CRM_Utils_Array::value('entity_status', $value); - $entityRecipient = CRM_Utils_Array::value('entity_recipient', $value); - $valueLabel = array('- ' . strtolower(CRM_Utils_Array::value('entity_value_label', $value)) . ' -'); - $key = CRM_Utils_Array::value('id', $value); - $entityMapping[$key] = CRM_Utils_Array::value('entity', $value); - - $sel1Val = NULL; - switch ($entityValue) { - case 'activity_type': - if ($value['entity'] == 'civicrm_activity') { - $sel1Val = ts('Activity'); - } - $sel2[$key] = $valueLabel + $activityType; - break; - - case 'event_type': - if ($value['entity'] == 'civicrm_participant') { - $sel1Val = ts('Event Type'); - } - $sel2[$key] = $valueLabel + $eventType; - break; - - case 'event_template': - if ($value['entity'] == 'civicrm_participant') { - $sel1Val = ts('Event Template'); - } - $sel2[$key] = $valueLabel + $eventTemplate; - break; - - case 'civicrm_event': - if ($value['entity'] == 'civicrm_participant') { - $sel1Val = ts('Event Name'); - } - $sel2[$key] = $valueLabel + $event; - break; - - case 'civicrm_membership_type': - if ($value['entity'] == 'civicrm_membership') { - $sel1Val = ts('Membership'); - } - $sel2[$key] = $valueLabel + $membershipType; - break; - - case 'civicrm_contact': - if ($value['entity'] == 'civicrm_contact') { - $sel1Val = ts('Contact'); - } - $sel2[$key] = $dateFields; - break; - } - $sel1[$key] = $sel1Val; - - if ($key == $id) { - if ($startDate = CRM_Utils_Array::value('entity_date_start', $value)) { - $sel4[$startDate] = ucwords(str_replace('_', ' ', $startDate)); - } - if ($endDate = CRM_Utils_Array::value('entity_date_end', $value)) { - $sel4[$endDate] = ucwords(str_replace('_', ' ', $endDate)); - } - - switch ($entityRecipient) { - case 'activity_contacts': - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts'); - $sel5[$entityRecipient] = $activityContacts + $options; - $recipientMapping += CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - break; - - case 'event_contacts': - $eventContacts = CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name'); - $sel5[$entityRecipient] = $eventContacts + $options; - $recipientMapping += CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name'); - break; - - case NULL: - $sel5[$entityRecipient] = $options; - break; - } + $entityStatusLabels = array(); + foreach ($mappings as $mapping) { + /** @var \Civi\ActionSchedule\Mapping $mapping */ + $statusLabel = array('- ' . strtolower($mapping->getStatusHeader()) . ' -'); + $entityStatusLabels[$mapping->getId()] = $entityValueLabels[$mapping->getId()]; + foreach ($entityStatusLabels[$mapping->getId()] as $kkey => & $vval) { + $vval = $statusLabel + $mapping->getStatusLabels($kkey); } } - $sel3 = $sel2; - - foreach ($mapping as $value) { - $entityStatus = CRM_Utils_Array::value('entity_status', $value); - $statusLabel = array('- ' . strtolower(CRM_Utils_Array::value('entity_status_label', $value)) . ' -'); - $id = CRM_Utils_Array::value('id', $value); - switch ($entityStatus) { - case 'activity_status': - foreach ($sel3[$id] as $kkey => & $vval) { - $vval = $statusLabel + $activityStatus; - } - break; + $entityRecipientLabels = $selectedMapping->getRecipientTypes() + self::getAdditionalRecipients(); - case 'civicrm_participant_status_type': - foreach ($sel3[$id] as $kkey => & $vval) { - $vval = $statusLabel + $participantStatus; - } - break; - - case 'auto_renew_options': - foreach ($sel3[$id] as $kkey => & $vval) { - $auto = 0; - if ($kkey) { - $auto = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $kkey, 'auto_renew'); - } - if ($auto) { - $vval = $statusLabel + $autoRenew; - } - else { - $vval = $statusLabel; - } - } - break; - - case 'contact_date_reminder_options': - foreach ($sel3[$id] as $kkey => & $vval) { - $vval = $contactOptions; - } - break; - - case '': - $sel3[$id] = ''; - break; - - } - } return array( - 'sel1' => $sel1, - 'sel2' => $sel2, - 'sel3' => $sel3, - 'sel4' => $sel4, - 'sel5' => $sel5, - 'entityMapping' => $entityMapping, - 'recipientMapping' => $recipientMapping, + 'sel1' => CRM_Utils_Array::collectMethod('getLabel', $mappings), + 'sel2' => $entityValueLabels, + 'sel3' => $entityStatusLabels, + 'sel4' => $selectedMapping->getDateFields(), + 'sel5' => $entityRecipientLabels, + 'entityMapping' => CRM_Utils_Array::collectMethod('getEntity', $mappings), + 'recipientMapping' => array_combine(array_keys($entityRecipientLabels), array_keys($entityRecipientLabels)), ); } /** - * @param int $id + * @param int $mappingId * @param int $isLimit * * @return array */ - public static function getSelection1($id = NULL, $isLimit = NULL) { - $mapping = self::getMapping($id); - $sel4 = $sel5 = array(); - $options = array( - 'manual' => ts('Choose Recipient(s)'), - 'group' => ts('Select Group'), - ); - - $recipientMapping = array_combine(array_keys($options), array_keys($options)); - - foreach ($mapping as $value) { - $entityRecipient = CRM_Utils_Array::value('entity_recipient', $value); - $key = CRM_Utils_Array::value('id', $value); - - if ($startDate = CRM_Utils_Array::value('entity_date_start', $value)) { - $sel4[$startDate] = ucwords(str_replace('_', ' ', $startDate)); - } - if ($endDate = CRM_Utils_Array::value('entity_date_end', $value)) { - $sel4[$endDate] = ucwords(str_replace('_', ' ', $endDate)); - } - - switch ($entityRecipient) { - case 'activity_contacts': - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts'); - $sel5[$id] = $activityContacts + $options; - $recipientMapping += CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - break; - - case 'event_contacts': - //CRM-15536, don't provide participant_role option on choosing 'Also Include' for Event entity - if ($isLimit == 1) { - $options += CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'label', TRUE, FALSE, 'name'); - } - $sel5[$id] = $options; - $recipientMapping += CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name'); - break; - - case NULL: - $sel5[$id] = $options; - break; - } + public static function getSelection1($mappingId = NULL, $isLimit = NULL) { + $mappings = CRM_Core_BAO_ActionSchedule::getMappings(array( + 'id' => $mappingId, + )); + $dateFieldLabels = $entityRecipientLabels = array(); + + foreach ($mappings as $mapping) { + /** @var \Civi\ActionSchedule\Mapping $mapping */ + $dateFieldLabels = $mapping->getDateFields(); + $entityRecipientLabels = $mapping->getRecipientTypes(!$isLimit) + self::getAdditionalRecipients(); } return array( - 'sel4' => $sel4, - 'sel5' => $sel5[$id], - 'recipientMapping' => $recipientMapping, + 'sel4' => $dateFieldLabels, + 'sel5' => $entityRecipientLabels, + 'recipientMapping' => array_combine(array_keys($entityRecipientLabels), array_keys($entityRecipientLabels)), ); } @@ -325,41 +151,21 @@ class CRM_Core_BAO_ActionSchedule extends CRM_Core_DAO_ActionSchedule { * @param bool $namesOnly * Return simple list of names. * - * @param null $entityValue - * @param int $id + * @param \Civi\ActionSchedule\Mapping|NULL $filterMapping + * Filter by the schedule's mapping type. + * @param int $filterValue + * Filter by the schedule's entity_value. * * @return array * (reference) reminder list */ - public static function &getList($namesOnly = FALSE, $entityValue = NULL, $id = NULL) { - $activity_type = CRM_Core_PseudoConstant::activityType(TRUE, TRUE); - $activity_status = CRM_Core_PseudoConstant::activityStatus(); - - $event_type = CRM_Event_PseudoConstant::eventType(); - $civicrm_event = CRM_Event_PseudoConstant::event(NULL, FALSE, "( is_template IS NULL OR is_template != 1 )"); - $civicrm_participant_status_type = CRM_Event_PseudoConstant::participantStatus(NULL, NULL, 'label'); - $event_template = CRM_Event_PseudoConstant::eventTemplates(); - $civicrm_contact = self::getDateFields(); - - $auto_renew_options = CRM_Core_OptionGroup::values('auto_renew_options'); - $contact_date_reminder_options = CRM_Core_OptionGroup::values('contact_date_reminder_options'); - $civicrm_membership_type = CRM_Member_PseudoConstant::membershipType(); - - $entity = array( - 'civicrm_activity' => 'Activity', - 'civicrm_participant' => 'Event', - 'civicrm_membership' => 'Member', - 'civicrm_contact' => 'Contact', - ); - + public static function &getList($namesOnly = FALSE, $filterMapping = NULL, $filterValue = NULL) { $query = " SELECT title, - cam.entity, cas.id as id, - cam.entity_value as entityValue, + cas.mapping_id, cas.entity_value as entityValueIds, - cam.entity_status as entityStatus, cas.entity_status as entityStatusIds, cas.start_action_date as entityDate, cas.start_action_offset, @@ -370,24 +176,22 @@ SELECT is_active FROM civicrm_action_schedule cas -LEFT JOIN civicrm_action_mapping cam ON (cam.id = cas.mapping_id) "; - $params = CRM_Core_DAO::$_nullArray; + $queryParams = array(); $where = " WHERE 1 "; - if ($entityValue and $id) { - $where .= " -AND cas.entity_value = $id AND - cam.entity_value = '$entityValue'"; - - $params = array( - 1 => array($id, 'Integer'), - 2 => array($entityValue, 'String'), - ); + if ($filterMapping and $filterValue) { + $where .= " AND cas.entity_value = %1 AND cas.mapping_id = %2"; + $queryParams[1] = array($filterValue, 'Integer'); + $queryParams[2] = array($filterMapping->getId(), 'String'); } $where .= " AND cas.used_for IS NULL"; $query .= $where; - $dao = CRM_Core_DAO::executeQuery($query); + $dao = CRM_Core_DAO::executeQuery($query, $queryParams); while ($dao->fetch()) { + /** @var Civi\ActionSchedule\Mapping $filterMapping */ + $filterMapping = CRM_Utils_Array::first(self::getMappings(array( + 'id' => $dao->mapping_id, + ))); $list[$dao->id]['id'] = $dao->id; $list[$dao->id]['title'] = $dao->title; $list[$dao->id]['start_action_offset'] = $dao->start_action_offset; @@ -395,23 +199,15 @@ AND cas.entity_value = $id AND $list[$dao->id]['start_action_condition'] = $dao->start_action_condition; $list[$dao->id]['entityDate'] = ucwords(str_replace('_', ' ', $dao->entityDate)); $list[$dao->id]['absolute_date'] = $dao->absolute_date; - - $status = $dao->entityStatus; - $statusArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $dao->entityStatusIds); - foreach ($statusArray as & $s) { - $s = CRM_Utils_Array::value($s, $$status); - } - $statusIds = implode(', ', $statusArray); - - $value = $dao->entityValue; - $valueArray = explode(CRM_Core_DAO::VALUE_SEPARATOR, $dao->entityValueIds); - foreach ($valueArray as & $v) { - $v = CRM_Utils_Array::value($v, $$value); - } - $valueIds = implode(', ', $valueArray); - $list[$dao->id]['entity'] = $entity[$dao->entity]; - $list[$dao->id]['value'] = $valueIds; - $list[$dao->id]['status'] = $statusIds; + $list[$dao->id]['entity'] = $filterMapping->getLabel(); + $list[$dao->id]['value'] = implode(', ', CRM_Utils_Array::subset( + $filterMapping->getValueLabels(), + explode(CRM_Core_DAO::VALUE_SEPARATOR, $dao->entityValueIds) + )); + $list[$dao->id]['status'] = implode(', ', CRM_Utils_Array::subset( + $filterMapping->getStatusLabels($dao->entityValueIds), + explode(CRM_Core_DAO::VALUE_SEPARATOR, $dao->entityStatusIds) + )); $list[$dao->id]['is_repeat'] = $dao->is_repeat; $list[$dao->id]['is_active'] = $dao->is_active; } @@ -419,179 +215,6 @@ AND cas.entity_value = $id AND return $list; } - /** - * @param int $contactId - * @param $to - * @param int $scheduleID - * @param $from - * @param array $tokenParams - * - * @return bool|null - * @throws CRM_Core_Exception - */ - public static function sendReminder($contactId, $to, $scheduleID, $from, $tokenParams) { - $email = $to['email']; - $phoneNumber = $to['phone']; - $schedule = new CRM_Core_DAO_ActionSchedule(); - $schedule->id = $scheduleID; - - $domain = CRM_Core_BAO_Domain::getDomain(); - $result = NULL; - $hookTokens = array(); - - if ($schedule->find(TRUE)) { - $body_text = $schedule->body_text; - $body_html = $schedule->body_html; - $sms_body_text = $schedule->sms_body_text; - $body_subject = $schedule->subject; - if (!$body_text) { - $body_text = CRM_Utils_String::htmlToText($body_html); - } - - $params = array(array('contact_id', '=', $contactId, 0, 0)); - list($contact, $_) = CRM_Contact_BAO_Query::apiQuery($params); - - //CRM-4524 - $contact = reset($contact); - - if (!$contact || is_a($contact, 'CRM_Core_Error')) { - return NULL; - } - - // merge activity tokens with contact array - $contact = array_merge($contact, $tokenParams); - - //CRM-5734 - CRM_Utils_Hook::tokenValues($contact, $contactId); - - CRM_Utils_Hook::tokens($hookTokens); - $categories = array_keys($hookTokens); - - $type = array('body_html' => 'html', 'body_text' => 'text', 'sms_body_text' => 'text'); - - foreach ($type as $bodyType => $value) { - $dummy_mail = new CRM_Mailing_BAO_Mailing(); - if ($bodyType == 'sms_body_text') { - $dummy_mail->body_text = $$bodyType; - } - else { - $dummy_mail->$bodyType = $$bodyType; - } - $tokens = $dummy_mail->getTokens(); - - if ($$bodyType) { - CRM_Utils_Token::replaceGreetingTokens($$bodyType, NULL, $contact['contact_id']); - $$bodyType = CRM_Utils_Token::replaceDomainTokens($$bodyType, $domain, TRUE, $tokens[$value], TRUE); - $$bodyType = CRM_Utils_Token::replaceContactTokens($$bodyType, $contact, FALSE, $tokens[$value], FALSE, TRUE); - $$bodyType = CRM_Utils_Token::replaceComponentTokens($$bodyType, $contact, $tokens[$value], TRUE, FALSE); - $$bodyType = CRM_Utils_Token::replaceHookTokens($$bodyType, $contact, $categories, TRUE); - } - } - $html = $body_html; - $text = $body_text; - $sms_text = $sms_body_text; - - $smarty = CRM_Core_Smarty::singleton(); - foreach (array( - 'text', - 'html', - 'sms_text', - ) as $elem) { - $$elem = $smarty->fetch("string:{$$elem}"); - } - - //@todo - this next section is a duplicate of function CRM_Utils_Token::getTokens - $matches = array(); - preg_match_all('/(?fetch("string:{$messageSubject}"); - - if ($schedule->mode == 'SMS' or $schedule->mode == 'User_Preference') { - $session = CRM_Core_Session::singleton(); - $userID = $session->get('userID') ? $session->get('userID') : $contactId; - $smsParams = array( - 'To' => $phoneNumber, - 'provider_id' => $schedule->sms_provider_id, - 'activity_subject' => $messageSubject, - ); - $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', - 'SMS', - 'name' - ); - $activityParams = array( - 'source_contact_id' => $userID, - 'activity_type_id' => $activityTypeID, - 'activity_date_time' => date('YmdHis'), - 'subject' => $messageSubject, - 'details' => $sms_text, - 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'), - ); - - $activity = CRM_Activity_BAO_Activity::create($activityParams); - - CRM_Activity_BAO_Activity::sendSMSMessage($contactId, - $sms_text, - $smsParams, - $activity->id, - $userID - ); - } - - if ($schedule->mode == 'Email' or $schedule->mode == 'User_Preference') { - // set up the parameters for CRM_Utils_Mail::send - $mailParams = array( - 'groupName' => 'Scheduled Reminder Sender', - 'from' => $from, - 'toName' => $contact['display_name'], - 'toEmail' => $email, - 'subject' => $messageSubject, - 'entity' => 'action_schedule', - 'entity_id' => $scheduleID, - ); - - if (!$html || $contact['preferred_mail_format'] == 'Text' || - $contact['preferred_mail_format'] == 'Both' - ) { - // render the & entities in text mode, so that the links work - $mailParams['text'] = str_replace('&', '&', $text); - } - if ($html && ($contact['preferred_mail_format'] == 'HTML' || - $contact['preferred_mail_format'] == 'Both' - ) - ) { - $mailParams['html'] = $html; - } - $result = CRM_Utils_Mail::send($mailParams); - } - } - $schedule->free(); - - return $result; - } - /** * Add the schedules reminders in the db. * @@ -681,260 +304,62 @@ AND cas.entity_value = $id AND * @throws CRM_Core_Exception */ public static function sendMailings($mappingID, $now) { - $domainValues = CRM_Core_BAO_Domain::getNameAndEmail(); - $fromEmailAddress = "$domainValues[0] <$domainValues[1]>"; - - $mapping = new CRM_Core_DAO_ActionMapping(); - $mapping->id = $mappingID; - $mapping->find(TRUE); + $mapping = CRM_Utils_Array::first(self::getMappings(array( + 'id' => $mappingID, + ))); $actionSchedule = new CRM_Core_DAO_ActionSchedule(); $actionSchedule->mapping_id = $mappingID; $actionSchedule->is_active = 1; $actionSchedule->find(FALSE); - $tokenFields = array(); - $session = CRM_Core_Session::singleton(); - while ($actionSchedule->fetch()) { - $extraSelect = $extraJoin = $extraWhere = $extraOn = ''; - - if ($actionSchedule->from_email) { - $fromEmailAddress = "$actionSchedule->from_name <$actionSchedule->from_email>"; - } - - $activityTypeID = FALSE; - $activityStatusID = FALSE; - if ($actionSchedule->record_activity) { - if ($mapping->entity == 'civicrm_membership') { - $activityTypeID - = CRM_Core_OptionGroup::getValue('activity_type', 'Membership Renewal Reminder', 'name'); - } - else { - $activityTypeID - = CRM_Core_OptionGroup::getValue('activity_type', 'Reminder Sent', 'name'); - } - - $activityStatusID - = CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'); - } - - if ($mapping->entity == 'civicrm_activity') { - $compInfo = CRM_Core_Component::getEnabledComponents(); - $tokenEntity = 'activity'; - $tokenFields = array('activity_id', 'activity_type', 'subject', 'details', 'activity_date_time'); - $extraSelect = ', ov.label as activity_type, e.id as activity_id'; - $extraJoin = " -INNER JOIN civicrm_option_group og ON og.name = 'activity_type' -INNER JOIN civicrm_option_value ov ON e.activity_type_id = ov.value AND ov.option_group_id = og.id"; - $extraOn = ' AND e.is_current_revision = 1 AND e.is_deleted = 0 '; - if ($actionSchedule->limit_to == 0) { - $extraJoin = " -LEFT JOIN civicrm_option_group og ON og.name = 'activity_type' -LEFT JOIN civicrm_option_value ov ON e.activity_type_id = ov.value AND ov.option_group_id = og.id"; - } - - //join for caseId - // if CiviCase component is enabled - if (array_key_exists('CiviCase', $compInfo)) { - $extraSelect .= ", civicrm_case_activity.case_id as case_id"; - $extraJoin .= " - LEFT JOIN `civicrm_case_activity` ON `e`.`id` = `civicrm_case_activity`.`activity_id`"; - } - } - - if ($mapping->entity == 'civicrm_participant') { - $tokenEntity = 'event'; - $tokenFields = array( - 'event_type', - 'title', - 'event_id', - 'start_date', - 'end_date', - 'summary', - 'description', - 'location', - 'info_url', - 'registration_url', - 'fee_amount', - 'contact_email', - 'contact_phone', - 'balance', - ); - $extraSelect = ', ov.label as event_type, ev.title, ev.id as event_id, ev.start_date, ev.end_date, ev.summary, ev.description, address.street_address, address.city, address.state_province_id, address.postal_code, email.email as contact_email, phone.phone as contact_phone '; - - $extraJoin = " -INNER JOIN civicrm_event ev ON e.event_id = ev.id -INNER JOIN civicrm_option_group og ON og.name = 'event_type' -INNER JOIN civicrm_option_value ov ON ev.event_type_id = ov.value AND ov.option_group_id = og.id -LEFT JOIN civicrm_loc_block lb ON lb.id = ev.loc_block_id -LEFT JOIN civicrm_address address ON address.id = lb.address_id -LEFT JOIN civicrm_email email ON email.id = lb.email_id -LEFT JOIN civicrm_phone phone ON phone.id = lb.phone_id -"; - if ($actionSchedule->limit_to == 0) { - $extraJoin = " -LEFT JOIN civicrm_event ev ON e.event_id = ev.id -LEFT JOIN civicrm_option_group og ON og.name = 'event_type' -LEFT JOIN civicrm_option_value ov ON ev.event_type_id = ov.value AND ov.option_group_id = og.id -LEFT JOIN civicrm_loc_block lb ON lb.id = ev.loc_block_id -LEFT JOIN civicrm_address address ON address.id = lb.address_id -LEFT JOIN civicrm_email email ON email.id = lb.email_id -LEFT JOIN civicrm_phone phone ON phone.id = lb.phone_id -"; - } - } - - if ($mapping->entity == 'civicrm_membership') { - $tokenEntity = 'membership'; - $tokenFields = array('fee', 'id', 'join_date', 'start_date', 'end_date', 'status', 'type'); - $extraSelect = ', mt.minimum_fee as fee, e.id as id , e.join_date, e.start_date, e.end_date, ms.name as status, mt.name as type'; - $extraJoin = ' - INNER JOIN civicrm_membership_type mt ON e.membership_type_id = mt.id - INNER JOIN civicrm_membership_status ms ON e.status_id = ms.id'; - - if ($actionSchedule->limit_to == 0) { - $extraJoin = ' - LEFT JOIN civicrm_membership_type mt ON e.membership_type_id = mt.id - LEFT JOIN civicrm_membership_status ms ON e.status_id = ms.id'; - } - } - - if ($mapping->entity == 'civicrm_contact') { - $tokenEntity = 'contact'; - //TODO: get full list somewhere! - $tokenFields = array('birth_date', 'last_name'); - //TODO: is there anything to add here? - } - - $entityJoinClause = "INNER JOIN {$mapping->entity} e ON e.id = reminder.entity_id"; - if ($actionSchedule->limit_to == 0) { - $entityJoinClause = "LEFT JOIN {$mapping->entity} e ON e.id = reminder.entity_id"; - $extraWhere .= " AND (e.id = reminder.entity_id OR reminder.entity_table = 'civicrm_contact')"; - } - $entityJoinClause .= $extraOn; - - $query = " -SELECT reminder.id as reminderID, reminder.contact_id as contactID, reminder.entity_table as entityTable, reminder.*, e.id as entityID, e.* {$extraSelect} -FROM civicrm_action_log reminder -{$entityJoinClause} -{$extraJoin} -WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL -{$extraWhere}"; - + $query = CRM_Core_BAO_ActionSchedule::prepareMailingQuery($mapping, $actionSchedule); $dao = CRM_Core_DAO::executeQuery($query, array(1 => array($actionSchedule->id, 'Integer')) ); + $multilingual = CRM_Core_I18n::isMultilingual(); while ($dao->fetch()) { - $entityTokenParams = array(); - foreach ($tokenFields as $field) { - if ($field == 'location') { - $loc = array(); - $stateProvince = CRM_Core_PseudoConstant::stateProvince(); - $loc['street_address'] = $dao->street_address; - $loc['city'] = $dao->city; - $loc['state_province'] = CRM_Utils_Array::value($dao->state_province_id, $stateProvince); - $loc['postal_code'] = $dao->postal_code; - $entityTokenParams["{$tokenEntity}." . $field] = CRM_Utils_Address::format($loc); - } - elseif ($field == 'info_url') { - $entityTokenParams["{$tokenEntity}." . $field] = CRM_Utils_System::url('civicrm/event/info', 'reset=1&id=' . $dao->event_id, TRUE, NULL, FALSE); - } - elseif ($field == 'registration_url') { - $entityTokenParams["{$tokenEntity}." . $field] = CRM_Utils_System::url('civicrm/event/register', 'reset=1&id=' . $dao->event_id, TRUE, NULL, FALSE); - } - elseif (in_array($field, array('start_date', 'end_date', 'join_date', 'activity_date_time'))) { - $entityTokenParams["{$tokenEntity}." . $field] = CRM_Utils_Date::customFormat($dao->$field); - } - elseif ($field == 'balance') { - if ($dao->entityTable == 'civicrm_contact') { - $balancePay = 'N/A'; - } - elseif (!empty($dao->entityID)) { - $info = CRM_Contribute_BAO_Contribution::getPaymentInfo($dao->entityID, 'event'); - $balancePay = CRM_Utils_Array::value('balance', $info); - $balancePay = CRM_Utils_Money::format($balancePay); + // switch language if necessary + if ($multilingual) { + $preferred_language = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $dao->contactID, 'preferred_language'); + CRM_Core_BAO_ActionSchedule::setCommunicationLanguage($actionSchedule->communication_language, $preferred_language); + } + + $errors = array(); + try { + $tokenProcessor = self::createTokenProcessor($actionSchedule, $mapping); + $tokenProcessor->addRow() + ->context('contactId', $dao->contactID) + ->context('actionSearchResult', (object) $dao->toArray()); + foreach ($tokenProcessor->evaluate()->getRows() as $tokenRow) { + if ($actionSchedule->mode == 'SMS' or $actionSchedule->mode == 'User_Preference') { + CRM_Utils_Array::extend($errors, self::sendReminderSms($tokenRow, $actionSchedule, $dao->contactID)); } - $entityTokenParams["{$tokenEntity}." . $field] = $balancePay; - } - elseif ($field == 'fee_amount') { - $entityTokenParams["{$tokenEntity}." . $field] = CRM_Utils_Money::format($dao->$field); - } - else { - $entityTokenParams["{$tokenEntity}." . $field] = $dao->$field; - } - } - $isError = 0; - $errorMsg = $toEmail = $toPhoneNumber = ''; - - if ($actionSchedule->mode == 'SMS' or $actionSchedule->mode == 'User_Preference') { - $filters = array('is_deceased' => 0, 'is_deleted' => 0, 'do_not_sms' => 0); - $toPhoneNumbers = CRM_Core_BAO_Phone::allPhones($dao->contactID, FALSE, 'Mobile', $filters); - //to get primary mobile ph,if not get a first mobile phONE - if (!empty($toPhoneNumbers)) { - $toPhoneNumberDetails = reset($toPhoneNumbers); - $toPhoneNumber = CRM_Utils_Array::value('phone', $toPhoneNumberDetails); - //contact allows to send sms - $toDoNotSms = 0; - } - } - if ($actionSchedule->mode == 'Email' or $actionSchedule->mode == 'User_Preference') { - $toEmail = CRM_Contact_BAO_Contact::getPrimaryEmail($dao->contactID); - } - if ($toEmail || !(empty($toPhoneNumber) or $toDoNotSms)) { - $to['email'] = $toEmail; - $to['phone'] = $toPhoneNumber; - $result - = CRM_Core_BAO_ActionSchedule::sendReminder( - $dao->contactID, - $to, - $actionSchedule->id, - $fromEmailAddress, - $entityTokenParams - ); - - if (!$result || is_a($result, 'PEAR_Error')) { - // we could not send an email, for now we ignore, CRM-3406 - $isError = 1; + if ($actionSchedule->mode == 'Email' or $actionSchedule->mode == 'User_Preference') { + CRM_Utils_Array::extend($errors, self::sendReminderEmail($tokenRow, $actionSchedule, $dao->contactID)); + } } } - else { - $isError = 1; - $errorMsg = "Couldn\'t find recipient\'s email address."; + catch (\Civi\Token\TokenException $e) { + $errors['token_exception'] = $e->getMessage(); } // update action log record $logParams = array( 'id' => $dao->reminderID, - 'is_error' => $isError, - 'message' => $errorMsg ? $errorMsg : "null", + 'is_error' => !empty($errors), + 'message' => empty($errors) ? "null" : implode(' ', $errors), 'action_date_time' => $now, ); CRM_Core_BAO_ActionLog::create($logParams); // insert activity log record if needed - if ($actionSchedule->record_activity && !$isError) { - $activityParams = array( - 'subject' => $actionSchedule->title, - 'details' => $actionSchedule->body_html, - 'source_contact_id' => $session->get('userID') ? $session->get('userID') : $dao->contactID, - 'target_contact_id' => $dao->contactID, - 'activity_date_time' => date('YmdHis'), - 'status_id' => $activityStatusID, - 'activity_type_id' => $activityTypeID, - 'source_record_id' => $dao->entityID, - ); - $activity = CRM_Activity_BAO_Activity::create($activityParams); - - //file reminder on case if source activity is a case activity - if (!empty($dao->case_id)) { - $caseActivityParams = array(); - $caseActivityParams['case_id'] = $dao->case_id; - $caseActivityParams['activity_id'] = $activity->id; - CRM_Case_BAO_Case::processCaseActivity($caseActivityParams); - } + if ($actionSchedule->record_activity && empty($errors)) { + $caseID = empty($dao->case_id) ? NULL : $dao->case_id; + CRM_Core_BAO_ActionSchedule::createMailingActivity($actionSchedule, $mapping, $dao->contactID, $dao->entityID, $caseID); } } @@ -959,507 +384,13 @@ WHERE reminder.action_schedule_id = %1 AND reminder.action_date_time IS NULL $actionSchedule->find(); while ($actionSchedule->fetch()) { - $mapping = new CRM_Core_DAO_ActionMapping(); - $mapping->id = $mappingID; - $mapping->find(TRUE); - - // note: $where - this filtering applies for both - // 'limit to' and 'addition to' options - // $limitWhere - this filtering applies only for - // 'limit to' option - $select = $join = $where = $limitWhere = array(); - $selectColumns = "contact_id, entity_id, entity_table, action_schedule_id"; - $limitTo = $actionSchedule->limit_to; - $value = explode(CRM_Core_DAO::VALUE_SEPARATOR, - trim($actionSchedule->entity_value, CRM_Core_DAO::VALUE_SEPARATOR) - ); - $value = implode(',', $value); - - $status = explode(CRM_Core_DAO::VALUE_SEPARATOR, - trim($actionSchedule->entity_status, CRM_Core_DAO::VALUE_SEPARATOR) - ); - $status = implode(',', $status); - - $anniversary = FALSE; - - if (!CRM_Utils_System::isNull($mapping->entity_recipient)) { - if ($mapping->entity_recipient == 'event_contacts') { - $recipientOptions = CRM_Core_OptionGroup::values($mapping->entity_recipient, FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name'); - } - else { - $recipientOptions = CRM_Core_OptionGroup::values($mapping->entity_recipient, FALSE, FALSE, FALSE, NULL, 'name'); - } - } - $from = "{$mapping->entity} e"; - - if ($mapping->entity == 'civicrm_activity') { - $contactField = 'r.contact_id'; - $table = 'civicrm_activity e'; - $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name'); - $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts); - $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts); - $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts); - - if (!is_null($limitTo)) { - if ($limitTo == 0) { - // including the activity target contacts if 'in addition' is defined - $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$targetID}"; - } - else { - switch (CRM_Utils_Array::value($actionSchedule->recipient, $recipientOptions)) { - case 'Activity Assignees': - $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$assigneeID}"; - break; - - case 'Activity Source': - $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$sourceID}"; - break; - - default: - case 'Activity Targets': - $join[] = "INNER JOIN civicrm_activity_contact r ON r.activity_id = e.id AND record_type_id = {$targetID}"; - break; - } - } - } - // build where clause - if (!empty($value)) { - $where[] = "e.activity_type_id IN ({$value})"; - } - else { - $where[] = "e.activity_type_id IS NULL"; - } - if (!empty($status)) { - $where[] = "e.status_id IN ({$status})"; - } - $where[] = ' e.is_current_revision = 1 '; - $where[] = ' e.is_deleted = 0 '; - - $dateField = 'e.activity_date_time'; - } - - if ($mapping->entity == 'civicrm_participant') { - $table = 'civicrm_event r'; - $contactField = 'e.contact_id'; - $join[] = 'INNER JOIN civicrm_event r ON e.event_id = r.id'; - if ($actionSchedule->recipient_listing && $limitTo) { - $rList = explode(CRM_Core_DAO::VALUE_SEPARATOR, - trim($actionSchedule->recipient_listing, CRM_Core_DAO::VALUE_SEPARATOR) - ); - $rList = implode(',', $rList); - - switch (CRM_Utils_Array::value($actionSchedule->recipient, $recipientOptions)) { - case 'participant_role': - $where[] = "e.role_id IN ({$rList})"; - break; - - default: - break; - } - } - - // build where clause - if (!empty($value)) { - $where[] = ($mapping->entity_value == 'event_type') ? "r.event_type_id IN ({$value})" : "r.id IN ({$value})"; - } - else { - $where[] = ($mapping->entity_value == 'event_type') ? "r.event_type_id IS NULL" : "r.id IS NULL"; - } - - // participant status criteria not to be implemented - // for additional recipients - if (!empty($status)) { - $limitWhere[] = "e.status_id IN ({$status})"; - } - - $where[] = 'r.is_active = 1'; - $where[] = 'r.is_template = 0'; - $dateField = str_replace('event_', 'r.', $actionSchedule->start_action_date); - } - - $notINClause = ''; - if ($mapping->entity == 'civicrm_membership') { - $contactField = 'e.contact_id'; - $table = 'civicrm_membership e'; - // build where clause - if ($status == 2) { - //auto-renew memberships - $where[] = "e.contribution_recur_id IS NOT NULL "; - } - elseif ($status == 1) { - $where[] = "e.contribution_recur_id IS NULL "; - } - - // build where clause - if (!empty($value)) { - $where[] = "e.membership_type_id IN ({$value})"; - } - else { - $where[] = "e.membership_type_id IS NULL"; - } - - $where[] = "( e.is_override IS NULL OR e.is_override = 0 )"; - $dateField = str_replace('membership_', 'e.', $actionSchedule->start_action_date); - $notINClause = self::permissionedRelationships($contactField); - - $membershipStatus = CRM_Member_PseudoConstant::membershipStatus(NULL, "(is_current_member = 1 OR name = 'Expired')", 'id'); - $mStatus = implode(',', $membershipStatus); - $where[] = "e.status_id IN ({$mStatus})"; - - // We are not tracking the reference date for 'repeated' schedule reminders, - // for further details please check CRM-15376 - if ($actionSchedule->start_action_date && $actionSchedule->is_repeat == FALSE) { - $select[] = $dateField; - $selectColumns = "reference_date, " . $selectColumns; - } - } - - if ($mapping->entity == 'civicrm_contact') { - $contactFields = array( - 'birth_date', - 'created_date', - 'modified_date', - ); - if (in_array($value, $contactFields)) { - $dateDBField = $value; - $table = 'civicrm_contact e'; - $contactField = 'e.id'; - $where[] = 'e.is_deleted = 0'; - $where[] = 'e.is_deceased = 0'; - } - else { - //custom field - $customFieldParams = array('id' => substr($value, 7)); - $customGroup = $customField = array(); - CRM_Core_BAO_CustomField::retrieve($customFieldParams, $customField); - $dateDBField = $customField['column_name']; - $customGroupParams = array('id' => $customField['custom_group_id'], $customGroup); - CRM_Core_BAO_CustomGroup::retrieve($customGroupParams, $customGroup); - $from = $table = "{$customGroup['table_name']} e"; - $contactField = 'e.entity_id'; - $where[] = '1'; // possible to have no "where" in this case - } - - $status_ = explode(',', $status); - if (in_array(2, $status_)) { - // anniversary mode: - $dateField = 'DATE_ADD(e.' . $dateDBField . ', INTERVAL ROUND(DATEDIFF(DATE(' . $now . '), e.' . $dateDBField . ') / 365) YEAR)'; - $anniversary = TRUE; - } - else { - // regular mode: - $dateField = 'e.' . $dateDBField; - } - } - - // CRM-13577 Introduce Smart Groups Handling - if ($actionSchedule->group_id) { - - // Need to check if its a smart group or not - // Then decide which table to join onto the query - $group = CRM_Contact_DAO_Group::getTableName(); - - // Get the group information - $sql = " -SELECT $group.id, $group.cache_date, $group.saved_search_id, $group.children -FROM $group -WHERE $group.id = {$actionSchedule->group_id} -"; - - $groupDAO = CRM_Core_DAO::executeQuery($sql); - $isSmartGroup = FALSE; - if ( - $groupDAO->fetch() && - !empty($groupDAO->saved_search_id) - ) { - // Check that the group is in place in the cache and up to date - CRM_Contact_BAO_GroupContactCache::check($actionSchedule->group_id); - // Set smart group flag - $isSmartGroup = TRUE; - } - } - // CRM-13577 End Introduce Smart Groups Handling - - if ($limitTo) { - if ($actionSchedule->group_id) { - // CRM-13577 If smart group then use Cache table - if ($isSmartGroup) { - $join[] = "INNER JOIN civicrm_group_contact_cache grp ON {$contactField} = grp.contact_id"; - $where[] = "grp.group_id IN ({$actionSchedule->group_id})"; - } - else { - $join[] = "INNER JOIN civicrm_group_contact grp ON {$contactField} = grp.contact_id AND grp.status = 'Added'"; - $where[] = "grp.group_id IN ({$actionSchedule->group_id})"; - } - } - elseif (!empty($actionSchedule->recipient_manual)) { - $rList = CRM_Utils_Type::escape($actionSchedule->recipient_manual, 'String'); - $where[] = "{$contactField} IN ({$rList})"; - } - } - elseif (!is_null($limitTo)) { - $addGroup = $addWhere = ''; - if ($actionSchedule->group_id) { - // CRM-13577 If smart group then use Cache table - if ($isSmartGroup) { - $addGroup = " INNER JOIN civicrm_group_contact_cache grp ON c.id = grp.contact_id"; - $addWhere = " grp.group_id IN ({$actionSchedule->group_id})"; - } - else { - $addGroup = " INNER JOIN civicrm_group_contact grp ON c.id = grp.contact_id AND grp.status = 'Added'"; - $addWhere = " grp.group_id IN ({$actionSchedule->group_id})"; - } - } - if (!empty($actionSchedule->recipient_manual)) { - $rList = CRM_Utils_Type::escape($actionSchedule->recipient_manual, 'String'); - $addWhere = "c.id IN ({$rList})"; - } - } - - $select[] = "{$contactField} as contact_id"; - $select[] = 'e.id as entity_id'; - $select[] = "'{$mapping->entity}' as entity_table"; - $select[] = "{$actionSchedule->id} as action_schedule_id"; - $reminderJoinClause = "civicrm_action_log reminder ON reminder.contact_id = {$contactField} AND -reminder.entity_id = e.id AND -reminder.entity_table = '{$mapping->entity}' AND -reminder.action_schedule_id = %1"; - - if ($anniversary) { - // only consider reminders less than 11 months ago - $reminderJoinClause .= " AND reminder.action_date_time > DATE_SUB({$now}, INTERVAL 11 MONTH)"; - } - - if ($table != 'civicrm_contact e') { - $join[] = "INNER JOIN civicrm_contact c ON c.id = {$contactField} AND c.is_deleted = 0 AND c.is_deceased = 0 "; - } - - if ($actionSchedule->start_action_date) { - $startDateClause = array(); - $op = ($actionSchedule->start_action_condition == 'before' ? '<=' : '>='); - $operator = ($actionSchedule->start_action_condition == 'before' ? 'DATE_SUB' : 'DATE_ADD'); - $date = $operator . "({$dateField}, INTERVAL {$actionSchedule->start_action_offset} {$actionSchedule->start_action_unit})"; - $startDateClause[] = "'{$now}' >= {$date}"; - if ($mapping->entity == 'civicrm_participant') { - $startDateClause[] = $operator . "({$now}, INTERVAL 1 DAY ) {$op} " . $dateField; - } - else { - $startDateClause[] = "DATE_SUB({$now}, INTERVAL 1 DAY ) <= {$date}"; - } - - $startDate = implode(' AND ', $startDateClause); - } - elseif ($actionSchedule->absolute_date) { - $startDate = "DATEDIFF(DATE('{$now}'),'{$actionSchedule->absolute_date}') = 0"; - } - - // ( now >= date_built_from_start_time ) OR ( now = absolute_date ) - $dateClause = "reminder.id IS NULL AND {$startDate}"; - - // start composing query - $selectClause = 'SELECT ' . implode(', ', $select); - $fromClause = "FROM $from"; - $joinClause = !empty($join) ? implode(' ', $join) : ''; - $whereClause = 'WHERE ' . implode(' AND ', $where); - $limitWhereClause = ''; - if (!empty($limitWhere)) { - $limitWhereClause = ' AND ' . implode(' AND ', $limitWhere); - } - - $query = " -INSERT INTO civicrm_action_log ({$selectColumns}) -{$selectClause} -{$fromClause} -{$joinClause} -LEFT JOIN {$reminderJoinClause} -{$whereClause} {$limitWhereClause} AND {$dateClause} {$notINClause} -"; - - // In some cases reference_date got outdated due to many reason e.g. In Membership renewal end_date got extended - // which means reference date mismatches with the end_date where end_date may be used as the start_action_date - // criteria for some schedule reminder so in order to send new reminder we INSERT new reminder with new reference_date - // value via UNION operation - if (strpos($selectColumns, 'reference_date') !== FALSE) { - $dateClause = str_replace('reminder.id IS NULL', 'reminder.id IS NOT NULL', $dateClause); - $referenceQuery = " -INSERT INTO civicrm_action_log ({$selectColumns}) -{$selectClause} -{$fromClause} -{$joinClause} - LEFT JOIN {$reminderJoinClause} -{$whereClause} {$limitWhereClause} {$notINClause} AND {$dateClause} AND - reminder.action_date_time IS NOT NULL AND - reminder.reference_date IS NOT NULL -GROUP BY reminder.id, reminder.reference_date -HAVING reminder.id = MAX(reminder.id) AND reminder.reference_date <> {$dateField} -"; - } - - CRM_Core_DAO::executeQuery($query, array(1 => array($actionSchedule->id, 'Integer'))); - - if (!empty($referenceQuery)) { - CRM_Core_DAO::executeQuery($referenceQuery, array(1 => array($actionSchedule->id, 'Integer'))); - } - - $isSendToAdditionalContacts = (!is_null($limitTo) && $limitTo == 0 && (!empty($addGroup) || !empty($addWhere))) ? TRUE : FALSE; - if ($isSendToAdditionalContacts) { - $contactTable = "civicrm_contact c"; - $addSelect = "SELECT c.id as contact_id, c.id as entity_id, 'civicrm_contact' as entity_table, {$actionSchedule->id} as action_schedule_id"; - $additionReminderClause = "civicrm_action_log reminder ON reminder.contact_id = c.id AND - reminder.entity_id = c.id AND - reminder.entity_table = 'civicrm_contact' AND - reminder.action_schedule_id = {$actionSchedule->id}"; - $addWhereClause = ''; - if ($addWhere) { - $addWhereClause = "AND {$addWhere}"; - } - $insertAdditionalSql = " -INSERT INTO civicrm_action_log (contact_id, entity_id, entity_table, action_schedule_id) -{$addSelect} -FROM ({$contactTable}) -LEFT JOIN {$additionReminderClause} -{$addGroup} -WHERE c.is_deleted = 0 AND c.is_deceased = 0 -{$addWhereClause} - -AND c.id NOT IN ( - SELECT rem.contact_id - FROM civicrm_action_log rem INNER JOIN {$mapping->entity} e ON rem.entity_id = e.id - WHERE rem.action_schedule_id = {$actionSchedule->id} - AND rem.entity_table = '{$mapping->entity}' - ) -GROUP BY c.id -"; - CRM_Core_DAO::executeQuery($insertAdditionalSql); - } - // if repeat is turned ON: - if ($actionSchedule->is_repeat) { - $repeatEvent = ($actionSchedule->end_action == 'before' ? 'DATE_SUB' : 'DATE_ADD') . "({$dateField}, INTERVAL {$actionSchedule->end_frequency_interval} {$actionSchedule->end_frequency_unit})"; - - if ($actionSchedule->repetition_frequency_unit == 'day') { - $hrs = 24 * $actionSchedule->repetition_frequency_interval; - } - elseif ($actionSchedule->repetition_frequency_unit == 'week') { - $hrs = 24 * $actionSchedule->repetition_frequency_interval * 7; - } - elseif ($actionSchedule->repetition_frequency_unit == 'month') { - $hrs = "24*(DATEDIFF(DATE_ADD(latest_log_time, INTERVAL 1 MONTH ), latest_log_time))"; - } - elseif ($actionSchedule->repetition_frequency_unit == 'year') { - $hrs = "24*(DATEDIFF(DATE_ADD(latest_log_time, INTERVAL 1 YEAR ), latest_log_time))"; - } - else { - $hrs = $actionSchedule->repetition_frequency_interval; - } - - // (now <= repeat_end_time ) - $repeatEventClause = "'{$now}' <= {$repeatEvent}"; - // diff(now && logged_date_time) >= repeat_interval - $havingClause = "HAVING TIMEDIFF({$now}, latest_log_time) >= TIME('{$hrs}:00:00')"; - $groupByClause = 'GROUP BY reminder.contact_id, reminder.entity_id, reminder.entity_table'; - $selectClause .= ', MAX(reminder.action_date_time) as latest_log_time'; - //CRM-15376 - do not send our reminders if original criteria no longer applies - // the first part of the startDateClause array is the earliest the reminder can be sent. If the - // event (e.g membership_end_date) has changed then the reminder may no longer apply - // @todo - this only handles events that get moved later. Potentially they might get moved earlier - $originalEventStartDateClause = empty($startDateClause) ? '' : 'AND' . $startDateClause[0]; - $sqlInsertValues = "{$selectClause} -{$fromClause} -{$joinClause} -INNER JOIN {$reminderJoinClause} -{$whereClause} {$limitWhereClause} AND {$repeatEventClause} {$originalEventStartDateClause} {$notINClause} -{$groupByClause} -{$havingClause}"; - - $valsqlInsertValues = CRM_Core_DAO::executeQuery($sqlInsertValues, array( - 1 => array( - $actionSchedule->id, - 'Integer', - ), - ) - ); - - $arrValues = array(); - while ($valsqlInsertValues->fetch()) { - $arrValues[] = "( {$valsqlInsertValues->contact_id}, {$valsqlInsertValues->entity_id}, '{$valsqlInsertValues->entity_table}',{$valsqlInsertValues->action_schedule_id} )"; - } - - $valString = implode(',', $arrValues); - - if ($valString) { - $query = ' - INSERT INTO civicrm_action_log (contact_id, entity_id, entity_table, action_schedule_id) VALUES ' . $valString; - CRM_Core_DAO::executeQuery($query, array(1 => array($actionSchedule->id, 'Integer'))); - } - - if ($isSendToAdditionalContacts) { - $addSelect .= ', MAX(reminder.action_date_time) as latest_log_time'; - $sqlEndEventCheck = " -SELECT * FROM {$table} -{$whereClause} AND {$repeatEventClause} LIMIT 1"; - - $daoCheck = CRM_Core_DAO::executeQuery($sqlEndEventCheck); - if ($daoCheck->fetch()) { - $valSqlAdditionInsert = " -{$addSelect} -FROM {$contactTable} -{$addGroup} -INNER JOIN {$additionReminderClause} -WHERE {$addWhere} AND c.is_deleted = 0 AND c.is_deceased = 0 -GROUP BY reminder.contact_id -{$havingClause} -"; - $daoForVals = CRM_Core_DAO::executeQuery($valSqlAdditionInsert); - $addValues = array(); - while ($daoForVals->fetch()) { - $addValues[] = "( {$daoForVals->contact_id}, {$daoForVals->entity_id}, '{$daoForVals->entity_table}',{$daoForVals->action_schedule_id} )"; - } - $valString = implode(',', $addValues); - - if ($valString) { - $query = ' - INSERT INTO civicrm_action_log (contact_id, entity_id, entity_table, action_schedule_id) VALUES ' . $valString; - CRM_Core_DAO::executeQuery($query); - } - } - } - } - } - } - - /** - * @param $field - * - * @return null|string - */ - public static function permissionedRelationships($field) { - $query = ' -SELECT cm.id AS owner_id, cm.contact_id AS owner_contact, m.id AS slave_id, m.contact_id AS slave_contact, cmt.relationship_type_id AS relation_type, rel.contact_id_a, rel.contact_id_b, rel.is_permission_a_b, rel.is_permission_b_a -FROM civicrm_membership m -LEFT JOIN civicrm_membership cm ON cm.id = m.owner_membership_id -LEFT JOIN civicrm_membership_type cmt ON cmt.id = m.membership_type_id -LEFT JOIN civicrm_relationship rel ON ( ( rel.contact_id_a = m.contact_id AND rel.contact_id_b = cm.contact_id AND rel.relationship_type_id = cmt.relationship_type_id ) - OR ( rel.contact_id_a = cm.contact_id AND rel.contact_id_b = m.contact_id AND rel.relationship_type_id = cmt.relationship_type_id ) ) -WHERE m.owner_membership_id IS NOT NULL AND - ( rel.is_permission_a_b = 0 OR rel.is_permission_b_a = 0) - -'; - $excludeIds = array(); - $dao = CRM_Core_DAO::executeQuery($query, array()); - while ($dao->fetch()) { - if ($dao->slave_contact == $dao->contact_id_a && $dao->is_permission_a_b == 0) { - $excludeIds[] = $dao->slave_contact; - } - elseif ($dao->slave_contact == $dao->contact_id_b && $dao->is_permission_b_a == 0) { - $excludeIds[] = $dao->slave_contact; - } - } - - if (!empty($excludeIds)) { - $clause = "AND {$field} NOT IN ( " . implode(', ', $excludeIds) . ' ) '; - return $clause; + /** @var \Civi\ActionSchedule\Mapping $mapping */ + $mapping = CRM_Utils_Array::first(self::getMappings(array( + 'id' => $mappingID, + ))); + $builder = new \Civi\ActionSchedule\RecipientBuilder($now, $actionSchedule, $mapping); + $builder->build(); } - return NULL; } /** @@ -1471,10 +402,10 @@ WHERE m.owner_membership_id IS NOT NULL AND public static function processQueue($now = NULL, $params = array()) { $now = $now ? CRM_Utils_Time::setTime($now) : CRM_Utils_Time::getTime(); - $mappings = self::getMapping(); + $mappings = CRM_Core_BAO_ActionSchedule::getMappings(); foreach ($mappings as $mappingID => $mapping) { - self::buildRecipientContacts($mappingID, $now, $params); - self::sendMailings($mappingID, $now); + CRM_Core_BAO_ActionSchedule::buildRecipientContacts($mappingID, $now, $params); + CRM_Core_BAO_ActionSchedule::sendMailings($mappingID, $now); } $result = array( @@ -1496,7 +427,7 @@ WHERE m.owner_membership_id IS NOT NULL AND entity_value = %2"; $params = array( - 1 => array($mappingID, 'Integer'), + 1 => array($mappingID, 'String'), 2 => array($id, 'Integer'), ); return CRM_Core_DAO::singleValueQuery($queryString, $params); @@ -1509,26 +440,288 @@ WHERE m.owner_membership_id IS NOT NULL AND * @return array */ public static function getRecipientListing($mappingID, $recipientType) { - $options = array(); - if (!$mappingID || !$recipientType) { - return $options; + if (!$mappingID) { + return array(); } - $mapping = self::getMapping($mappingID); + /** @var \Civi\ActionSchedule\Mapping $mapping */ + $mapping = CRM_Utils_Array::first(CRM_Core_BAO_ActionSchedule::getMappings(array( + 'id' => $mappingID, + ))); + return $mapping->getRecipientListing($recipientType); + } - switch ($mapping['entity']) { - case 'civicrm_participant': - $eventContacts = CRM_Core_OptionGroup::values('event_contacts', FALSE, FALSE, FALSE, NULL, 'name', TRUE, FALSE, 'name'); - if (empty($eventContacts[$recipientType])) { - return $options; - } - if ($eventContacts[$recipientType] == 'participant_role') { - $options = CRM_Event_PseudoConstant::participantRole(); - } - break; + /** + * @param $communication_language + * @param $preferred_language + */ + public static function setCommunicationLanguage($communication_language, $preferred_language) { + $config = CRM_Core_Config::singleton(); + $language = $config->lcMessages; + + // prepare the language for the email + if ($communication_language == CRM_Core_I18n::AUTO) { + if (!empty($preferred_language)) { + $language = $preferred_language; + } + } + else { + $language = $communication_language; + } + + // language not in the existing language, use default + $languages = CRM_Core_I18n::languages(TRUE); + if (!in_array($language, $languages)) { + $language = $config->lcMessages; + } + + // change the language + $i18n = CRM_Core_I18n::singleton(); + $i18n->setLanguage($language); + } + + /** + * Save a record about the delivery of a reminder email. + * + * WISHLIST: Instead of saving $actionSchedule->body_html, call this immediately after + * sending the message and pass in the fully rendered text of the message. + * + * @param CRM_Core_DAO_ActionSchedule $actionSchedule + * @param Civi\ActionSchedule\Mapping $mapping + * @param int $contactID + * @param int $entityID + * @param int|NULL $caseID + * @throws CRM_Core_Exception + */ + protected static function createMailingActivity($actionSchedule, $mapping, $contactID, $entityID, $caseID) { + $session = CRM_Core_Session::singleton(); + + if ($mapping->getEntity() == 'civicrm_membership') { + $activityTypeID + = CRM_Core_OptionGroup::getValue('activity_type', 'Membership Renewal Reminder', 'name'); + } + else { + $activityTypeID + = CRM_Core_OptionGroup::getValue('activity_type', 'Reminder Sent', 'name'); } - return $options; + $activityParams = array( + 'subject' => $actionSchedule->title, + 'details' => $actionSchedule->body_html, + 'source_contact_id' => $session->get('userID') ? $session->get('userID') : $contactID, + 'target_contact_id' => $contactID, + 'activity_date_time' => CRM_Utils_Time::getTime('YmdHis'), + 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'), + 'activity_type_id' => $activityTypeID, + 'source_record_id' => $entityID, + ); + $activity = CRM_Activity_BAO_Activity::create($activityParams); + + //file reminder on case if source activity is a case activity + if (!empty($caseID)) { + $caseActivityParams = array(); + $caseActivityParams['case_id'] = $caseID; + $caseActivityParams['activity_id'] = $activity->id; + CRM_Case_BAO_Case::processCaseActivity($caseActivityParams); + } + } + + /** + * @param \Civi\ActionSchedule\MappingInterface $mapping + * @param \CRM_Core_DAO_ActionSchedule $actionSchedule + * @return string + */ + protected static function prepareMailingQuery($mapping, $actionSchedule) { + $select = CRM_Utils_SQL_Select::from('civicrm_action_log reminder') + ->select("reminder.id as reminderID, reminder.contact_id as contactID, reminder.entity_table as entityTable, reminder.*, e.id AS entityID") + ->join('e', "!casMailingJoinType !casMappingEntity e ON !casEntityJoinExpr") + ->select("e.id as entityID, e.*") + ->where("reminder.action_schedule_id = #casActionScheduleId") + ->where("reminder.action_date_time IS NULL") + ->param(array( + 'casActionScheduleId' => $actionSchedule->id, + 'casMailingJoinType' => ($actionSchedule->limit_to == 0) ? 'LEFT JOIN' : 'INNER JOIN', + 'casMappingId' => $mapping->getId(), + 'casMappingEntity' => $mapping->getEntity(), + 'casEntityJoinExpr' => 'e.id = reminder.entity_id', + )); + + if ($actionSchedule->limit_to == 0) { + $select->where("e.id = reminder.entity_id OR reminder.entity_table = 'civicrm_contact'"); + } + + \Civi\Core\Container::singleton()->get('dispatcher') + ->dispatch( + \Civi\ActionSchedule\Events::MAILING_QUERY, + new \Civi\ActionSchedule\Event\MailingQueryEvent($actionSchedule, $mapping, $select) + ); + + return $select->toSQL(); + } + + /** + * @param \Civi\Token\TokenRow $tokenRow + * @param CRM_Core_DAO_ActionSchedule $schedule + * @param int $toContactID + * @throws CRM_Core_Exception + * @return array + * List of error messages. + */ + protected static function sendReminderSms($tokenRow, $schedule, $toContactID) { + $toPhoneNumber = self::pickSmsPhoneNumber($toContactID); + if (!$toPhoneNumber) { + return array("sms_phone_missing" => "Couldn't find recipient's phone number."); + } + + $messageSubject = $tokenRow->render('subject'); + $sms_body_text = $tokenRow->render('sms_body_text'); + + $session = CRM_Core_Session::singleton(); + $userID = $session->get('userID') ? $session->get('userID') : $tokenRow->context['contactId']; + $smsParams = array( + 'To' => $toPhoneNumber, + 'provider_id' => $schedule->sms_provider_id, + 'activity_subject' => $messageSubject, + ); + $activityTypeID = CRM_Core_OptionGroup::getValue('activity_type', + 'SMS', + 'name' + ); + $activityParams = array( + 'source_contact_id' => $userID, + 'activity_type_id' => $activityTypeID, + 'activity_date_time' => date('YmdHis'), + 'subject' => $messageSubject, + 'details' => $sms_body_text, + 'status_id' => CRM_Core_OptionGroup::getValue('activity_status', 'Completed', 'name'), + ); + + $activity = CRM_Activity_BAO_Activity::create($activityParams); + + CRM_Activity_BAO_Activity::sendSMSMessage($tokenRow->context['contactId'], + $sms_body_text, + $smsParams, + $activity->id, + $userID + ); + + return array(); + } + + /** + * @param CRM_Core_DAO_ActionSchedule $actionSchedule + * @return string + * Ex: "Alice ". + */ + protected static function pickFromEmail($actionSchedule) { + $domainValues = CRM_Core_BAO_Domain::getNameAndEmail(); + $fromEmailAddress = "$domainValues[0] <$domainValues[1]>"; + if ($actionSchedule->from_email) { + $fromEmailAddress = "$actionSchedule->from_name <$actionSchedule->from_email>"; + return $fromEmailAddress; + } + return $fromEmailAddress; + } + + /** + * @param \Civi\Token\TokenRow $tokenRow + * @param CRM_Core_DAO_ActionSchedule $schedule + * @param int $toContactID + * @return array + * List of error messages. + */ + protected static function sendReminderEmail($tokenRow, $schedule, $toContactID) { + $toEmail = CRM_Contact_BAO_Contact::getPrimaryEmail($toContactID); + if (!$toEmail) { + return array("email_missing" => "Couldn't find recipient's email address."); + } + + $body_text = $tokenRow->render('body_text'); + $body_html = $tokenRow->render('body_html'); + if (!$schedule->body_text) { + $body_text = CRM_Utils_String::htmlToText($body_html); + } + + // set up the parameters for CRM_Utils_Mail::send + $mailParams = array( + 'groupName' => 'Scheduled Reminder Sender', + 'from' => self::pickFromEmail($schedule), + 'toName' => $tokenRow->context['contact']['display_name'], + 'toEmail' => $toEmail, + 'subject' => $tokenRow->render('subject'), + 'entity' => 'action_schedule', + 'entity_id' => $schedule->id, + ); + + if (!$body_html || $tokenRow->context['contact']['preferred_mail_format'] == 'Text' || + $tokenRow->context['contact']['preferred_mail_format'] == 'Both' + ) { + // render the & entities in text mode, so that the links work + $mailParams['text'] = str_replace('&', '&', $body_text); + } + if ($body_html && ($tokenRow->context['contact']['preferred_mail_format'] == 'HTML' || + $tokenRow->context['contact']['preferred_mail_format'] == 'Both' + ) + ) { + $mailParams['html'] = $body_html; + } + $result = CRM_Utils_Mail::send($mailParams); + if (!$result || is_a($result, 'PEAR_Error')) { + return array('email_fail' => 'Failed to send message'); + } + + return array(); + } + + /** + * @param CRM_Core_DAO_ActionSchedule $schedule + * @param \Civi\ActionSchedule\Mapping $mapping + * @return \Civi\Token\TokenProcessor + */ + protected static function createTokenProcessor($schedule, $mapping) { + $tp = new \Civi\Token\TokenProcessor(\Civi\Core\Container::singleton()->get('dispatcher'), array( + 'controller' => __CLASS__, + 'actionSchedule' => $schedule, + 'actionMapping' => $mapping, + 'smarty' => TRUE, + )); + $tp->addMessage('body_text', $schedule->body_text, 'text/plain'); + $tp->addMessage('body_html', $schedule->body_html, 'text/html'); + $tp->addMessage('sms_body_text', $schedule->sms_body_text, 'text/plain'); + $tp->addMessage('subject', $schedule->subject, 'text/plain'); + return $tp; + } + + /** + * @param $dao + * @return string|NULL + */ + protected static function pickSmsPhoneNumber($smsToContactId) { + $toPhoneNumbers = CRM_Core_BAO_Phone::allPhones($smsToContactId, FALSE, 'Mobile', array( + 'is_deceased' => 0, + 'is_deleted' => 0, + 'do_not_sms' => 0, + )); + //to get primary mobile ph,if not get a first mobile phONE + if (!empty($toPhoneNumbers)) { + $toPhoneNumberDetails = reset($toPhoneNumbers); + $toPhoneNumber = CRM_Utils_Array::value('phone', $toPhoneNumberDetails); + return $toPhoneNumber; + } + return NULL; + } + + /** + * Get the list of generic recipient types supported by all entities/mappings. + * + * @return array + * array(mixed $value => string $label). + */ + protected static function getAdditionalRecipients() { + return array( + 'manual' => ts('Choose Recipient(s)'), + 'group' => ts('Select Group'), + ); } }