Merge pull request #4897 from totten/master-cleanup2
[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 const
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,
66 CLOSE = 262144,
67 REOPEN = 524288,
68 MAX_ACTION = 1048575;
69
70 //make sure MAX_ACTION = 2^n - 1 ( n = total number of actions )
71
72 /**
73 * Map the action names to the relevant constant. We perform
74 * bit manipulation operations so we can perform multiple
75 * actions on the same object if needed
76 *
77 * @var array $_names type of variable name to action constant
78 *
79 * @static
80 */
81 static $_names = array(
82 'add' => self::ADD,
83 'update' => self::UPDATE,
84 'view' => self::VIEW,
85 'delete' => self::DELETE,
86 'browse' => self::BROWSE,
87 'enable' => self::ENABLE,
88 'disable' => self::DISABLE,
89 'export' => self::EXPORT,
90 'preview' => self::PREVIEW,
91 'map' => self::MAP,
92 'copy' => self::COPY,
93 'profile' => self::PROFILE,
94 'renew' => self::RENEW,
95 'detach' => self::DETACH,
96 'revert' => self::REVERT,
97 'close' => self::CLOSE,
98 'reopen' => self::REOPEN,
99 );
100
101 /**
102 * The flipped version of the names array, initialized when used
103 *
104 * @var array
105 * @static
106 */
107 static $_description;
108
109 /**
110 * Called by the request object to translate a string into a mask
111 *
112 * @param string $str
113 * The action to be resolved.
114 *
115 * @return int
116 * 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
133 * Either a single string or an array of strings.
134 *
135 * @return int
136 * the action mask corresponding to the input args
137 * @static
138 */
139 public static function map($item) {
140 $mask = 0;
141
142 if (is_array($item)) {
143 foreach ($item as $it) {
144 $mask |= self::mapItem($it);
145 }
146 return $mask;
147 }
148 else {
149 return self::mapItem($item);
150 }
151 }
152
153 /**
154 * Given a string determine the bitmask for this specific string
155 *
156 * @param string $item
157 * The input action to process.
158 *
159 * @return int
160 * the action mask corresponding to the input string
161 * @static
162 */
163 public static function mapItem($item) {
164 $mask = CRM_Utils_Array::value(trim($item), self::$_names);
165 return $mask ? $mask : 0;
166 }
167
168 /**
169 *
170 * Given an action mask, find the corresponding description
171 *
172 * @param int $mask
173 * The action mask.
174 *
175 * @return string
176 * the corresponding action description
177 * @static
178 */
179 public static function description($mask) {
180 if (!isset($_description)) {
181 self::$_description = array_flip(self::$_names);
182 }
183
184 return CRM_Utils_Array::value($mask, self::$_description, 'NO DESCRIPTION SET');
185 }
186
187 /**
188 * Given a set of links and a mask, return the html action string for
189 * the links associated with the mask
190 *
191 * @param array $links
192 * The set of link items.
193 * @param int $mask
194 * The mask to be used. a null mask means all items.
195 * @param array $values
196 * The array of values for parameter substitution in the link items.
197 * @param string $extraULName
198 * Enclosed extra links in this UL.
199 * @param bool $enclosedAllInSingleUL
200 * Force to enclosed all links in single UL.
201 *
202 * @param null $op
203 * @param null $objectName
204 * @param int $objectId
205 *
206 * @return string
207 * the html string
208 * @static
209 */
210 static function formLink(
211 $links,
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
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 }
232
233 if ($op && $objectName && $objectId) {
234 CRM_Utils_Hook::links($op, $objectName, $objectId, $seqLinks, $mask, $values);
235 }
236
237 $url = array();
238
239 foreach ($seqLinks as $i => $link) {
240 if (!$mask || !array_key_exists('bit', $link) || ($mask & $link['bit'])) {
241 $extra = isset($link['extra']) ? self::replace($link['extra'], $values) : NULL;
242
243 $frontend = (isset($link['fe'])) ? TRUE : FALSE;
244
245 if (isset($link['qs']) && !CRM_Utils_System::isNull($link['qs'])) {
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 {
251 $urlPath = CRM_Utils_Array::value('url', $link, '#');
252 }
253
254 $classes = 'action-item crm-hover-button';
255 if (isset($link['ref'])) {
256 $classes .= ' ' . strtolower($link['ref']);
257 }
258
259 //get the user specified classes in.
260 if (isset($link['class'])) {
261 $className = is_array($link['class']) ? implode(' ', $link['class']) : $link['class'];
262 $classes .= ' ' . strtolower($className);
263 }
264
265 if ($urlPath !== '#' && $frontend) {
266 $extra .= ' target="_blank"';
267 }
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>',
273 $urlPath,
274 $classes,
275 !empty($link['title']) ? "title='{$link['title']}' " : '',
276 $link['name']
277 );
278 }
279 }
280
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>";
286 $result = "<span class='btn-slide crm-hover-button'>{$allLinks}</span>";
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) {
299 $result = "<span>{$resultLinks}</span><span class='btn-slide crm-hover-button'>{$extra}</span>";
300 }
301 else {
302 $result = "<span>{$resultLinks}</span>";
303 }
304 }
305
306 return $result;
307 }
308
309 /**
310 * Given a string and an array of values, substitute the real values
311 * in the placeholder in the str in the CiviCRM format
312 *
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.
317 *
318 * @return string
319 * the substituted string
320 * @static
321 */
322 public static function &replace(&$str, &$values) {
323 foreach ($values as $n => $v) {
324 $str = str_replace("%%$n%%", $v, $str);
325 }
326 return $str;
327 }
328
329 /**
330 * Get the mask for a permission (view, edit or null)
331 *
332 * @param string the permission
333 *
334 * @return int
335 * the mask for the above permission
336 * @static
337 */
338 public static function mask($permissions) {
339 $mask = NULL;
340 if (!is_array($permissions) || CRM_Utils_System::isNull($permissions)) {
341 return $mask;
342 }
343 //changed structure since we are handling delete separately - CRM-4418
344 if (in_array(CRM_Core_Permission::VIEW, $permissions)) {
345 $mask |= self::VIEW | self::EXPORT | self::BASIC | self::ADVANCED | self::BROWSE | self::MAP | self::PROFILE;
346 }
347 if (in_array(CRM_Core_Permission::DELETE, $permissions)) {
348 $mask |= self::DELETE;
349 }
350 if (in_array(CRM_Core_Permission::EDIT, $permissions)) {
351 //make sure we make self::MAX_ACTION = 2^n - 1
352 //if we add more actions; ( n = total number of actions )
353 $mask |= (self::MAX_ACTION & ~self::DELETE);
354 }
355
356 return $mask;
357 }
358 }