CRM-19821 unit test
[civicrm-core.git] / CRM / Core / Action.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
0f03f337 6 | Copyright CiviCRM LLC (c) 2004-2017 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 * The core concept of the system is an action performed on an object. Typically this will be a "data model" object
30 * as specified in the API specs. We attempt to keep the number and type of actions consistent
31 * and similar across all objects (thus providing both reuse and standards)
32 *
33 * @package CRM
0f03f337 34 * @copyright CiviCRM LLC (c) 2004-2017
6a488035
TO
35 * $Id$
36 *
37 */
38class CRM_Core_Action {
39
40 /**
41 * Different possible actions are defined here. Keep in sync with the
42 * constant from CRM_Core_Form for various modes.
43 *
cbdcc634 44 * @var integer const
6a488035 45 */
7da04cde 46 const
6a488035
TO
47 NONE = 0,
48 ADD = 1,
49 UPDATE = 2,
50 VIEW = 4,
51 DELETE = 8,
52 BROWSE = 16,
53 ENABLE = 32,
54 DISABLE = 64,
55 EXPORT = 128,
56 BASIC = 256,
57 ADVANCED = 512,
58 PREVIEW = 1024,
59 FOLLOWUP = 2048,
60 MAP = 4096,
61 PROFILE = 8192,
62 COPY = 16384,
63 RENEW = 32768,
64 DETACH = 65536,
65 REVERT = 131072,
353ffa53
TO
66 CLOSE = 262144,
67 REOPEN = 524288,
68 MAX_ACTION = 1048575;
6a488035
TO
69
70 //make sure MAX_ACTION = 2^n - 1 ( n = total number of actions )
71
72 /**
100fef9d 73 * Map the action names to the relevant constant. We perform
6a488035
TO
74 * bit manipulation operations so we can perform multiple
75 * actions on the same object if needed
76 *
353ffa53 77 * @var array $_names type of variable name to action constant
6a488035 78 *
6a488035
TO
79 */
80 static $_names = array(
81 'add' => self::ADD,
82 'update' => self::UPDATE,
83 'view' => self::VIEW,
84 'delete' => self::DELETE,
85 'browse' => self::BROWSE,
86 'enable' => self::ENABLE,
87 'disable' => self::DISABLE,
88 'export' => self::EXPORT,
89 'preview' => self::PREVIEW,
90 'map' => self::MAP,
91 'copy' => self::COPY,
92 'profile' => self::PROFILE,
93 'renew' => self::RENEW,
94 'detach' => self::DETACH,
95 'revert' => self::REVERT,
353ffa53
TO
96 'close' => self::CLOSE,
97 'reopen' => self::REOPEN,
6a488035
TO
98 );
99
100 /**
100fef9d 101 * The flipped version of the names array, initialized when used
6a488035
TO
102 *
103 * @var array
6a488035
TO
104 */
105 static $_description;
106
107 /**
eceb18cc 108 * Called by the request object to translate a string into a mask.
6a488035 109 *
6a0b768e
TO
110 * @param string $str
111 * The action to be resolved.
6a488035 112 *
a6c01b45
CW
113 * @return int
114 * the action mask corresponding to the input string
6a488035 115 */
00be9182 116 public static function resolve($str) {
6a488035
TO
117 $action = 0;
118 if ($str) {
119 $items = explode('|', $str);
120 $action = self::map($items);
121 }
122 return $action;
123 }
124
125 /**
126 * Given a string or an array of strings, determine the bitmask
127 * for this set of actions
128 *
6a0b768e
TO
129 * @param mixed $item
130 * Either a single string or an array of strings.
6a488035 131 *
a6c01b45
CW
132 * @return int
133 * the action mask corresponding to the input args
6a488035 134 */
00be9182 135 public static function map($item) {
6a488035
TO
136 $mask = 0;
137
138 if (is_array($item)) {
139 foreach ($item as $it) {
140 $mask |= self::mapItem($it);
141 }
142 return $mask;
143 }
144 else {
145 return self::mapItem($item);
146 }
147 }
148
149 /**
eceb18cc 150 * Given a string determine the bitmask for this specific string.
6a488035 151 *
6a0b768e
TO
152 * @param string $item
153 * The input action to process.
6a488035 154 *
a6c01b45
CW
155 * @return int
156 * the action mask corresponding to the input string
6a488035 157 */
00be9182 158 public static function mapItem($item) {
6a488035
TO
159 $mask = CRM_Utils_Array::value(trim($item), self::$_names);
160 return $mask ? $mask : 0;
161 }
162
163 /**
164 *
165 * Given an action mask, find the corresponding description
166 *
6a0b768e
TO
167 * @param int $mask
168 * The action mask.
6a488035 169 *
a6c01b45
CW
170 * @return string
171 * the corresponding action description
6a488035 172 */
00be9182 173 public static function description($mask) {
0e02cb01 174 if (!isset(self::$_description)) {
6a488035
TO
175 self::$_description = array_flip(self::$_names);
176 }
177
178 return CRM_Utils_Array::value($mask, self::$_description, 'NO DESCRIPTION SET');
179 }
180
181 /**
100fef9d 182 * Given a set of links and a mask, return the html action string for
6a488035
TO
183 * the links associated with the mask
184 *
6a0b768e
TO
185 * @param array $links
186 * The set of link items.
187 * @param int $mask
188 * The mask to be used. a null mask means all items.
189 * @param array $values
190 * The array of values for parameter substitution in the link items.
191 * @param string $extraULName
192 * Enclosed extra links in this UL.
193 * @param bool $enclosedAllInSingleUL
194 * Force to enclosed all links in single UL.
da6b46f4
EM
195 *
196 * @param null $op
197 * @param null $objectName
100fef9d 198 * @param int $objectId
6a488035 199 *
a6c01b45
CW
200 * @return string
201 * the html string
6a488035 202 */
d5cc0fc2 203 public static function formLink(
f9f40af3 204 $links,
6a488035
TO
205 $mask,
206 $values,
207 $extraULName = 'more',
208 $enclosedAllInSingleUL = FALSE,
209 $op = NULL,
210 $objectName = NULL,
211 $objectId = NULL
212 ) {
6a488035
TO
213 if (empty($links)) {
214 return NULL;
215 }
216
a9b15f31
AH
217 // make links indexed sequentially instead of by bitmask
218 // otherwise it's next to impossible to reliably add new ones
219 $seqLinks = array();
220 foreach ($links as $bit => $link) {
221 $link['bit'] = $bit;
222 $seqLinks[] = $link;
223 }
6a488035
TO
224
225 if ($op && $objectName && $objectId) {
a9b15f31 226 CRM_Utils_Hook::links($op, $objectName, $objectId, $seqLinks, $mask, $values);
6a488035
TO
227 }
228
229 $url = array();
230
a1c7d42f 231 foreach ($seqLinks as $i => $link) {
a9b15f31 232 if (!$mask || !array_key_exists('bit', $link) || ($mask & $link['bit'])) {
6a488035
TO
233 $extra = isset($link['extra']) ? self::replace($link['extra'], $values) : NULL;
234
235 $frontend = (isset($link['fe'])) ? TRUE : FALSE;
236
18d13c82 237 if (isset($link['qs']) && !CRM_Utils_System::isNull($link['qs'])) {
6a488035
TO
238 $urlPath = CRM_Utils_System::url(self::replace($link['url'], $values),
239 self::replace($link['qs'], $values), TRUE, NULL, TRUE, $frontend
240 );
241 }
242 else {
18d13c82 243 $urlPath = CRM_Utils_Array::value('url', $link, '#');
6a488035
TO
244 }
245
a1c7d42f 246 $classes = 'action-item crm-hover-button';
6a488035
TO
247 if (isset($link['ref'])) {
248 $classes .= ' ' . strtolower($link['ref']);
249 }
250
251 //get the user specified classes in.
252 if (isset($link['class'])) {
a1c7d42f 253 $className = is_array($link['class']) ? implode(' ', $link['class']) : $link['class'];
6a488035
TO
254 $classes .= ' ' . strtolower($className);
255 }
256
18d13c82
CW
257 if ($urlPath !== '#' && $frontend) {
258 $extra .= ' target="_blank"';
6a488035 259 }
a1c7d42f
CW
260 // Hack to make delete dialogs smaller
261 if (strpos($urlPath, '/delete') || strpos($urlPath, 'action=delete')) {
262 $classes .= " small-popup";
263 }
264 $url[] = sprintf('<a href="%s" class="%s" %s' . $extra . '>%s</a>',
18d13c82 265 $urlPath,
a1c7d42f
CW
266 $classes,
267 !empty($link['title']) ? "title='{$link['title']}' " : '',
18d13c82
CW
268 $link['name']
269 );
6a488035
TO
270 }
271 }
272
6a488035
TO
273 $mainLinks = $url;
274 if ($enclosedAllInSingleUL) {
275 $allLinks = '';
276 CRM_Utils_String::append($allLinks, '</li><li>', $mainLinks);
277 $allLinks = "{$extraULName}<ul class='panel'><li>{$allLinks}</li></ul>";
a1c7d42f 278 $result = "<span class='btn-slide crm-hover-button'>{$allLinks}</span>";
6a488035
TO
279 }
280 else {
281 $extra = '';
282 $extraLinks = array_splice($url, 2);
283 if (count($extraLinks) > 1) {
284 $mainLinks = array_slice($url, 0, 2);
285 CRM_Utils_String::append($extra, '</li><li>', $extraLinks);
286 $extra = "{$extraULName}<ul class='panel'><li>{$extra}</li></ul>";
287 }
288 $resultLinks = '';
289 CRM_Utils_String::append($resultLinks, '', $mainLinks);
290 if ($extra) {
a1c7d42f 291 $result = "<span>{$resultLinks}</span><span class='btn-slide crm-hover-button'>{$extra}</span>";
6a488035
TO
292 }
293 else {
294 $result = "<span>{$resultLinks}</span>";
295 }
296 }
297
298 return $result;
299 }
300
301 /**
100fef9d 302 * Given a string and an array of values, substitute the real values
6a488035
TO
303 * in the placeholder in the str in the CiviCRM format
304 *
6a0b768e
TO
305 * @param string $str
306 * The string to be replaced.
307 * @param array $values
308 * The array of values for parameter substitution in the str.
6a488035 309 *
a6c01b45
CW
310 * @return string
311 * the substituted string
6a488035 312 */
00be9182 313 public static function &replace(&$str, &$values) {
6a488035
TO
314 foreach ($values as $n => $v) {
315 $str = str_replace("%%$n%%", $v, $str);
316 }
317 return $str;
318 }
319
320 /**
100fef9d 321 * Get the mask for a permission (view, edit or null)
6a488035 322 *
ad37ac8e 323 * @param array $permissions
324 *
a6c01b45 325 * @return int
ad37ac8e 326 * The mask for the above permission
6a488035 327 */
00be9182 328 public static function mask($permissions) {
6a488035
TO
329 $mask = NULL;
330 if (!is_array($permissions) || CRM_Utils_System::isNull($permissions)) {
331 return $mask;
332 }
333 //changed structure since we are handling delete separately - CRM-4418
334 if (in_array(CRM_Core_Permission::VIEW, $permissions)) {
335 $mask |= self::VIEW | self::EXPORT | self::BASIC | self::ADVANCED | self::BROWSE | self::MAP | self::PROFILE;
336 }
337 if (in_array(CRM_Core_Permission::DELETE, $permissions)) {
338 $mask |= self::DELETE;
339 }
340 if (in_array(CRM_Core_Permission::EDIT, $permissions)) {
341 //make sure we make self::MAX_ACTION = 2^n - 1
342 //if we add more actions; ( n = total number of actions )
343 $mask |= (self::MAX_ACTION & ~self::DELETE);
344 }
345
346 return $mask;
347 }
96025800 348
6a488035 349}