From 83ef8f4c6cd7617b66ce3378b5a9fef67cc988b0 Mon Sep 17 00:00:00 2001 From: colemanw Date: Tue, 15 Aug 2023 19:17:28 -0500 Subject: [PATCH] ActionSchedule - Enable `getEntityTable()` to return dynamic values This functionality is needed for SavedSearch-based reminders, which do not have a fixed base-table. --- CRM/Activity/ActionMapping.php | 2 +- CRM/Activity/Tokens.php | 2 +- CRM/Contact/ActionMapping.php | 6 +++--- CRM/Core/BAO/ActionSchedule.php | 4 ++-- CRM/Core/EntityTokens.php | 5 ++--- CRM/Event/ActionMapping.php | 2 +- CRM/Event/ParticipantTokens.php | 2 +- CRM/Member/ActionMapping.php | 2 +- Civi/ActionSchedule/MappingBase.php | 4 ++-- Civi/ActionSchedule/MappingInterface.php | 3 ++- Civi/ActionSchedule/RecipientBuilder.php | 14 +++++++++----- 11 files changed, 25 insertions(+), 21 deletions(-) diff --git a/CRM/Activity/ActionMapping.php b/CRM/Activity/ActionMapping.php index 04a0a41f86..3f472bbc89 100644 --- a/CRM/Activity/ActionMapping.php +++ b/CRM/Activity/ActionMapping.php @@ -108,7 +108,7 @@ class CRM_Activity_ActionMapping extends \Civi\ActionSchedule\MappingBase { $selectedValues = (array) \CRM_Utils_Array::explodePadded($schedule->entity_value); $selectedStatuses = (array) \CRM_Utils_Array::explodePadded($schedule->entity_status); - $query = \CRM_Utils_SQL_Select::from("{$this->getEntityTable()} e")->param($defaultParams); + $query = \CRM_Utils_SQL_Select::from("civicrm_activity e")->param($defaultParams); $query['casAddlCheckFrom'] = 'civicrm_activity e'; $query['casContactIdField'] = 'r.contact_id'; $query['casEntityIdField'] = 'e.id'; diff --git a/CRM/Activity/Tokens.php b/CRM/Activity/Tokens.php index 79ff0ff0ad..714c26fa0a 100644 --- a/CRM/Activity/Tokens.php +++ b/CRM/Activity/Tokens.php @@ -37,7 +37,7 @@ class CRM_Activity_Tokens extends CRM_Core_EntityTokens { * @inheritDoc */ public function alterActionScheduleQuery(MailingQueryEvent $e): void { - if ($e->mapping->getEntityTable() !== $this->getExtendableTableName()) { + if ($e->mapping->getEntityTable($e->actionSchedule) !== $this->getExtendableTableName()) { return; } diff --git a/CRM/Contact/ActionMapping.php b/CRM/Contact/ActionMapping.php index 440d9fdde0..ca2788fada 100644 --- a/CRM/Contact/ActionMapping.php +++ b/CRM/Contact/ActionMapping.php @@ -95,14 +95,14 @@ class CRM_Contact_ActionMapping extends \Civi\ActionSchedule\MappingBase { $selectedValues = (array) \CRM_Utils_Array::explodePadded($schedule->entity_value); $selectedStatuses = (array) \CRM_Utils_Array::explodePadded($schedule->entity_status); - // FIXME: This assumes that $values only has one field, but UI shows multiselect. - // Properly supporting multiselect would require total rewrite of this function. + // Only one value is allowed for this mapping type. + // The form and API both enforce this, so this error should never happen. if (count($selectedValues) != 1 || !isset($selectedValues[0])) { throw new \CRM_Core_Exception("Error: Scheduled reminders may only have one contact field."); } elseif (in_array($selectedValues[0], $this->contactDateFields)) { $dateDBField = $selectedValues[0]; - $query = \CRM_Utils_SQL_Select::from("{$this->getEntityTable()} e")->param($defaultParams); + $query = \CRM_Utils_SQL_Select::from('civicrm_contact e')->param($defaultParams); $query->param([ 'casAddlCheckFrom' => 'civicrm_contact e', 'casContactIdField' => 'e.id', diff --git a/CRM/Core/BAO/ActionSchedule.php b/CRM/Core/BAO/ActionSchedule.php index 668d894f77..4e2b468a39 100644 --- a/CRM/Core/BAO/ActionSchedule.php +++ b/CRM/Core/BAO/ActionSchedule.php @@ -540,7 +540,7 @@ FROM civicrm_action_schedule cas protected static function createMailingActivity($tokenRow, $mapping, $contactID, $entityID, $caseID) { $session = CRM_Core_Session::singleton(); - if ($mapping->getEntityTable() == 'civicrm_membership') { + if ($mapping->getEntityName() === 'Membership') { // @todo - not required with api $activityTypeID = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Membership Renewal Reminder'); @@ -585,7 +585,7 @@ FROM civicrm_action_schedule cas 'casActionScheduleId' => $actionSchedule->id, 'casMailingJoinType' => ($actionSchedule->limit_to == 2) ? 'LEFT JOIN' : 'INNER JOIN', 'casMappingId' => $mapping->getId(), - 'casMappingEntity' => $mapping->getEntityTable(), + 'casMappingEntity' => $mapping->getEntityTable($actionSchedule), 'casEntityJoinExpr' => 'e.id = IF(reminder.entity_table = "civicrm_contact", reminder.contact_id, reminder.entity_id)', ]); diff --git a/CRM/Core/EntityTokens.php b/CRM/Core/EntityTokens.php index a5e80be223..317dc5e074 100644 --- a/CRM/Core/EntityTokens.php +++ b/CRM/Core/EntityTokens.php @@ -350,8 +350,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber { public function checkActive(TokenProcessor $processor) { return ((!empty($processor->context['actionMapping']) // This makes the 'schema context compulsory - which feels accidental - // since recent discu - && $processor->context['actionMapping']->getEntityTable()) || in_array($this->getEntityIDField(), $processor->context['schema'])) && in_array($this->getApiEntityName(), array_keys(\Civi::service('action_object_provider')->getEntities())); + && $processor->context['actionMapping']->getEntityName()) || in_array($this->getEntityIDField(), $processor->context['schema'])) && in_array($this->getApiEntityName(), array_keys(\Civi::service('action_object_provider')->getEntities())); } /** @@ -360,7 +359,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber { * @param \Civi\ActionSchedule\Event\MailingQueryEvent $e */ public function alterActionScheduleQuery(MailingQueryEvent $e): void { - if ($e->mapping->getEntityTable() !== $this->getExtendableTableName()) { + if ($e->mapping->getEntityTable($e->actionSchedule) !== $this->getExtendableTableName()) { return; } $e->query->select('e.id AS tokenContext_' . $this->getEntityIDField()); diff --git a/CRM/Event/ActionMapping.php b/CRM/Event/ActionMapping.php index 8049942cfb..9450794438 100644 --- a/CRM/Event/ActionMapping.php +++ b/CRM/Event/ActionMapping.php @@ -138,7 +138,7 @@ abstract class CRM_Event_ActionMapping extends \Civi\ActionSchedule\MappingBase $selectedValues = (array) \CRM_Utils_Array::explodePadded($schedule->entity_value); $selectedStatuses = (array) \CRM_Utils_Array::explodePadded($schedule->entity_status); - $query = \CRM_Utils_SQL_Select::from("{$this->getEntityTable()} e")->param($defaultParams); + $query = \CRM_Utils_SQL_Select::from('civicrm_participant e')->param($defaultParams); $query['casAddlCheckFrom'] = 'civicrm_event r'; $query['casContactIdField'] = 'e.contact_id'; $query['casEntityIdField'] = 'e.id'; diff --git a/CRM/Event/ParticipantTokens.php b/CRM/Event/ParticipantTokens.php index 6df913b0ac..1c5d000950 100644 --- a/CRM/Event/ParticipantTokens.php +++ b/CRM/Event/ParticipantTokens.php @@ -54,7 +54,7 @@ class CRM_Event_ParticipantTokens extends CRM_Core_EntityTokens { public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQueryEvent $e): void { // When targeting `civicrm_participant` records, we enable both `{participant.*}` (per usual) and the related `{event.*}`. parent::alterActionScheduleQuery($e); - if ($e->mapping->getEntityTable() === $this->getExtendableTableName()) { + if ($e->mapping->getEntityTable($e->actionSchedule) === $this->getExtendableTableName()) { $e->query->select('e.event_id AS tokenContext_eventId'); } } diff --git a/CRM/Member/ActionMapping.php b/CRM/Member/ActionMapping.php index 094aebda0a..ff62e653dc 100644 --- a/CRM/Member/ActionMapping.php +++ b/CRM/Member/ActionMapping.php @@ -80,7 +80,7 @@ class CRM_Member_ActionMapping extends \Civi\ActionSchedule\MappingBase { $selectedValues = (array) \CRM_Utils_Array::explodePadded($schedule->entity_value); $selectedStatuses = (array) \CRM_Utils_Array::explodePadded($schedule->entity_status); - $query = \CRM_Utils_SQL_Select::from("{$this->getEntityTable()} e")->param($defaultParams); + $query = \CRM_Utils_SQL_Select::from('civicrm_membership e')->param($defaultParams); $query['casAddlCheckFrom'] = 'civicrm_membership e'; $query['casContactIdField'] = 'e.contact_id'; $query['casEntityIdField'] = 'e.id'; diff --git a/Civi/ActionSchedule/MappingBase.php b/Civi/ActionSchedule/MappingBase.php index 76c37839a5..419f5c6114 100644 --- a/Civi/ActionSchedule/MappingBase.php +++ b/Civi/ActionSchedule/MappingBase.php @@ -41,7 +41,7 @@ abstract class MappingBase extends AutoSubscriber implements MappingInterface { $registrations->register(new static()); } - public function getEntityTable(): string { + public function getEntityTable(\CRM_Core_DAO_ActionSchedule $actionSchedule): string { return \CRM_Core_DAO_AllCoreTables::getTableForEntityName($this->getEntityName()); } @@ -52,7 +52,7 @@ abstract class MappingBase extends AutoSubscriber implements MappingInterface { */ public function getEntity(): string { \CRM_Core_Error::deprecatedFunctionWarning('getEntityTable'); - return $this->getEntityTable(); + return \CRM_Core_DAO_AllCoreTables::getTableForEntityName($this->getEntityName()); } public function getLabel(): string { diff --git a/Civi/ActionSchedule/MappingInterface.php b/Civi/ActionSchedule/MappingInterface.php index 610e14598c..609d40f7f1 100644 --- a/Civi/ActionSchedule/MappingInterface.php +++ b/Civi/ActionSchedule/MappingInterface.php @@ -38,9 +38,10 @@ interface MappingInterface extends SpecProviderInterface { /** * Name of the table belonging to the main entity e.g. `civicrm_activity` + * @param \CRM_Core_DAO_ActionSchedule $actionSchedule * @return string */ - public function getEntityTable(): string; + public function getEntityTable(\CRM_Core_DAO_ActionSchedule $actionSchedule): string; /** * Main entity name e.g. `Activity` diff --git a/Civi/ActionSchedule/RecipientBuilder.php b/Civi/ActionSchedule/RecipientBuilder.php index 959baf56f0..c58fa8b50c 100644 --- a/Civi/ActionSchedule/RecipientBuilder.php +++ b/Civi/ActionSchedule/RecipientBuilder.php @@ -190,9 +190,9 @@ class RecipientBuilder { ->merge($this->prepareAddlFilter('c.id')) ->where("c.id NOT IN ( SELECT rem.contact_id - FROM civicrm_action_log rem INNER JOIN {$this->mapping->getEntityTable()} e ON rem.entity_id = e.id + FROM civicrm_action_log rem INNER JOIN {$this->getMappingTable()} e ON rem.entity_id = e.id WHERE rem.action_schedule_id = {$this->actionSchedule->id} - AND rem.entity_table = '{$this->mapping->getEntityTable()}' + AND rem.entity_table = '{$this->getMappingTable()}' )") // Where does e.id come from here? ^^^ ->groupBy("c.id") @@ -276,7 +276,7 @@ class RecipientBuilder { $defaultParams = [ 'casActionScheduleId' => $this->actionSchedule->id, 'casMappingId' => $this->mapping->getId(), - 'casMappingEntity' => $this->mapping->getEntityTable(), + 'casMappingEntity' => $this->getMappingTable(), 'casNow' => $this->now, ]; @@ -402,7 +402,7 @@ class RecipientBuilder { $date = $operator . "(!casDateField, INTERVAL {$actionSchedule->start_action_offset} {$actionSchedule->start_action_unit})"; $startDateClauses[] = "'!casNow' >= {$date}"; // This is weird. Waddupwidat? - if ($this->mapping->getEntityTable() == 'civicrm_participant') { + if ($this->getMappingTable() == 'civicrm_participant') { $startDateClauses[] = $operator . "(!casNow, INTERVAL 1 DAY ) {$op} " . '!casDateField'; } else { @@ -566,7 +566,7 @@ WHERE $group.id = {$groupId} switch ($for) { case 'rel': $contactIdField = $query['casContactIdField']; - $entityName = $this->mapping->getEntityTable(); + $entityName = $this->getMappingTable(); $entityIdField = $query['casEntityIdField']; break; @@ -608,4 +608,8 @@ reminder.action_schedule_id = {$this->actionSchedule->id}"; return $this->mapping->resetOnTriggerDateChange($this->actionSchedule); } + protected function getMappingTable(): string { + return $this->mapping->getEntityTable($this->actionSchedule); + } + } -- 2.25.1