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