Merge pull request #4892 from colemanw/INFRA-132
[civicrm-core.git] / CRM / Core / Action.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 +--------------------------------------------------------------------+
26*/
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
06b69b18 34 * @copyright CiviCRM LLC (c) 2004-2014
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 *
6a488035 46 */
7da04cde 47 const
6a488035
TO
48 NONE = 0,
49 ADD = 1,
50 UPDATE = 2,
51 VIEW = 4,
52 DELETE = 8,
53 BROWSE = 16,
54 ENABLE = 32,
55 DISABLE = 64,
56 EXPORT = 128,
57 BASIC = 256,
58 ADVANCED = 512,
59 PREVIEW = 1024,
60 FOLLOWUP = 2048,
61 MAP = 4096,
62 PROFILE = 8192,
63 COPY = 16384,
64 RENEW = 32768,
65 DETACH = 65536,
66 REVERT = 131072,
2aa397bc
TO
67 CLOSE = 262144,
68 REOPEN = 524288,
6a488035
TO
69 MAX_ACTION = 1048575;
70
71 //make sure MAX_ACTION = 2^n - 1 ( n = total number of actions )
72
73 /**
100fef9d 74 * Map the action names to the relevant constant. We perform
6a488035
TO
75 * bit manipulation operations so we can perform multiple
76 * actions on the same object if needed
77 *
c490a46a 78 * @var array $_names type of variable name to action constant
6a488035 79 *
6a488035
TO
80 * @static
81 *
82 */
83 static $_names = array(
84 'add' => self::ADD,
85 'update' => self::UPDATE,
86 'view' => self::VIEW,
87 'delete' => self::DELETE,
88 'browse' => self::BROWSE,
89 'enable' => self::ENABLE,
90 'disable' => self::DISABLE,
91 'export' => self::EXPORT,
92 'preview' => self::PREVIEW,
93 'map' => self::MAP,
94 'copy' => self::COPY,
95 'profile' => self::PROFILE,
96 'renew' => self::RENEW,
97 'detach' => self::DETACH,
98 'revert' => self::REVERT,
99 'close' => self::CLOSE,
100 'reopen' => self::REOPEN,
101 );
102
103 /**
100fef9d 104 * The flipped version of the names array, initialized when used
6a488035
TO
105 *
106 * @var array
107 * @static
108 */
109 static $_description;
110
111 /**
100fef9d 112 * Called by the request object to translate a string into a mask
6a488035 113 *
6a0b768e
TO
114 * @param string $str
115 * The action to be resolved.
6a488035
TO
116 *
117 * @return int the action mask corresponding to the input string
6a488035 118 * @static
6a488035 119 */
00be9182 120 public static function resolve($str) {
6a488035
TO
121 $action = 0;
122 if ($str) {
123 $items = explode('|', $str);
124 $action = self::map($items);
125 }
126 return $action;
127 }
128
129 /**
130 * Given a string or an array of strings, determine the bitmask
131 * for this set of actions
132 *
6a0b768e
TO
133 * @param mixed $item
134 * Either a single string or an array of strings.
6a488035
TO
135 *
136 * @return int the action mask corresponding to the input args
6a488035
TO
137 * @static
138 *
139 */
00be9182 140 public static function map($item) {
6a488035
TO
141 $mask = 0;
142
143 if (is_array($item)) {
144 foreach ($item as $it) {
145 $mask |= self::mapItem($it);
146 }
147 return $mask;
148 }
149 else {
150 return self::mapItem($item);
151 }
152 }
153
154 /**
155 * Given a string determine the bitmask for this specific string
156 *
6a0b768e
TO
157 * @param string $item
158 * The input action to process.
6a488035
TO
159 *
160 * @return int the action mask corresponding to the input string
6a488035
TO
161 * @static
162 *
163 */
00be9182 164 public static function mapItem($item) {
6a488035
TO
165 $mask = CRM_Utils_Array::value(trim($item), self::$_names);
166 return $mask ? $mask : 0;
167 }
168
169 /**
170 *
171 * Given an action mask, find the corresponding description
172 *
6a0b768e
TO
173 * @param int $mask
174 * The action mask.
6a488035
TO
175 *
176 * @return string the corresponding action description
6a488035
TO
177 * @static
178 *
179 */
00be9182 180 public static function description($mask) {
6a488035
TO
181 if (!isset($_description)) {
182 self::$_description = array_flip(self::$_names);
183 }
184
185 return CRM_Utils_Array::value($mask, self::$_description, 'NO DESCRIPTION SET');
186 }
187
188 /**
100fef9d 189 * Given a set of links and a mask, return the html action string for
6a488035
TO
190 * the links associated with the mask
191 *
6a0b768e
TO
192 * @param array $links
193 * The set of link items.
194 * @param int $mask
195 * The mask to be used. a null mask means all items.
196 * @param array $values
197 * The array of values for parameter substitution in the link items.
198 * @param string $extraULName
199 * Enclosed extra links in this UL.
200 * @param bool $enclosedAllInSingleUL
201 * Force to enclosed all links in single UL.
da6b46f4
EM
202 *
203 * @param null $op
204 * @param null $objectName
100fef9d 205 * @param int $objectId
6a488035
TO
206 *
207 * @return string the html string
6a488035
TO
208 * @static
209 */
f9f40af3
TO
210 static function formLink(
211 $links,
6a488035
TO
212 $mask,
213 $values,
214 $extraULName = 'more',
215 $enclosedAllInSingleUL = FALSE,
216 $op = NULL,
217 $objectName = NULL,
218 $objectId = NULL
219 ) {
220 $config = CRM_Core_Config::singleton();
221 if (empty($links)) {
222 return NULL;
223 }
224
a9b15f31
AH
225 // make links indexed sequentially instead of by bitmask
226 // otherwise it's next to impossible to reliably add new ones
227 $seqLinks = array();
228 foreach ($links as $bit => $link) {
229 $link['bit'] = $bit;
230 $seqLinks[] = $link;
231 }
6a488035
TO
232
233 if ($op && $objectName && $objectId) {
a9b15f31 234 CRM_Utils_Hook::links($op, $objectName, $objectId, $seqLinks, $mask, $values);
6a488035
TO
235 }
236
237 $url = array();
238
a1c7d42f 239 foreach ($seqLinks as $i => $link) {
a9b15f31 240 if (!$mask || !array_key_exists('bit', $link) || ($mask & $link['bit'])) {
6a488035
TO
241 $extra = isset($link['extra']) ? self::replace($link['extra'], $values) : NULL;
242
243 $frontend = (isset($link['fe'])) ? TRUE : FALSE;
244
18d13c82 245 if (isset($link['qs']) && !CRM_Utils_System::isNull($link['qs'])) {
6a488035
TO
246 $urlPath = CRM_Utils_System::url(self::replace($link['url'], $values),
247 self::replace($link['qs'], $values), TRUE, NULL, TRUE, $frontend
248 );
249 }
250 else {
18d13c82 251 $urlPath = CRM_Utils_Array::value('url', $link, '#');
6a488035
TO
252 }
253
a1c7d42f 254 $classes = 'action-item crm-hover-button';
6a488035
TO
255 if (isset($link['ref'])) {
256 $classes .= ' ' . strtolower($link['ref']);
257 }
258
259 //get the user specified classes in.
260 if (isset($link['class'])) {
a1c7d42f 261 $className = is_array($link['class']) ? implode(' ', $link['class']) : $link['class'];
6a488035
TO
262 $classes .= ' ' . strtolower($className);
263 }
264
18d13c82
CW
265 if ($urlPath !== '#' && $frontend) {
266 $extra .= ' target="_blank"';
6a488035 267 }
a1c7d42f
CW
268 // Hack to make delete dialogs smaller
269 if (strpos($urlPath, '/delete') || strpos($urlPath, 'action=delete')) {
270 $classes .= " small-popup";
271 }
272 $url[] = sprintf('<a href="%s" class="%s" %s' . $extra . '>%s</a>',
18d13c82 273 $urlPath,
a1c7d42f
CW
274 $classes,
275 !empty($link['title']) ? "title='{$link['title']}' " : '',
18d13c82
CW
276 $link['name']
277 );
6a488035
TO
278 }
279 }
280
6a488035
TO
281 $mainLinks = $url;
282 if ($enclosedAllInSingleUL) {
283 $allLinks = '';
284 CRM_Utils_String::append($allLinks, '</li><li>', $mainLinks);
285 $allLinks = "{$extraULName}<ul class='panel'><li>{$allLinks}</li></ul>";
a1c7d42f 286 $result = "<span class='btn-slide crm-hover-button'>{$allLinks}</span>";
6a488035
TO
287 }
288 else {
289 $extra = '';
290 $extraLinks = array_splice($url, 2);
291 if (count($extraLinks) > 1) {
292 $mainLinks = array_slice($url, 0, 2);
293 CRM_Utils_String::append($extra, '</li><li>', $extraLinks);
294 $extra = "{$extraULName}<ul class='panel'><li>{$extra}</li></ul>";
295 }
296 $resultLinks = '';
297 CRM_Utils_String::append($resultLinks, '', $mainLinks);
298 if ($extra) {
a1c7d42f 299 $result = "<span>{$resultLinks}</span><span class='btn-slide crm-hover-button'>{$extra}</span>";
6a488035
TO
300 }
301 else {
302 $result = "<span>{$resultLinks}</span>";
303 }
304 }
305
306 return $result;
307 }
308
309 /**
100fef9d 310 * Given a string and an array of values, substitute the real values
6a488035
TO
311 * in the placeholder in the str in the CiviCRM format
312 *
6a0b768e
TO
313 * @param string $str
314 * The string to be replaced.
315 * @param array $values
316 * The array of values for parameter substitution in the str.
6a488035
TO
317 *
318 * @return string the substituted string
6a488035
TO
319 * @static
320 */
00be9182 321 public static function &replace(&$str, &$values) {
6a488035
TO
322 foreach ($values as $n => $v) {
323 $str = str_replace("%%$n%%", $v, $str);
324 }
325 return $str;
326 }
327
328 /**
100fef9d 329 * Get the mask for a permission (view, edit or null)
6a488035
TO
330 *
331 * @param string the permission
332 *
333 * @return int the mask for the above permission
334 * @static
6a488035 335 */
00be9182 336 public static function mask($permissions) {
6a488035
TO
337 $mask = NULL;
338 if (!is_array($permissions) || CRM_Utils_System::isNull($permissions)) {
339 return $mask;
340 }
341 //changed structure since we are handling delete separately - CRM-4418
342 if (in_array(CRM_Core_Permission::VIEW, $permissions)) {
343 $mask |= self::VIEW | self::EXPORT | self::BASIC | self::ADVANCED | self::BROWSE | self::MAP | self::PROFILE;
344 }
345 if (in_array(CRM_Core_Permission::DELETE, $permissions)) {
346 $mask |= self::DELETE;
347 }
348 if (in_array(CRM_Core_Permission::EDIT, $permissions)) {
349 //make sure we make self::MAX_ACTION = 2^n - 1
350 //if we add more actions; ( n = total number of actions )
351 $mask |= (self::MAX_ACTION & ~self::DELETE);
352 }
353
354 return $mask;
355 }
356}