3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
14 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 use Civi\Api4\Utils\CoreUtil
;
20 * Recent items utility class.
22 class CRM_Utils_Recent
{
29 const STORE_NAME
= 'CRM_Utils_Recent';
32 * Max number of recent items to store
39 * The list of recently viewed items.
43 static private $_recent = NULL;
49 static private $_maxItems = 10;
52 * Initialize this class and set the static variables.
54 public static function initialize() {
55 $maxItemsSetting = Civi
::settings()->get('recentItemsMaxCount');
56 if (isset($maxItemsSetting) && $maxItemsSetting > 0 && $maxItemsSetting < self
::MAX_ITEMS
) {
57 self
::$_maxItems = $maxItemsSetting;
59 if (!self
::$_recent) {
60 $session = CRM_Core_Session
::singleton();
61 self
::$_recent = $session->get(self
::STORE_NAME
);
62 if (!self
::$_recent) {
69 * Return the recently viewed array.
72 * the recently viewed array
74 public static function &get() {
76 return self
::$_recent;
80 * Create function used by the API - supplies defaults
82 * @param array $params
83 * @param Civi\Api4\Generic\AbstractAction $action
85 public static function create(array $params, Civi\Api4\Generic\AbstractAction
$action) {
86 if ($action->getCheckPermissions()) {
87 $allowed = civicrm_api4($params['entity_type'], 'checkAccess', [
89 'values' => ['id' => $params['entity_id']],
91 if (empty($allowed['access'])) {
95 $params['title'] = $params['title'] ?? self
::getTitle($params['entity_type'], $params['entity_id']);
96 $params['view_url'] = $params['view_url'] ?? self
::getUrl($params['entity_type'], $params['entity_id'], 'view');
97 $params['edit_url'] = $params['edit_url'] ?? self
::getUrl($params['entity_type'], $params['entity_id'], 'update');
98 $params['delete_url'] = $params['delete_url'] ??
(empty($params['is_deleted']) ? self
::getUrl($params['entity_type'], $params['entity_id'], 'delete') : NULL);
99 self
::add($params['title'], $params['view_url'], $params['entity_id'], $params['entity_type'], $params['contact_id'] ??
NULL, NULL, $params);
104 * Add an item to the recent stack.
106 * @param string $title
107 * The title to display.
109 * The link for the above title.
110 * @param string $entityId
112 * @param string $entityType
113 * @param int $contactId
114 * Deprecated, probably unused param
115 * @param string $contactName
116 * Deprecated, probably unused param
117 * @param array $others
119 public static function add(
128 $entityType = self
::normalizeEntityType($entityType);
130 // Abort if this entity type is not supported
131 if (!self
::isProviderEnabled($entityType)) {
135 // Ensure item is not already present in list
136 self
::removeItems(['entity_id' => $entityId, 'entity_type' => $entityType]);
138 if (!is_array($others)) {
142 array_unshift(self
::$_recent,
145 // TODO: deprecate & remove "url" in favor of "view_url"
148 // TODO: deprecate & remove "id" in favor of "entity_id"
150 'entity_id' => (int) $entityId,
151 // TODO: deprecate & remove "type" in favor of "entity_type"
152 'type' => $entityType,
153 'entity_type' => $entityType,
155 'contact_id' => $contactId,
156 // Param appears to be unused
157 'contactName' => $contactName,
158 'subtype' => $others['subtype'] ??
NULL,
159 // TODO: deprecate & remove "isDeleted" in favor of "is_deleted"
160 'isDeleted' => $others['is_deleted'] ??
$others['isDeleted'] ??
FALSE,
161 'is_deleted' => (bool) ($others['is_deleted'] ??
$others['isDeleted'] ??
FALSE),
162 // imageUrl is deprecated
163 'image_url' => $others['imageUrl'] ??
NULL,
164 'edit_url' => $others['edit_url'] ??
$others['editUrl'] ??
NULL,
165 'delete_url' => $others['delete_url'] ??
$others['deleteUrl'] ??
NULL,
166 'icon' => $others['icon'] ?? self
::getIcon($entityType, $entityId),
170 // Keep the list trimmed to max length
171 while (count(self
::$_recent) > self
::$_maxItems) {
172 array_pop(self
::$_recent);
175 CRM_Utils_Hook
::recent(self
::$_recent);
177 $session = CRM_Core_Session
::singleton();
178 $session->set(self
::STORE_NAME
, self
::$_recent);
182 * Get default title for this item, based on the entity's `label_field`
184 * @param string $entityType
185 * @param int $entityId
186 * @return string|null
188 private static function getTitle($entityType, $entityId) {
189 $labelField = CoreUtil
::getInfoItem($entityType, 'label_field');
192 $record = civicrm_api4($entityType, 'get', [
193 'where' => [['id', '=', $entityId]],
194 'select' => [$labelField],
195 'checkPermissions' => FALSE,
197 $title = $record[$labelField] ??
NULL;
199 return $title ??
(CoreUtil
::getInfoItem($entityType, 'title'));
203 * Get a link to view/update/delete a given entity.
205 * @param string $entityType
206 * @param int $entityId
207 * @param string $action
208 * Either 'view', 'update', or 'delete'
209 * @return string|null
211 private static function getUrl($entityType, $entityId, $action) {
212 if ($action !== 'view') {
213 $check = civicrm_api4($entityType, 'checkAccess', [
215 'values' => ['id' => $entityId],
217 if (empty($check['access'])) {
221 $paths = (array) CoreUtil
::getInfoItem($entityType, 'paths');
222 if (!empty($paths[$action])) {
223 return CRM_Utils_System
::url(str_replace('[id]', $entityId, $paths[$action]));
231 * @return string|null
233 private static function getIcon($entityType, $entityId) {
235 $daoClass = CRM_Core_DAO_AllCoreTables
::getFullName($entityType);
237 $icon = CRM_Core_DAO_AllCoreTables
::getBAOClassName($daoClass)::getEntityIcon($entityType, $entityId);
239 return $icon ?
: 'fa-gear';
243 * Callback for hook_civicrm_post().
244 * @param \Civi\Core\Event\PostEvent $event
246 public static function on_hook_civicrm_post(\Civi\Core\Event\PostEvent
$event) {
247 if ($event->id
&& CRM_Core_Session
::getLoggedInContactID()) {
248 $entityType = self
::normalizeEntityType($event->entity
);
249 if ($event->action
=== 'delete') {
250 // Is this an entity that might be in the recent items list?
251 $providersPermitted = Civi
::settings()->get('recentItemsProviders') ?
: array_keys(self
::getProviders());
252 if (in_array($entityType, $providersPermitted)) {
253 self
::del(['entity_id' => $event->id
, 'entity_type' => $entityType]);
256 elseif ($event->action
=== 'edit') {
257 if (isset($event->object->is_deleted
)) {
258 \Civi\Api4\RecentItem
::update(FALSE)
259 ->addWhere('entity_type', '=', $entityType)
260 ->addWhere('entity_id', '=', $event->id
)
261 ->addValue('is_deleted', (bool) $event->object->is_deleted
)
269 * Remove items from the array that match given props
270 * @param array $props
272 private static function removeItems(array $props) {
275 self
::$_recent = array_filter(self
::$_recent, function($item) use ($props) {
276 foreach ($props as $key => $val) {
277 if (($item[$key] ??
NULL) != $val) {
286 * Delete item(s) from the recently-viewed list.
288 * @param array $removeItem
289 * Item to be removed.
291 public static function del($removeItem) {
292 self
::removeItems($removeItem);
293 CRM_Utils_Hook
::recent(self
::$_recent);
294 $session = CRM_Core_Session
::singleton();
295 $session->set(self
::STORE_NAME
, self
::$_recent);
299 * Delete an item from the recent stack.
304 public static function delContact($id) {
305 CRM_Core_Error
::deprecatedFunctionWarning('del');
306 self
::del(['contact_id' => $id]);
310 * Check if a provider is allowed to add stuff.
311 * If corresponding setting is empty, all are allowed
313 * @param string $providerName
316 public static function isProviderEnabled($providerName) {
319 // Use core setting recentItemsProviders if configured
320 $providersPermitted = Civi
::settings()->get('recentItemsProviders');
321 if ($providersPermitted) {
322 $allowed = in_array($providerName, $providersPermitted);
329 * @param string $entityType
332 private static function normalizeEntityType($entityType) {
333 // Change Individual/Organization/Household to 'Contact'
334 if (in_array($entityType, CRM_Contact_BAO_ContactType
::basicTypes(TRUE), TRUE)) {
341 * Gets the list of available providers to civi's recent items stack
343 * TODO: Make this an option group so extensions can extend it.
347 public static function getProviders() {
349 'Contact' => ts('Contacts'),
350 'Relationship' => ts('Relationships'),
351 'Activity' => ts('Activities'),
352 'Note' => ts('Notes'),
353 'Group' => ts('Groups'),
354 'Case' => ts('Cases'),
355 'Contribution' => ts('Contributions'),
356 'Participant' => ts('Participants'),
357 'Grant' => ts('Grants'),
358 'Membership' => ts('Memberships'),
359 'Pledge' => ts('Pledges'),
360 'Event' => ts('Events'),
361 'Campaign' => ts('Campaigns'),