ctools.module 27 KB

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