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