From ab2f4d3702f1077c374b94df7db60188b2230b01 Mon Sep 17 00:00:00 2001 From: colemanw Date: Mon, 27 Nov 2023 09:37:22 -0500 Subject: [PATCH] APIv4 - Make Activity::getLinks return multiple add links per activity type --- CRM/Activity/Form/ActivityLinks.php | 75 ++------- Civi/API/Event/RespondEvent.php | 6 +- .../Service/Links/ActivityLinksProvider.php | 148 ++++++++++++++++++ 3 files changed, 162 insertions(+), 67 deletions(-) create mode 100644 Civi/Api4/Service/Links/ActivityLinksProvider.php diff --git a/CRM/Activity/Form/ActivityLinks.php b/CRM/Activity/Form/ActivityLinks.php index 238758614a..c8b351556a 100644 --- a/CRM/Activity/Form/ActivityLinks.php +++ b/CRM/Activity/Form/ActivityLinks.php @@ -28,73 +28,20 @@ class CRM_Activity_Form_ActivityLinks extends CRM_Core_Form { * @param self $self */ public static function commonBuildQuickForm($self) { - $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $self); - if (!$contactId) { - $contactId = CRM_Utils_Request::retrieve('cid', 'Positive'); - } - $urlParams = "action=add&reset=1&cid={$contactId}&selectedChild=activity&atype="; - - $allTypes = CRM_Utils_Array::value('values', civicrm_api3('OptionValue', 'get', [ - 'option_group_id' => 'activity_type', - 'is_active' => 1, - 'options' => ['limit' => 0, 'sort' => 'weight'], - ])); + $contactId = CRM_Utils_Request::retrieve('cid', 'Positive', $self) ?: CRM_Utils_Request::retrieve('cid', 'Positive'); $activityTypes = []; - foreach ($allTypes as $act) { - $url = 'civicrm/activity/add'; - if ($act['name'] === 'Email') { - if (!CRM_Utils_Mail::validOutBoundMail() || !$contactId) { - continue; - } - [, $email, $doNotEmail, , $isDeceased] = CRM_Contact_BAO_Contact::getContactDetails($contactId); - if (!$doNotEmail && $email && !$isDeceased) { - $url = 'civicrm/activity/email/add'; - $act['label'] = ts('Send an Email'); - } - else { - continue; - } - } - elseif ($act['name'] === 'SMS') { - if (!$contactId || !CRM_SMS_BAO_Provider::activeProviderCount() || !CRM_Core_Permission::check('send SMS')) { - continue; - } - // Check for existence of a mobile phone and ! do not SMS privacy setting - try { - $phone = civicrm_api3('Phone', 'getsingle', [ - 'contact_id' => $contactId, - 'phone_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'phone_type_id', 'Mobile'), - 'return' => ['phone', 'contact_id'], - 'options' => ['limit' => 1, 'sort' => "is_primary DESC"], - 'api.Contact.getsingle' => [ - 'id' => '$value.contact_id', - 'return' => 'do_not_sms', - ], - ]); - } - catch (CRM_Core_Exception $e) { - continue; - } - if (!$phone['api.Contact.getsingle']['do_not_sms'] && $phone['phone']) { - $url = 'civicrm/activity/sms/add'; - } - else { - continue; - } - } - elseif ($act['name'] == 'Print PDF Letter') { - $url = 'civicrm/activity/pdf/add'; - } - elseif (!empty($act['filter']) || (!empty($act['component_id']) && $act['component_id'] != '1')) { - continue; - } - $act['url'] = CRM_Utils_System::url($url, - "{$urlParams}{$act['value']}", FALSE, NULL, FALSE - ); - $act += ['icon' => 'fa-plus-square-o']; - $activityTypes[$act['value']] = $act; + $activityLinks = \Civi\Api4\Activity::getLinks() + ->addValue('target_contact_id', $contactId) + ->addWhere('ui_action', '=', 'add') + ->execute(); + foreach ($activityLinks as $activityLink) { + $activityTypes[] = [ + 'label' => $activityLink['text'], + 'icon' => $activityLink['icon'], + 'url' => CRM_Utils_System::url($activityLink['path'], NULL, FALSE, NULL, FALSE), + ]; } $self->assign('activityTypes', $activityTypes); diff --git a/Civi/API/Event/RespondEvent.php b/Civi/API/Event/RespondEvent.php index 462cc6edab..89ee03fbc2 100644 --- a/Civi/API/Event/RespondEvent.php +++ b/Civi/API/Event/RespondEvent.php @@ -21,7 +21,7 @@ namespace Civi\API\Event; */ class RespondEvent extends Event { /** - * @var mixed + * @var \Civi\Api4\Generic\Result|mixed */ private $response; @@ -30,7 +30,7 @@ class RespondEvent extends Event { * The API provider responsible for executing the request. * @param array $apiRequest * The full description of the API request. - * @param mixed $response + * @param \Civi\Api4\Generic\Result|mixed $response * The response to return to the client. * @param \Civi\API\Kernel $apiKernel * The kernel which fired the event. @@ -41,7 +41,7 @@ class RespondEvent extends Event { } /** - * @return mixed + * @return \Civi\Api4\Generic\Result|mixed */ public function getResponse() { return $this->response; diff --git a/Civi/Api4/Service/Links/ActivityLinksProvider.php b/Civi/Api4/Service/Links/ActivityLinksProvider.php new file mode 100644 index 0000000000..7b95e01d1c --- /dev/null +++ b/Civi/Api4/Service/Links/ActivityLinksProvider.php @@ -0,0 +1,148 @@ + 'alterActivityLinksResult', + ]; + } + + public static function alterActivityLinksResult(RespondEvent $e): void { + $request = $e->getApiRequest(); + if ($request['version'] == 4 && $request->getEntityName() === 'Activity' && is_a($request, '\Civi\Api4\Action\GetLinks')) { + $links = (array) $e->getResponse(); + $addLinkIndex = self::getActionIndex($links, 'add'); + $editLinkIndex = self::getActionIndex($links, 'update'); + $deleteLinkIndex = self::getActionIndex($links, 'delete'); + // Expand the "add" link to multiple activity types if it exists (otherwise the WHERE clause excluded it and we should too) + if ($request->getExpandMultiple() && isset($addLinkIndex)) { + // Expanding the "add" link requires a value for target_contact. + // This might come back from SearchKit in a couple different ways, + // either an implicit join on 'target_contact_id' or as an explicit join. + $targetContactId = $request->getValue('target_contact_id'); + foreach ($request->getValues() as $valueKey => $value) { + if (!$targetContactId && is_numeric($value) && preg_match('/^Activity_ActivityContact_Contact_\d\d\.id$/', $valueKey)) { + $targetContactId = $value; + } + } + if ($targetContactId) { + // Ensure links contain exactly the return values requested in the SELECT clause + $addLinks = self::getActivityTypeAddLinks($targetContactId, $request->getCheckPermissions()); + foreach ($addLinks as &$addLink) { + $addLink += $links[$addLinkIndex]; + $addLink = array_intersect_key($addLink, $links[$addLinkIndex]); + } + // Replace the one generic "add" link with multiple per-activity-type links + array_splice($links, $addLinkIndex, 1, $addLinks); + } + } + // With an activity type provided, alter path of edit links appropriately + $activityType = $request->getValue('activity_type_id:name'); + $activityId = $request->getValue('id'); + // Lookup activity type from id + if (!$activityType && $activityId) { + $activityTypeId = \CRM_Core_DAO::getFieldValue('CRM_Activity_DAO_Activity', $activityId, 'activity_type_id'); + $activityType = \CRM_Core_PseudoConstant::getName('CRM_Activity_DAO_Activity', 'activity_type_id', $activityTypeId); + } + if ($activityType) { + $viewOnlyTypes = \CRM_Activity_BAO_Activity::getViewOnlyActivityTypeIDs($request->getCheckPermissions()); + // Remove edit & delete links for "view only" types + if (isset($viewOnlyTypes[$activityType])) { + unset($links[$editLinkIndex], $links[$deleteLinkIndex]); + } + } + $e->getResponse()->exchangeArray(array_values($links)); + } + } + + private static function getActivityTypeAddLinks($contactId, $checkPermissions): array { + $addLinks = []; + $activityTypeQuery = OptionValue::get(FALSE) + ->addSelect('name', 'label', 'icon', 'value') + ->addWhere('option_group_id:name', '=', 'activity_type') + ->addWhere('is_active', '=', TRUE) + ->addWhere('filter', 'IS EMPTY') + ->addWhere('component_id', 'IS NULL') + ->addOrderBy('weight'); + + // TODO: Code block was moved from CRM_Activity_Form_ActivityLinks and could use further cleanup + $urlParams = "action=add&reset=1&cid={$contactId}&selectedChild=activity&atype="; + foreach ($activityTypeQuery->execute() as $act) { + $url = 'civicrm/activity/add'; + if ($act['name'] === 'Email') { + if (!\CRM_Utils_Mail::validOutBoundMail()) { + continue; + } + [, $email, $doNotEmail, , $isDeceased] = \CRM_Contact_BAO_Contact::getContactDetails($contactId); + if (!$doNotEmail && $email && !$isDeceased) { + $url = 'civicrm/activity/email/add'; + $act['label'] = ts('Send an Email'); + } + else { + continue; + } + } + elseif ($act['name'] === 'SMS') { + if (!\CRM_SMS_BAO_Provider::activeProviderCount() || + ($checkPermissions && !\CRM_Core_Permission::check('send SMS')) + ) { + continue; + } + // Check for existence of a mobile phone and ! do not SMS privacy setting + try { + $phone = civicrm_api3('Phone', 'getsingle', [ + 'contact_id' => $contactId, + 'phone_type_id' => \CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Phone', 'phone_type_id', 'Mobile'), + 'return' => ['phone', 'contact_id'], + 'options' => ['limit' => 1, 'sort' => "is_primary DESC"], + 'api.Contact.getsingle' => [ + 'id' => '$value.contact_id', + 'return' => 'do_not_sms', + ], + ]); + } + catch (\CRM_Core_Exception $e) { + continue; + } + if (!$phone['api.Contact.getsingle']['do_not_sms'] && $phone['phone']) { + $url = 'civicrm/activity/sms/add'; + } + else { + continue; + } + } + elseif ($act['name'] == 'Print PDF Letter') { + $url = 'civicrm/activity/pdf/add'; + } + + $act['icon'] = $act['icon'] ?? 'fa-plus-square-o'; + $act['path'] = "$url?$urlParams{$act['value']}"; + $act['text'] = $act['label']; + $addLinks[] = $act; + } + + return $addLinks; + } + +} -- 2.25.1