comparison sites/all/modules/ctools/ctools.module @ 0:ff03f76ab3fe

initial version
author danieleb <danielebarchiesi@me.com>
date Wed, 21 Aug 2013 18:51:11 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:ff03f76ab3fe
1 <?php
2
3 /**
4 * @file
5 * CTools primary module file.
6 *
7 * Most of the CTools tools are in their own .inc files. This contains
8 * nothing more than a few convenience functions and some hooks that
9 * must be implemented in the module file.
10 */
11
12 define('CTOOLS_API_VERSION', '2.0.7');
13
14 /**
15 * Test the CTools API version.
16 *
17 * This function can always be used to safely test if CTools has the minimum
18 * API version that your module can use. It can also try to protect you from
19 * running if the CTools API version is too new, but if you do that you need
20 * to be very quick about watching CTools API releases and release new versions
21 * of your software as soon as the new release is made, or people might end up
22 * updating CTools and having your module shut down without any recourse.
23 *
24 * It is recommended that every hook of your module that might use CTools or
25 * might lead to a use of CTools be guarded like this:
26 *
27 * @code
28 * if (!module_invoke('ctools', 'api_version', '1.0')) {
29 * return;
30 * }
31 * @endcode
32 *
33 * Note that some hooks such as _menu() or _theme() must return an array().
34 *
35 * You can use it in your hook_requirements to report this error condition
36 * like this:
37 *
38 * @code
39 * define('MODULENAME_MINIMUM_CTOOLS_API_VERSION', '1.0');
40 * define('MODULENAME_MAXIMUM_CTOOLS_API_VERSION', '1.1');
41 *
42 * function MODULENAME_requirements($phase) {
43 * $requirements = array();
44 * if (!module_invoke('ctools', 'api_version', MODULENAME_MINIMUM_CTOOLS_API_VERSION, MODULENAME_MAXIMUM_CTOOLS_API_VERSION)) {
45 * $requirements['MODULENAME_ctools'] = array(
46 * 'title' => $t('MODULENAME required Chaos Tool Suite (CTools) API Version'),
47 * 'value' => t('Between @a and @b', array('@a' => MODULENAME_MINIMUM_CTOOLS_API_VERSION, '@b' => MODULENAME_MAXIMUM_CTOOLS_API_VERSION)),
48 * 'severity' => REQUIREMENT_ERROR,
49 * );
50 * }
51 * return $requirements;
52 * }
53 * @endcode
54 *
55 * Please note that the version is a string, not an floating point number.
56 * This will matter once CTools reaches version 1.10.
57 *
58 * A CTools API changes history will be kept in API.txt. Not every new
59 * version of CTools will necessarily update the API version.
60 * @param $minimum
61 * The minimum version of CTools necessary for your software to run with it.
62 * @param $maximum
63 * The maximum version of CTools allowed for your software to run with it.
64 */
65 function ctools_api_version($minimum, $maximum = NULL) {
66 if (version_compare(CTOOLS_API_VERSION, $minimum, '<')) {
67 return FALSE;
68 }
69
70 if (isset($maximum) && version_compare(CTOOLS_API_VERSION, $maximum, '>')) {
71 return FALSE;
72 }
73
74 return TRUE;
75 }
76
77 // -----------------------------------------------------------------------
78 // General utility functions
79
80 /**
81 * Include .inc files as necessary.
82 *
83 * This fuction is helpful for including .inc files for your module. The
84 * general case is including ctools funcitonality like this:
85 *
86 * @code
87 * ctools_include('plugins');
88 * @endcode
89 *
90 * Similar funcitonality can be used for other modules by providing the $module
91 * and $dir arguments like this:
92 *
93 * @code
94 * // include mymodule/includes/import.inc
95 * ctools_include('import', 'mymodule');
96 * // include mymodule/plugins/foobar.inc
97 * ctools_include('foobar', 'mymodule', 'plugins');
98 * @endcode
99 *
100 * @param $file
101 * The base file name to be included.
102 * @param $module
103 * Optional module containing the include.
104 * @param $dir
105 * Optional subdirectory containing the include file.
106 */
107 function ctools_include($file, $module = 'ctools', $dir = 'includes') {
108 static $used = array();
109
110 $dir = '/' . ($dir ? $dir . '/' : '');
111
112 if (!isset($used[$module][$dir][$file])) {
113 require_once DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "$dir$file.inc";
114 $used[$module][$dir][$file] = TRUE;
115 }
116 }
117
118 /**
119 * Include .inc files in a form context.
120 *
121 * This is a variant of ctools_include that will save information in the
122 * the form_state so that cached forms will properly include things.
123 */
124 function ctools_form_include(&$form_state, $file, $module = 'ctools', $dir = 'includes') {
125 if (!isset($form_state['build_info']['args'])) {
126 $form_state['build_info']['args'] = array();
127 }
128
129 $dir = '/' . ($dir ? $dir . '/' : '');
130 form_load_include($form_state, 'inc', $module, $dir . $file);
131 }
132
133 /**
134 * Add an arbitrary path to the $form_state so it can work with form cache.
135 *
136 * module_load_include uses an unfortunately annoying syntax to work, making it
137 * difficult to translate the more simple $path + $file syntax.
138 */
139 function ctools_form_include_file(&$form_state, $filename) {
140 if (!isset($form_state['build_info']['args'])) {
141 $form_state['build_info']['args'] = array();
142 }
143
144 // Now add this to the build info files so that AJAX requests will know to load it.
145 $form_state['build_info']['files']["$filename"] = $filename;
146 require_once DRUPAL_ROOT . '/' . $filename;
147 }
148
149 /**
150 * Provide the proper path to an image as necessary.
151 *
152 * This helper function is used by ctools but can also be used in other
153 * modules in the same way as explained in the comments of ctools_include.
154 *
155 * @param $image
156 * The base file name (with extension) of the image to be included.
157 * @param $module
158 * Optional module containing the include.
159 * @param $dir
160 * Optional subdirectory containing the include file.
161 */
162 function ctools_image_path($image, $module = 'ctools', $dir = 'images') {
163 return drupal_get_path('module', $module) . "/$dir/" . $image;
164 }
165
166 /**
167 * Include css files as necessary.
168 *
169 * This helper function is used by ctools but can also be used in other
170 * modules in the same way as explained in the comments of ctools_include.
171 *
172 * @param $file
173 * The base file name to be included.
174 * @param $module
175 * Optional module containing the include.
176 * @param $dir
177 * Optional subdirectory containing the include file.
178 */
179 function ctools_add_css($file, $module = 'ctools', $dir = 'css') {
180 drupal_add_css(drupal_get_path('module', $module) . "/$dir/$file.css");
181 }
182
183 /**
184 * Format a css file name for use with $form['#attached']['css'].
185 *
186 * This helper function is used by ctools but can also be used in other
187 * modules in the same way as explained in the comments of ctools_include.
188 *
189 * @code
190 * $form['#attached']['css'] = array(ctools_attach_css('collapsible-div'));
191 * $form['#attached']['css'][ctools_attach_css('collapsible-div')] = array('preprocess' => FALSE);
192 * @endcode
193 *
194 * @param $file
195 * The base file name to be included.
196 * @param $module
197 * Optional module containing the include.
198 * @param $dir
199 * Optional subdirectory containing the include file.
200 */
201 function ctools_attach_css($file, $module = 'ctools', $dir = 'css') {
202 return drupal_get_path('module', $module) . "/$dir/$file.css";
203 }
204
205 /**
206 * Include js files as necessary.
207 *
208 * This helper function is used by ctools but can also be used in other
209 * modules in the same way as explained in the comments of ctools_include.
210 *
211 * @param $file
212 * The base file name to be included.
213 * @param $module
214 * Optional module containing the include.
215 * @param $dir
216 * Optional subdirectory containing the include file.
217 */
218 function ctools_add_js($file, $module = 'ctools', $dir = 'js') {
219 drupal_add_js(drupal_get_path('module', $module) . "/$dir/$file.js");
220 }
221
222 /**
223 * Format a javascript file name for use with $form['#attached']['js'].
224 *
225 * This helper function is used by ctools but can also be used in other
226 * modules in the same way as explained in the comments of ctools_include.
227 *
228 * @code
229 * $form['#attached']['js'] = array(ctools_attach_js('auto-submit'));
230 * @endcode
231 *
232 * @param $file
233 * The base file name to be included.
234 * @param $module
235 * Optional module containing the include.
236 * @param $dir
237 * Optional subdirectory containing the include file.
238 */
239 function ctools_attach_js($file, $module = 'ctools', $dir = 'js') {
240 return drupal_get_path('module', $module) . "/$dir/$file.js";
241 }
242
243 /**
244 * Get a list of roles in the system.
245 *
246 * @return
247 * An array of role names keyed by role ID.
248 *
249 * @deprecated
250 * user_roles() should be used instead.
251 */
252 function ctools_get_roles() {
253 return user_roles();
254 }
255
256 /*
257 * Break x,y,z and x+y+z into an array. Numeric only.
258 *
259 * @param $str
260 * The string to parse.
261 *
262 * @return $object
263 * An object containing
264 * - operator: Either 'and' or 'or'
265 * - value: An array of numeric values.
266 */
267 function ctools_break_phrase($str) {
268 $object = new stdClass();
269
270 if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) {
271 // The '+' character in a query string may be parsed as ' '.
272 $object->operator = 'or';
273 $object->value = preg_split('/[+ ]/', $str);
274 }
275 else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) {
276 $object->operator = 'and';
277 $object->value = explode(',', $str);
278 }
279
280 // Keep an 'error' value if invalid strings were given.
281 if (!empty($str) && (empty($object->value) || !is_array($object->value))) {
282 $object->value = array(-1);
283 $object->invalid_input = TRUE;
284 return $object;
285 }
286
287 if (empty($object->value)) {
288 $object->value = array();
289 }
290
291 // Doubly ensure that all values are numeric only.
292 foreach ($object->value as $id => $value) {
293 $object->value[$id] = intval($value);
294 }
295
296 return $object;
297 }
298
299 /**
300 * Set a token/value pair to be replaced later in the request, specifically in
301 * ctools_preprocess_page().
302 *
303 * @param $token
304 * The token to be replaced later, during page rendering. This should
305 * ideally be a string inside of an HTML comment, so that if there is
306 * no replacement, the token will not render on the page.
307 * @param $type
308 * The type of the token. Can be either 'variable', which will pull data
309 * directly from the page variables
310 * @param $argument
311 * If $type == 'variable' then argument should be the key to fetch from
312 * the $variables. If $type == 'callback' then it should either be the
313 * callback, or an array that will be sent to call_user_func_array().
314 *
315 * @return
316 * A array of token/variable names to be replaced.
317 */
318 function ctools_set_page_token($token = NULL, $type = NULL, $argument = NULL) {
319 static $tokens = array();
320
321 if (isset($token)) {
322 $tokens[$token] = array($type, $argument);
323 }
324 return $tokens;
325 }
326
327 /**
328 * Easily set a token from the page variables.
329 *
330 * This function can be used like this:
331 * $token = ctools_set_variable_token('tabs');
332 *
333 * $token will then be a simple replacement for the 'tabs' about of the
334 * variables available in the page template.
335 */
336 function ctools_set_variable_token($token) {
337 $string = '<!-- ctools-page-' . $token . ' -->';
338 ctools_set_page_token($string, 'variable', $token);
339 return $string;
340 }
341
342 /**
343 * Easily set a token from the page variables.
344 *
345 * This function can be used like this:
346 * $token = ctools_set_variable_token('id', 'mymodule_myfunction');
347 */
348 function ctools_set_callback_token($token, $callback) {
349 // If the callback uses arguments they are considered in the token.
350 if (is_array($callback)) {
351 $token .= '-' . md5(serialize($callback));
352 }
353 $string = '<!-- ctools-page-' . $token . ' -->';
354 ctools_set_page_token($string, 'callback', $callback);
355 return $string;
356 }
357
358 /**
359 * Tell CTools that sidebar blocks should not be rendered.
360 *
361 * It is often desirable to not display sidebars when rendering a page,
362 * particularly when using Panels. This informs CTools to alter out any
363 * sidebar regions during block render.
364 */
365 function ctools_set_no_blocks($blocks = FALSE) {
366 $status = &drupal_static(__FUNCTION__, TRUE);
367 $status = $blocks;
368 }
369
370 /**
371 * Add an array of classes to the body.
372 *
373 * @param mixed $classes
374 * A string or an array of class strings to add.
375 * @param string $hook
376 * The theme hook to add the class to. The default is 'html' which will
377 * affect the body tag.
378 */
379 function ctools_class_add($classes, $hook = 'html') {
380 if (!is_array($classes)) {
381 $classes = array($classes);
382 }
383
384 $static = &drupal_static('ctools_process_classes', array());
385 if (!isset($static[$hook]['add'])) {
386 $static[$hook]['add'] = array();
387 }
388 foreach ($classes as $class) {
389 $static[$hook]['add'][] = $class;
390 }
391 }
392
393 /**
394 * Remove an array of classes from the body.
395 *
396 * @param mixed $classes
397 * A string or an array of class strings to remove.
398 * @param string $hook
399 * The theme hook to remove the class from. The default is 'html' which will
400 * affect the body tag.
401 */
402 function ctools_class_remove($classes, $hook = 'html') {
403 if (!is_array($classes)) {
404 $classes = array($classes);
405 }
406
407 $static = &drupal_static('ctools_process_classes', array());
408 if (!isset($static[$hook]['remove'])) {
409 $static[$hook]['remove'] = array();
410 }
411 foreach ($classes as $class) {
412 $static[$hook]['remove'][] = $class;
413 }
414 }
415
416 // -----------------------------------------------------------------------
417 // Drupal core hooks
418
419 /**
420 * Implement hook_init to keep our global CSS at the ready.
421 */
422 function ctools_init() {
423 ctools_add_css('ctools');
424 // If we are sure that CTools' AJAX is in use, change the error handling.
425 if (!empty($_REQUEST['ctools_ajax'])) {
426 ini_set('display_errors', 0);
427 register_shutdown_function('ctools_shutdown_handler');
428 }
429
430 // Clear plugin cache on the module page submit.
431 if ($_GET['q'] == 'admin/modules/list/confirm' && !empty($_POST)) {
432 cache_clear_all('ctools_plugin_files:', 'cache', TRUE);
433 }
434 }
435
436 /**
437 * Shutdown handler used during ajax operations to help catch fatal errors.
438 */
439 function ctools_shutdown_handler() {
440 if (function_exists('error_get_last') AND ($error = error_get_last())) {
441 switch ($error['type']) {
442 case E_ERROR:
443 case E_CORE_ERROR:
444 case E_COMPILE_ERROR:
445 case E_USER_ERROR:
446 // Do this manually because including files here is dangerous.
447 $commands = array(
448 array(
449 'command' => 'alert',
450 'title' => t('Error'),
451 'text' => t('Unable to complete operation. Fatal error in @file on line @line: @message', array(
452 '@file' => $error['file'],
453 '@line' => $error['line'],
454 '@message' => $error['message'],
455 )),
456 ),
457 );
458
459 // Change the status code so that the client will read the AJAX returned.
460 header('HTTP/1.1 200 OK');
461 drupal_json($commands);
462 }
463 }
464 }
465
466 /**
467 * Implements hook_theme().
468 */
469 function ctools_theme() {
470 ctools_include('utility');
471 $items = array();
472 ctools_passthrough('ctools', 'theme', $items);
473 return $items;
474 }
475
476 /**
477 * Implements hook_menu().
478 */
479 function ctools_menu() {
480 ctools_include('utility');
481 $items = array();
482 ctools_passthrough('ctools', 'menu', $items);
483 return $items;
484 }
485
486 /**
487 * Implementation of hook_cron. Clean up old caches.
488 */
489 function ctools_cron() {
490 ctools_include('utility');
491 $items = array();
492 ctools_passthrough('ctools', 'cron', $items);
493 }
494
495 /**
496 * Ensure the CTools CSS cache is flushed whenever hook_flush_caches is invoked.
497 */
498 function ctools_flush_caches() {
499 // Do not actually flush caches if running on cron. Drupal uses this hook
500 // in an inconsistent fashion and it does not necessarily mean to *flush*
501 // caches when running from cron. Instead it's just getting a list of cache
502 // tables and may not do any flushing.
503 if (!empty($GLOBALS['locks']['cron'])) {
504 return;
505 }
506
507 ctools_include('css');
508 ctools_css_flush_caches();
509 }
510
511 /**
512 * Implements hook_element_info_alter().
513 *
514 */
515 function ctools_element_info_alter(&$type) {
516 ctools_include('dependent');
517 ctools_dependent_element_info_alter($type);
518 }
519
520 /**
521 * Implementation of hook_file_download()
522 *
523 * When using the private file system, we have to let Drupal know it's ok to
524 * download CSS and image files from our temporary directory.
525 */
526 function ctools_file_download($filepath) {
527 if (strpos($filepath, 'ctools') === 0) {
528 $mime = file_get_mimetype($filepath);
529 // For safety's sake, we allow only text and images.
530 if (strpos($mime, 'text') === 0 || strpos($mime, 'image') === 0) {
531 return array('Content-type:' . $mime);
532 }
533 }
534 }
535
536 /**
537 * Implements hook_registry_files_alter().
538 *
539 * Alter the registry of files to automagically include all classes in
540 * class-based plugins.
541 */
542 function ctools_registry_files_alter(&$files, $indexed_modules) {
543 ctools_include('registry');
544 return _ctools_registry_files_alter($files, $indexed_modules);
545 }
546
547 // -----------------------------------------------------------------------
548 // CTools hook implementations.
549
550 /**
551 * Implementation of hook_ctools_plugin_directory() to let the system know
552 * where all our own plugins are.
553 */
554 function ctools_ctools_plugin_directory($owner, $plugin_type) {
555 if ($owner == 'ctools') {
556 return 'plugins/' . $plugin_type;
557 }
558 }
559
560 /**
561 * Implements hook_ctools_plugin_type().
562 */
563 function ctools_ctools_plugin_type() {
564 ctools_include('utility');
565 $items = array();
566 // Add all the plugins that have their own declaration space elsewhere.
567 ctools_passthrough('ctools', 'plugin-type', $items);
568
569 return $items;
570 }
571
572 // -----------------------------------------------------------------------
573 // Drupal theme preprocess hooks that must be in the .module file.
574
575 /**
576 * A theme preprocess function to automatically allow panels-based node
577 * templates based upon input when the panel was configured.
578 */
579 function ctools_preprocess_node(&$vars) {
580 // The 'ctools_template_identifier' attribute of the node is added when the pane is
581 // rendered.
582 if (!empty($vars['node']->ctools_template_identifier)) {
583 $vars['ctools_template_identifier'] = check_plain($vars['node']->ctools_template_identifier);
584 $vars['theme_hook_suggestions'][] = 'node__panel__' . check_plain($vars['node']->ctools_template_identifier);
585 }
586 }
587
588 function ctools_page_alter(&$page) {
589 $page['#post_render'][] = 'ctools_page_token_processing';
590 }
591
592 /**
593 * A theme post_render callback to allow content type plugins to use page
594 * template variables which are not yet available when the content type is
595 * rendered.
596 */
597 function ctools_page_token_processing($children, $elements) {
598 $tokens = ctools_set_page_token();
599 if (!empty($tokens)) {
600 foreach ($tokens as $token => $key) {
601 list($type, $argument) = $key;
602 switch ($type) {
603 case 'variable':
604 $tokens[$token] = isset($variables[$argument]) ? $variables[$argument] : '';
605 break;
606 case 'callback':
607 if (is_string($argument) && function_exists($argument)) {
608 $tokens[$token] = $argument($variables);
609 }
610 if (is_array($argument) && function_exists($argument[0])) {
611 $function = array_shift($argument);
612 $argument = array_merge(array(&$variables), $argument);
613 $tokens[$token] = call_user_func_array($function, $argument);
614 }
615 break;
616 }
617 }
618 $children = strtr($children, $tokens);
619 }
620 return $children;
621 }
622
623 /**
624 * Implements hook_process().
625 *
626 * Add and remove CSS classes from the variables array. We use process so that
627 * we alter anything added in the preprocess hooks.
628 */
629 function ctools_process(&$variables, $hook) {
630 if (!isset($variables['classes'])) {
631 return;
632 }
633
634 $classes = drupal_static('ctools_process_classes', array());
635
636 // Process the classses to add.
637 if (!empty($classes[$hook]['add'])) {
638 $add_classes = array_map('drupal_clean_css_identifier', $classes[$hook]['add']);
639 $variables['classes_array'] = array_unique(array_merge($variables['classes_array'], $add_classes));
640 }
641
642 // Process the classes to remove.
643 if (!empty($classes[$hook]['remove'])) {
644 $remove_classes = array_map('drupal_clean_css_identifier', $classes[$hook]['remove']);
645 $variables['classes_array'] = array_diff($variables['classes_array'], $remove_classes);
646 }
647
648 // Since this runs after template_process(), we need to re-implode the
649 // classes array.
650 $variables['classes'] = implode(' ', $variables['classes_array']);
651 }
652
653 // -----------------------------------------------------------------------
654 // Menu callbacks that must be in the .module file.
655
656 /**
657 * Determine if the current user has access via a plugin.
658 *
659 * This function is meant to be embedded in the Drupal menu system, and
660 * therefore is in the .module file since sub files can't be loaded, and
661 * takes arguments a little bit more haphazardly than ctools_access().
662 *
663 * @param $access
664 * An access control array which contains the following information:
665 * - 'logic': and or or. Whether all tests must pass or one must pass.
666 * - 'plugins': An array of access plugins. Each contains:
667 * - - 'name': The name of the plugin
668 * - - 'settings': The settings from the plugin UI.
669 * - - 'context': Which context to use.
670 * @param ...
671 * zero or more context arguments generated from argument plugins. These
672 * contexts must have an 'id' attached to them so that they can be
673 * properly associated. The argument plugin system should set this, but
674 * if the context is coming from elsewhere it will need to be set manually.
675 *
676 * @return
677 * TRUE if access is granted, false if otherwise.
678 */
679 function ctools_access_menu($access) {
680 // Short circuit everything if there are no access tests.
681 if (empty($access['plugins'])) {
682 return TRUE;
683 }
684
685 $contexts = array();
686 foreach (func_get_args() as $arg) {
687 if (is_object($arg) && get_class($arg) == 'ctools_context') {
688 $contexts[$arg->id] = $arg;
689 }
690 }
691
692 ctools_include('context');
693 return ctools_access($access, $contexts);
694 }
695
696 /**
697 * Determine if the current user has access via checks to multiple different
698 * permissions.
699 *
700 * This function is a thin wrapper around user_access that allows multiple
701 * permissions to be easily designated for use on, for example, a menu callback.
702 *
703 * @param ...
704 * An indexed array of zero or more permission strings to be checked by
705 * user_access().
706 *
707 * @return
708 * Iff all checks pass will this function return TRUE. If an invalid argument
709 * is passed (e.g., not a string), this function errs on the safe said and
710 * returns FALSE.
711 */
712 function ctools_access_multiperm() {
713 foreach (func_get_args() as $arg) {
714 if (!is_string($arg) || !user_access($arg)) {
715 return FALSE;
716 }
717 }
718 return TRUE;
719 }
720
721 /**
722 * Check to see if the incoming menu item is js capable or not.
723 *
724 * This can be used as %ctools_js as part of a path in hook menu. CTools
725 * ajax functions will automatically change the phrase 'nojs' to 'ajax'
726 * when it attaches ajax to a link. This can be used to autodetect if
727 * that happened.
728 */
729 function ctools_js_load($js) {
730 if ($js == 'ajax') {
731 return TRUE;
732 }
733 return 0;
734 }
735
736 /**
737 * Menu _load hook.
738 *
739 * This function will be called to load an object as a replacement for
740 * %ctools_export_ui in menu paths.
741 */
742 function ctools_export_ui_load($item_name, $plugin_name) {
743 $return = &drupal_static(__FUNCTION__, FALSE);
744
745 if (!$return) {
746 ctools_include('export-ui');
747 $plugin = ctools_get_export_ui($plugin_name);
748 $handler = ctools_export_ui_get_handler($plugin);
749
750 if ($handler) {
751 return $handler->load_item($item_name);
752 }
753 }
754
755 return $return;
756 }
757
758 // -----------------------------------------------------------------------
759 // Caching callbacks on behalf of export-ui.
760
761 /**
762 * Menu access callback for various tasks of export-ui.
763 */
764 function ctools_export_ui_task_access($plugin_name, $op, $item = NULL) {
765 ctools_include('export-ui');
766 $plugin = ctools_get_export_ui($plugin_name);
767 $handler = ctools_export_ui_get_handler($plugin);
768
769 if ($handler) {
770 return $handler->access($op, $item);
771 }
772
773 // Deny access if the handler cannot be found.
774 return FALSE;
775 }
776
777 /**
778 * Callback for access control ajax form on behalf of export ui.
779 *
780 * Returns the cached access config and contexts used.
781 * Note that this is assuming that access will be in $item->access -- if it
782 * is not, an export UI plugin will have to make its own callbacks.
783 */
784 function ctools_export_ui_ctools_access_get($argument) {
785 ctools_include('export-ui');
786 list($plugin_name, $key) = explode(':', $argument, 2);
787
788 $plugin = ctools_get_export_ui($plugin_name);
789 $handler = ctools_export_ui_get_handler($plugin);
790
791 if ($handler) {
792 ctools_include('context');
793 $item = $handler->edit_cache_get($key);
794 if (!$item) {
795 $item = ctools_export_crud_load($handler->plugin['schema'], $key);
796 }
797
798 $contexts = ctools_context_load_contexts($item);
799 return array($item->access, $contexts);
800 }
801 }
802
803 /**
804 * Callback for access control ajax form on behalf of export ui
805 *
806 * Returns the cached access config and contexts used.
807 * Note that this is assuming that access will be in $item->access -- if it
808 * is not, an export UI plugin will have to make its own callbacks.
809 */
810 function ctools_export_ui_ctools_access_set($argument, $access) {
811 ctools_include('export-ui');
812 list($plugin_name, $key) = explode(':', $argument, 2);
813
814 $plugin = ctools_get_export_ui($plugin_name);
815 $handler = ctools_export_ui_get_handler($plugin);
816
817 if ($handler) {
818 ctools_include('context');
819 $item = $handler->edit_cache_get($key);
820 if (!$item) {
821 $item = ctools_export_crud_load($handler->plugin['schema'], $key);
822 }
823 $item->access = $access;
824 return $handler->edit_cache_set_key($item, $key);
825 }
826 }
827
828 /**
829 * Implements hook_menu_local_tasks_alter().
830 */
831 function ctools_menu_local_tasks_alter(&$data, $router_item, $root_path) {
832 ctools_include('menu');
833 _ctools_menu_add_dynamic_items($data, $router_item, $root_path);
834 }
835
836 /**
837 * Implement hook_block_list_alter() to potentially remove blocks.
838 *
839 * This exists in order to replicate Drupal 6's "no blocks" functionality.
840 */
841 function ctools_block_list_alter(&$blocks) {
842 $check = drupal_static('ctools_set_no_blocks', TRUE);
843 if (!$check) {
844 foreach ($blocks as $block_id => $block) {
845 // @todo -- possibly we can set configuration for this so that users can
846 // specify which blocks will not get rendered.
847 if (strpos($block->region, 'sidebar') !== FALSE) {
848 unset($blocks[$block_id]);
849 }
850 }
851 }
852 }
853
854 /**
855 * Implement hook_modules_enabled to clear static caches for detecting new plugins
856 */
857 function ctools_modules_enabled($modules) {
858 ctools_include('plugins');
859 ctools_get_plugins_reset();
860 }
861
862 /**
863 * Menu theme callback.
864 *
865 * This simply ensures that Panels ajax calls are rendered in the same
866 * theme as the original page to prevent .css file confusion.
867 *
868 * To use this, set this as the theme callback on AJAX related menu
869 * items. Since the ajax page state won't be sent during ajax requests,
870 * it should be safe to use even if ajax isn't invoked.
871 */
872 function ctools_ajax_theme_callback() {
873 if (!empty($_POST['ajax_page_state']['theme'])) {
874 return $_POST['ajax_page_state']['theme'];
875 }
876 }
877
878 /**
879 * Implements hook_ctools_entity_context_alter().
880 */
881 function ctools_ctools_entity_context_alter(&$plugin, &$entity, $plugin_id) {
882 ctools_include('context');
883 switch ($plugin_id) {
884 case 'entity_id:taxonomy_term':
885 $plugin['no ui'] = TRUE;
886 break;
887 case 'entity:user':
888 $plugin = ctools_get_context('user');
889 unset($plugin['no ui']);
890 unset($plugin['no required context ui']);
891 break;
892 }
893
894 // Apply restrictions on taxonomy term reverse relationships whose
895 // restrictions are in the settings on the field.
896 if (!empty($plugin['parent']) &&
897 $plugin['parent'] == 'entity_from_field' &&
898 !empty($plugin['reverse']) &&
899 $plugin['to entity'] == 'taxonomy_term') {
900 $field = field_info_field($plugin['field name']);
901 if (isset($field['settings']['allowed_values'][0]['vocabulary'])) {
902 $plugin['required context']->restrictions = array('vocabulary' => array($field['settings']['allowed_values'][0]['vocabulary']));
903 }
904 }
905 }