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 +--------------------------------------------------------------------+
13 * The core concept of the system is an action performed on an object. Typically this will be a "data model" object
14 * as specified in the API specs. We attempt to keep the number and type of actions consistent
15 * and similar across all objects (thus providing both reuse and standards)
18 * @copyright CiviCRM LLC https://civicrm.org/licensing
20 class CRM_Core_Action
{
23 * Different possible actions are defined here. Keep in sync with the
24 * constant from CRM_Core_Form for various modes.
52 //make sure MAX_ACTION = 2^n - 1 ( n = total number of actions )
55 * Map the action names to the relevant constant. We perform
56 * bit manipulation operations so we can perform multiple
57 * actions on the same object if needed
62 public static $_names = [
64 'update' => self
::UPDATE
,
66 'delete' => self
::DELETE
,
67 'browse' => self
::BROWSE
,
68 'enable' => self
::ENABLE
,
69 'disable' => self
::DISABLE
,
70 'export' => self
::EXPORT
,
71 'preview' => self
::PREVIEW
,
74 'profile' => self
::PROFILE
,
75 'renew' => self
::RENEW
,
76 'detach' => self
::DETACH
,
77 'revert' => self
::REVERT
,
78 'close' => self
::CLOSE
,
79 'reopen' => self
::REOPEN
,
83 * The flipped version of the names array, initialized when used
87 public static $_description;
90 * Called by the request object to translate a string into a mask.
93 * The action to be resolved.
96 * the action mask corresponding to the input string
98 public static function resolve($str) {
101 $items = explode('|', $str);
102 $action = self
::map($items);
108 * Given a string or an array of strings, determine the bitmask
109 * for this set of actions
112 * Either a single string or an array of strings.
115 * the action mask corresponding to the input args
117 public static function map($item) {
120 if (is_array($item)) {
121 foreach ($item as $it) {
122 $mask |
= self
::mapItem($it);
127 return self
::mapItem($item);
132 * Given a string determine the bitmask for this specific string.
134 * @param string $item
135 * The input action to process.
138 * the action mask corresponding to the input string
140 public static function mapItem($item) {
141 $mask = self
::$_names[trim($item)] ??
NULL;
142 return $mask ?
$mask : 0;
147 * Given an action mask, find the corresponding description
153 * the corresponding action description
155 public static function description($mask) {
156 if (!isset(self
::$_description)) {
157 self
::$_description = array_flip(self
::$_names);
160 return CRM_Utils_Array
::value($mask, self
::$_description, 'NO DESCRIPTION SET');
164 * Given a set of links and a mask, return the html action string for
165 * the links associated with the mask
167 * @param array $links
168 * The set of link items.
169 * @param int|null $mask
170 * The mask to be used. a null mask means all items.
171 * @param array $values
172 * The array of values for parameter substitution in the link items.
173 * @param string $extraULName
174 * Enclosed extra links in this UL.
175 * @param bool $enclosedAllInSingleUL
176 * Force to enclosed all links in single UL.
179 * @param null $objectName
180 * @param int $objectId
181 * @param string $iconMode
182 * - `text`: even if `icon` is set for a link, display the `name`
183 * - `icon`: display only the `icon` for each link if it's available, and
184 * don't tuck anything under "more >"
185 * - `both`: if `icon` is available, display it next to the `name` for each
191 public static function formLink(
195 $extraULName = 'more',
196 $enclosedAllInSingleUL = FALSE,
206 // make links indexed sequentially instead of by bitmask
207 // otherwise it's next to impossible to reliably add new ones
209 foreach ($links as $bit => $link) {
214 if ($op && $objectName && $objectId) {
215 CRM_Utils_Hook
::links($op, $objectName, $objectId, $seqLinks, $mask, $values);
220 foreach ($seqLinks as $i => $link) {
221 if (!$mask ||
!array_key_exists('bit', $link) ||
($mask & $link['bit'])) {
222 $extra = isset($link['extra']) ? self
::replace($link['extra'], $values) : NULL;
224 $frontend = isset($link['fe']);
226 if (isset($link['qs']) && !CRM_Utils_System
::isNull($link['qs'])) {
227 $urlPath = CRM_Utils_System
::url(self
::replace($link['url'], $values),
228 self
::replace($link['qs'], $values), FALSE, NULL, TRUE, $frontend
232 $urlPath = CRM_Utils_Array
::value('url', $link, '#');
235 $classes = 'action-item crm-hover-button';
236 if (isset($link['ref'])) {
237 $classes .= ' ' . strtolower($link['ref']);
240 //get the user specified classes in.
241 if (isset($link['class'])) {
242 $className = is_array($link['class']) ?
implode(' ', $link['class']) : $link['class'];
243 $classes .= ' ' . strtolower($className);
246 if ($urlPath !== '#' && $frontend) {
247 $extra .= ' target="_blank"';
249 // Hack to make delete dialogs smaller
250 if (strpos($urlPath, '/delete') ||
strpos($urlPath, 'action=delete')) {
251 $classes .= " small-popup";
254 $linkContent = $link['name'];
255 if (!empty($link['icon'])) {
256 if ($iconMode == 'icon') {
257 $linkContent = CRM_Core_Page
::crmIcon($link['icon'], $link['name'], TRUE, ['title' => '']);
259 elseif ($iconMode == 'both') {
260 $linkContent = CRM_Core_Page
::crmIcon($link['icon']) . ' ' . $linkContent;
264 $url[] = sprintf('<a href="%s" class="%s" %s' . $extra . '>%s</a>',
267 !empty($link['title']) ?
"title='{$link['title']}' " : '',
274 if ($enclosedAllInSingleUL) {
276 CRM_Utils_String
::append($allLinks, '</li><li>', $mainLinks);
277 $allLinks = "{$extraULName}<ul class='panel'><li>{$allLinks}</li></ul>";
278 $result = "<span class='btn-slide crm-hover-button'>{$allLinks}</span>";
282 if ($iconMode != 'icon') {
283 $extraLinks = array_splice($url, 2);
284 if (count($extraLinks) > 1) {
285 $mainLinks = array_slice($url, 0, 2);
286 CRM_Utils_String
::append($extra, '</li><li>', $extraLinks);
287 $extra = "{$extraULName}<ul class='panel'><li>{$extra}</li></ul>";
291 CRM_Utils_String
::append($resultLinks, '', $mainLinks);
293 $result = "<span>{$resultLinks}</span><span class='btn-slide crm-hover-button'>{$extra}</span>";
296 $result = "<span>{$resultLinks}</span>";
304 * Given a set of links and a mask, return a filtered (by mask) array containing the final links with parsed values
305 * and calling hooks as appropriate.
306 * Use this when passing a set of action links to the API or to the form without adding html formatting.
308 * @param array $links
309 * The set of link items.
311 * The mask to be used. a null mask means all items.
312 * @param array $values
313 * The array of values for parameter substitution in the link items.
314 * @param string|null $op
315 * @param string|null $objectName
316 * @param int $objectId
319 * The array describing each link
321 public static function filterLinks(
333 // make links indexed sequentially instead of by bitmask
334 // otherwise it's next to impossible to reliably add new ones
336 foreach ($links as $bit => $link) {
341 if ($op && $objectName && $objectId) {
342 CRM_Utils_Hook
::links($op, $objectName, $objectId, $seqLinks, $mask, $values);
345 foreach ($seqLinks as $i => $link) {
346 if (!$mask ||
!array_key_exists('bit', $link) ||
($mask & $link['bit'])) {
347 $seqLinks[$i]['extra'] = isset($link['extra']) ? self
::replace($link['extra'], $values) : NULL;
349 if (isset($link['qs']) && !CRM_Utils_System
::isNull($link['qs'])) {
350 $seqLinks[$i]['url'] = self
::replace($link['url'], $values);
351 $seqLinks[$i]['qs'] = self
::replace($link['qs'], $values);
355 unset($seqLinks[$i]);
363 * Given a string and an array of values, substitute the real values
364 * in the placeholder in the str in the CiviCRM format
367 * The string to be replaced.
368 * @param array $values
369 * The array of values for parameter substitution in the str.
372 * the substituted string
374 public static function &replace(&$str, &$values) {
375 foreach ($values as $n => $v) {
376 $str = str_replace("%%$n%%", $v, $str);
382 * Get the mask for a permission (view, edit or null)
384 * @param array $permissions
387 * The mask for the above permission
389 public static function mask($permissions) {
391 if (!is_array($permissions) || CRM_Utils_System
::isNull($permissions)) {
394 //changed structure since we are handling delete separately - CRM-4418
395 if (in_array(CRM_Core_Permission
::VIEW
, $permissions)) {
396 $mask |
= self
::VIEW | self
::EXPORT | self
::BASIC | self
::ADVANCED | self
::BROWSE | self
::MAP | self
::PROFILE
;
398 if (in_array(CRM_Core_Permission
::DELETE
, $permissions)) {
399 $mask |
= self
::DELETE
;
401 if (in_array(CRM_Core_Permission
::EDIT
, $permissions)) {
402 //make sure we make self::MAX_ACTION = 2^n - 1
403 //if we add more actions; ( n = total number of actions )
404 $mask |
= (self
::MAX_ACTION
& ~self
::DELETE
);