danielebarchiesi@0: $t('MODULENAME required Chaos Tool Suite (CTools) API Version'), danielebarchiesi@0: * 'value' => t('Between @a and @b', array('@a' => MODULENAME_MINIMUM_CTOOLS_API_VERSION, '@b' => MODULENAME_MAXIMUM_CTOOLS_API_VERSION)), danielebarchiesi@0: * 'severity' => REQUIREMENT_ERROR, danielebarchiesi@0: * ); danielebarchiesi@0: * } danielebarchiesi@0: * return $requirements; danielebarchiesi@0: * } danielebarchiesi@0: * @endcode danielebarchiesi@0: * danielebarchiesi@0: * Please note that the version is a string, not an floating point number. danielebarchiesi@0: * This will matter once CTools reaches version 1.10. danielebarchiesi@0: * danielebarchiesi@0: * A CTools API changes history will be kept in API.txt. Not every new danielebarchiesi@0: * version of CTools will necessarily update the API version. danielebarchiesi@0: * @param $minimum danielebarchiesi@0: * The minimum version of CTools necessary for your software to run with it. danielebarchiesi@0: * @param $maximum danielebarchiesi@0: * The maximum version of CTools allowed for your software to run with it. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_api_version($minimum, $maximum = NULL) { danielebarchiesi@0: if (version_compare(CTOOLS_API_VERSION, $minimum, '<')) { danielebarchiesi@0: return FALSE; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: if (isset($maximum) && version_compare(CTOOLS_API_VERSION, $maximum, '>')) { danielebarchiesi@0: return FALSE; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: return TRUE; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // ----------------------------------------------------------------------- danielebarchiesi@0: // General utility functions danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Include .inc files as necessary. danielebarchiesi@0: * danielebarchiesi@0: * This fuction is helpful for including .inc files for your module. The danielebarchiesi@0: * general case is including ctools funcitonality like this: danielebarchiesi@0: * danielebarchiesi@0: * @code danielebarchiesi@0: * ctools_include('plugins'); danielebarchiesi@0: * @endcode danielebarchiesi@0: * danielebarchiesi@0: * Similar funcitonality can be used for other modules by providing the $module danielebarchiesi@0: * and $dir arguments like this: danielebarchiesi@0: * danielebarchiesi@0: * @code danielebarchiesi@0: * // include mymodule/includes/import.inc danielebarchiesi@0: * ctools_include('import', 'mymodule'); danielebarchiesi@0: * // include mymodule/plugins/foobar.inc danielebarchiesi@0: * ctools_include('foobar', 'mymodule', 'plugins'); danielebarchiesi@0: * @endcode danielebarchiesi@0: * danielebarchiesi@0: * @param $file danielebarchiesi@0: * The base file name to be included. danielebarchiesi@0: * @param $module danielebarchiesi@0: * Optional module containing the include. danielebarchiesi@0: * @param $dir danielebarchiesi@0: * Optional subdirectory containing the include file. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_include($file, $module = 'ctools', $dir = 'includes') { danielebarchiesi@0: static $used = array(); danielebarchiesi@0: danielebarchiesi@0: $dir = '/' . ($dir ? $dir . '/' : ''); danielebarchiesi@0: danielebarchiesi@0: if (!isset($used[$module][$dir][$file])) { danielebarchiesi@0: require_once DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "$dir$file.inc"; danielebarchiesi@0: $used[$module][$dir][$file] = TRUE; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Include .inc files in a form context. danielebarchiesi@0: * danielebarchiesi@0: * This is a variant of ctools_include that will save information in the danielebarchiesi@0: * the form_state so that cached forms will properly include things. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_form_include(&$form_state, $file, $module = 'ctools', $dir = 'includes') { danielebarchiesi@0: if (!isset($form_state['build_info']['args'])) { danielebarchiesi@0: $form_state['build_info']['args'] = array(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: $dir = '/' . ($dir ? $dir . '/' : ''); danielebarchiesi@0: form_load_include($form_state, 'inc', $module, $dir . $file); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Add an arbitrary path to the $form_state so it can work with form cache. danielebarchiesi@0: * danielebarchiesi@0: * module_load_include uses an unfortunately annoying syntax to work, making it danielebarchiesi@0: * difficult to translate the more simple $path + $file syntax. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_form_include_file(&$form_state, $filename) { danielebarchiesi@0: if (!isset($form_state['build_info']['args'])) { danielebarchiesi@0: $form_state['build_info']['args'] = array(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Now add this to the build info files so that AJAX requests will know to load it. danielebarchiesi@0: $form_state['build_info']['files']["$filename"] = $filename; danielebarchiesi@0: require_once DRUPAL_ROOT . '/' . $filename; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Provide the proper path to an image as necessary. danielebarchiesi@0: * danielebarchiesi@0: * This helper function is used by ctools but can also be used in other danielebarchiesi@0: * modules in the same way as explained in the comments of ctools_include. danielebarchiesi@0: * danielebarchiesi@0: * @param $image danielebarchiesi@0: * The base file name (with extension) of the image to be included. danielebarchiesi@0: * @param $module danielebarchiesi@0: * Optional module containing the include. danielebarchiesi@0: * @param $dir danielebarchiesi@0: * Optional subdirectory containing the include file. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_image_path($image, $module = 'ctools', $dir = 'images') { danielebarchiesi@0: return drupal_get_path('module', $module) . "/$dir/" . $image; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Include css files as necessary. danielebarchiesi@0: * danielebarchiesi@0: * This helper function is used by ctools but can also be used in other danielebarchiesi@0: * modules in the same way as explained in the comments of ctools_include. danielebarchiesi@0: * danielebarchiesi@0: * @param $file danielebarchiesi@0: * The base file name to be included. danielebarchiesi@0: * @param $module danielebarchiesi@0: * Optional module containing the include. danielebarchiesi@0: * @param $dir danielebarchiesi@0: * Optional subdirectory containing the include file. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_add_css($file, $module = 'ctools', $dir = 'css') { danielebarchiesi@0: drupal_add_css(drupal_get_path('module', $module) . "/$dir/$file.css"); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Format a css file name for use with $form['#attached']['css']. danielebarchiesi@0: * danielebarchiesi@0: * This helper function is used by ctools but can also be used in other danielebarchiesi@0: * modules in the same way as explained in the comments of ctools_include. danielebarchiesi@0: * danielebarchiesi@0: * @code danielebarchiesi@0: * $form['#attached']['css'] = array(ctools_attach_css('collapsible-div')); danielebarchiesi@0: * $form['#attached']['css'][ctools_attach_css('collapsible-div')] = array('preprocess' => FALSE); danielebarchiesi@0: * @endcode danielebarchiesi@0: * danielebarchiesi@0: * @param $file danielebarchiesi@0: * The base file name to be included. danielebarchiesi@0: * @param $module danielebarchiesi@0: * Optional module containing the include. danielebarchiesi@0: * @param $dir danielebarchiesi@0: * Optional subdirectory containing the include file. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_attach_css($file, $module = 'ctools', $dir = 'css') { danielebarchiesi@0: return drupal_get_path('module', $module) . "/$dir/$file.css"; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Include js files as necessary. danielebarchiesi@0: * danielebarchiesi@0: * This helper function is used by ctools but can also be used in other danielebarchiesi@0: * modules in the same way as explained in the comments of ctools_include. danielebarchiesi@0: * danielebarchiesi@0: * @param $file danielebarchiesi@0: * The base file name to be included. danielebarchiesi@0: * @param $module danielebarchiesi@0: * Optional module containing the include. danielebarchiesi@0: * @param $dir danielebarchiesi@0: * Optional subdirectory containing the include file. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_add_js($file, $module = 'ctools', $dir = 'js') { danielebarchiesi@0: drupal_add_js(drupal_get_path('module', $module) . "/$dir/$file.js"); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Format a javascript file name for use with $form['#attached']['js']. danielebarchiesi@0: * danielebarchiesi@0: * This helper function is used by ctools but can also be used in other danielebarchiesi@0: * modules in the same way as explained in the comments of ctools_include. danielebarchiesi@0: * danielebarchiesi@0: * @code danielebarchiesi@0: * $form['#attached']['js'] = array(ctools_attach_js('auto-submit')); danielebarchiesi@0: * @endcode danielebarchiesi@0: * danielebarchiesi@0: * @param $file danielebarchiesi@0: * The base file name to be included. danielebarchiesi@0: * @param $module danielebarchiesi@0: * Optional module containing the include. danielebarchiesi@0: * @param $dir danielebarchiesi@0: * Optional subdirectory containing the include file. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_attach_js($file, $module = 'ctools', $dir = 'js') { danielebarchiesi@0: return drupal_get_path('module', $module) . "/$dir/$file.js"; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Get a list of roles in the system. danielebarchiesi@0: * danielebarchiesi@0: * @return danielebarchiesi@0: * An array of role names keyed by role ID. danielebarchiesi@0: * danielebarchiesi@0: * @deprecated danielebarchiesi@0: * user_roles() should be used instead. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_get_roles() { danielebarchiesi@0: return user_roles(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /* danielebarchiesi@0: * Break x,y,z and x+y+z into an array. Numeric only. danielebarchiesi@0: * danielebarchiesi@0: * @param $str danielebarchiesi@0: * The string to parse. danielebarchiesi@0: * danielebarchiesi@0: * @return $object danielebarchiesi@0: * An object containing danielebarchiesi@0: * - operator: Either 'and' or 'or' danielebarchiesi@0: * - value: An array of numeric values. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_break_phrase($str) { danielebarchiesi@0: $object = new stdClass(); danielebarchiesi@0: danielebarchiesi@0: if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) { danielebarchiesi@0: // The '+' character in a query string may be parsed as ' '. danielebarchiesi@0: $object->operator = 'or'; danielebarchiesi@0: $object->value = preg_split('/[+ ]/', $str); danielebarchiesi@0: } danielebarchiesi@0: else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) { danielebarchiesi@0: $object->operator = 'and'; danielebarchiesi@0: $object->value = explode(',', $str); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Keep an 'error' value if invalid strings were given. danielebarchiesi@0: if (!empty($str) && (empty($object->value) || !is_array($object->value))) { danielebarchiesi@0: $object->value = array(-1); danielebarchiesi@0: $object->invalid_input = TRUE; danielebarchiesi@0: return $object; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: if (empty($object->value)) { danielebarchiesi@0: $object->value = array(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Doubly ensure that all values are numeric only. danielebarchiesi@0: foreach ($object->value as $id => $value) { danielebarchiesi@0: $object->value[$id] = intval($value); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: return $object; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Set a token/value pair to be replaced later in the request, specifically in danielebarchiesi@0: * ctools_preprocess_page(). danielebarchiesi@0: * danielebarchiesi@0: * @param $token danielebarchiesi@0: * The token to be replaced later, during page rendering. This should danielebarchiesi@0: * ideally be a string inside of an HTML comment, so that if there is danielebarchiesi@0: * no replacement, the token will not render on the page. danielebarchiesi@0: * @param $type danielebarchiesi@0: * The type of the token. Can be either 'variable', which will pull data danielebarchiesi@0: * directly from the page variables danielebarchiesi@0: * @param $argument danielebarchiesi@0: * If $type == 'variable' then argument should be the key to fetch from danielebarchiesi@0: * the $variables. If $type == 'callback' then it should either be the danielebarchiesi@0: * callback, or an array that will be sent to call_user_func_array(). danielebarchiesi@0: * danielebarchiesi@0: * @return danielebarchiesi@0: * A array of token/variable names to be replaced. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_set_page_token($token = NULL, $type = NULL, $argument = NULL) { danielebarchiesi@0: static $tokens = array(); danielebarchiesi@0: danielebarchiesi@0: if (isset($token)) { danielebarchiesi@0: $tokens[$token] = array($type, $argument); danielebarchiesi@0: } danielebarchiesi@0: return $tokens; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Easily set a token from the page variables. danielebarchiesi@0: * danielebarchiesi@0: * This function can be used like this: danielebarchiesi@0: * $token = ctools_set_variable_token('tabs'); danielebarchiesi@0: * danielebarchiesi@0: * $token will then be a simple replacement for the 'tabs' about of the danielebarchiesi@0: * variables available in the page template. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_set_variable_token($token) { danielebarchiesi@0: $string = ''; danielebarchiesi@0: ctools_set_page_token($string, 'variable', $token); danielebarchiesi@0: return $string; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Easily set a token from the page variables. danielebarchiesi@0: * danielebarchiesi@0: * This function can be used like this: danielebarchiesi@0: * $token = ctools_set_variable_token('id', 'mymodule_myfunction'); danielebarchiesi@0: */ danielebarchiesi@0: function ctools_set_callback_token($token, $callback) { danielebarchiesi@0: // If the callback uses arguments they are considered in the token. danielebarchiesi@0: if (is_array($callback)) { danielebarchiesi@0: $token .= '-' . md5(serialize($callback)); danielebarchiesi@0: } danielebarchiesi@0: $string = ''; danielebarchiesi@0: ctools_set_page_token($string, 'callback', $callback); danielebarchiesi@0: return $string; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Tell CTools that sidebar blocks should not be rendered. danielebarchiesi@0: * danielebarchiesi@0: * It is often desirable to not display sidebars when rendering a page, danielebarchiesi@0: * particularly when using Panels. This informs CTools to alter out any danielebarchiesi@0: * sidebar regions during block render. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_set_no_blocks($blocks = FALSE) { danielebarchiesi@0: $status = &drupal_static(__FUNCTION__, TRUE); danielebarchiesi@0: $status = $blocks; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Add an array of classes to the body. danielebarchiesi@0: * danielebarchiesi@0: * @param mixed $classes danielebarchiesi@0: * A string or an array of class strings to add. danielebarchiesi@0: * @param string $hook danielebarchiesi@0: * The theme hook to add the class to. The default is 'html' which will danielebarchiesi@0: * affect the body tag. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_class_add($classes, $hook = 'html') { danielebarchiesi@0: if (!is_array($classes)) { danielebarchiesi@0: $classes = array($classes); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: $static = &drupal_static('ctools_process_classes', array()); danielebarchiesi@0: if (!isset($static[$hook]['add'])) { danielebarchiesi@0: $static[$hook]['add'] = array(); danielebarchiesi@0: } danielebarchiesi@0: foreach ($classes as $class) { danielebarchiesi@0: $static[$hook]['add'][] = $class; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Remove an array of classes from the body. danielebarchiesi@0: * danielebarchiesi@0: * @param mixed $classes danielebarchiesi@0: * A string or an array of class strings to remove. danielebarchiesi@0: * @param string $hook danielebarchiesi@0: * The theme hook to remove the class from. The default is 'html' which will danielebarchiesi@0: * affect the body tag. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_class_remove($classes, $hook = 'html') { danielebarchiesi@0: if (!is_array($classes)) { danielebarchiesi@0: $classes = array($classes); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: $static = &drupal_static('ctools_process_classes', array()); danielebarchiesi@0: if (!isset($static[$hook]['remove'])) { danielebarchiesi@0: $static[$hook]['remove'] = array(); danielebarchiesi@0: } danielebarchiesi@0: foreach ($classes as $class) { danielebarchiesi@0: $static[$hook]['remove'][] = $class; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // ----------------------------------------------------------------------- danielebarchiesi@0: // Drupal core hooks danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implement hook_init to keep our global CSS at the ready. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_init() { danielebarchiesi@0: ctools_add_css('ctools'); danielebarchiesi@0: // If we are sure that CTools' AJAX is in use, change the error handling. danielebarchiesi@0: if (!empty($_REQUEST['ctools_ajax'])) { danielebarchiesi@0: ini_set('display_errors', 0); danielebarchiesi@0: register_shutdown_function('ctools_shutdown_handler'); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Clear plugin cache on the module page submit. danielebarchiesi@0: if ($_GET['q'] == 'admin/modules/list/confirm' && !empty($_POST)) { danielebarchiesi@0: cache_clear_all('ctools_plugin_files:', 'cache', TRUE); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Shutdown handler used during ajax operations to help catch fatal errors. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_shutdown_handler() { danielebarchiesi@0: if (function_exists('error_get_last') AND ($error = error_get_last())) { danielebarchiesi@0: switch ($error['type']) { danielebarchiesi@0: case E_ERROR: danielebarchiesi@0: case E_CORE_ERROR: danielebarchiesi@0: case E_COMPILE_ERROR: danielebarchiesi@0: case E_USER_ERROR: danielebarchiesi@0: // Do this manually because including files here is dangerous. danielebarchiesi@0: $commands = array( danielebarchiesi@0: array( danielebarchiesi@0: 'command' => 'alert', danielebarchiesi@0: 'title' => t('Error'), danielebarchiesi@0: 'text' => t('Unable to complete operation. Fatal error in @file on line @line: @message', array( danielebarchiesi@0: '@file' => $error['file'], danielebarchiesi@0: '@line' => $error['line'], danielebarchiesi@0: '@message' => $error['message'], danielebarchiesi@0: )), danielebarchiesi@0: ), danielebarchiesi@0: ); danielebarchiesi@0: danielebarchiesi@0: // Change the status code so that the client will read the AJAX returned. danielebarchiesi@0: header('HTTP/1.1 200 OK'); danielebarchiesi@0: drupal_json($commands); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_theme(). danielebarchiesi@0: */ danielebarchiesi@0: function ctools_theme() { danielebarchiesi@0: ctools_include('utility'); danielebarchiesi@0: $items = array(); danielebarchiesi@0: ctools_passthrough('ctools', 'theme', $items); danielebarchiesi@0: return $items; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_menu(). danielebarchiesi@0: */ danielebarchiesi@0: function ctools_menu() { danielebarchiesi@0: ctools_include('utility'); danielebarchiesi@0: $items = array(); danielebarchiesi@0: ctools_passthrough('ctools', 'menu', $items); danielebarchiesi@0: return $items; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implementation of hook_cron. Clean up old caches. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_cron() { danielebarchiesi@0: ctools_include('utility'); danielebarchiesi@0: $items = array(); danielebarchiesi@0: ctools_passthrough('ctools', 'cron', $items); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Ensure the CTools CSS cache is flushed whenever hook_flush_caches is invoked. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_flush_caches() { danielebarchiesi@0: // Do not actually flush caches if running on cron. Drupal uses this hook danielebarchiesi@0: // in an inconsistent fashion and it does not necessarily mean to *flush* danielebarchiesi@0: // caches when running from cron. Instead it's just getting a list of cache danielebarchiesi@0: // tables and may not do any flushing. danielebarchiesi@0: if (!empty($GLOBALS['locks']['cron'])) { danielebarchiesi@0: return; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: ctools_include('css'); danielebarchiesi@0: ctools_css_flush_caches(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_element_info_alter(). danielebarchiesi@0: * danielebarchiesi@0: */ danielebarchiesi@0: function ctools_element_info_alter(&$type) { danielebarchiesi@0: ctools_include('dependent'); danielebarchiesi@0: ctools_dependent_element_info_alter($type); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implementation of hook_file_download() danielebarchiesi@0: * danielebarchiesi@0: * When using the private file system, we have to let Drupal know it's ok to danielebarchiesi@0: * download CSS and image files from our temporary directory. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_file_download($filepath) { danielebarchiesi@0: if (strpos($filepath, 'ctools') === 0) { danielebarchiesi@0: $mime = file_get_mimetype($filepath); danielebarchiesi@0: // For safety's sake, we allow only text and images. danielebarchiesi@0: if (strpos($mime, 'text') === 0 || strpos($mime, 'image') === 0) { danielebarchiesi@0: return array('Content-type:' . $mime); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_registry_files_alter(). danielebarchiesi@0: * danielebarchiesi@0: * Alter the registry of files to automagically include all classes in danielebarchiesi@0: * class-based plugins. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_registry_files_alter(&$files, $indexed_modules) { danielebarchiesi@0: ctools_include('registry'); danielebarchiesi@0: return _ctools_registry_files_alter($files, $indexed_modules); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // ----------------------------------------------------------------------- danielebarchiesi@0: // CTools hook implementations. danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implementation of hook_ctools_plugin_directory() to let the system know danielebarchiesi@0: * where all our own plugins are. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_ctools_plugin_directory($owner, $plugin_type) { danielebarchiesi@0: if ($owner == 'ctools') { danielebarchiesi@0: return 'plugins/' . $plugin_type; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_ctools_plugin_type(). danielebarchiesi@0: */ danielebarchiesi@0: function ctools_ctools_plugin_type() { danielebarchiesi@0: ctools_include('utility'); danielebarchiesi@0: $items = array(); danielebarchiesi@0: // Add all the plugins that have their own declaration space elsewhere. danielebarchiesi@0: ctools_passthrough('ctools', 'plugin-type', $items); danielebarchiesi@0: danielebarchiesi@0: return $items; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // ----------------------------------------------------------------------- danielebarchiesi@0: // Drupal theme preprocess hooks that must be in the .module file. danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * A theme preprocess function to automatically allow panels-based node danielebarchiesi@0: * templates based upon input when the panel was configured. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_preprocess_node(&$vars) { danielebarchiesi@0: // The 'ctools_template_identifier' attribute of the node is added when the pane is danielebarchiesi@0: // rendered. danielebarchiesi@0: if (!empty($vars['node']->ctools_template_identifier)) { danielebarchiesi@0: $vars['ctools_template_identifier'] = check_plain($vars['node']->ctools_template_identifier); danielebarchiesi@0: $vars['theme_hook_suggestions'][] = 'node__panel__' . check_plain($vars['node']->ctools_template_identifier); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: function ctools_page_alter(&$page) { danielebarchiesi@0: $page['#post_render'][] = 'ctools_page_token_processing'; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * A theme post_render callback to allow content type plugins to use page danielebarchiesi@0: * template variables which are not yet available when the content type is danielebarchiesi@0: * rendered. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_page_token_processing($children, $elements) { danielebarchiesi@0: $tokens = ctools_set_page_token(); danielebarchiesi@0: if (!empty($tokens)) { danielebarchiesi@0: foreach ($tokens as $token => $key) { danielebarchiesi@0: list($type, $argument) = $key; danielebarchiesi@0: switch ($type) { danielebarchiesi@0: case 'variable': danielebarchiesi@0: $tokens[$token] = isset($variables[$argument]) ? $variables[$argument] : ''; danielebarchiesi@0: break; danielebarchiesi@0: case 'callback': danielebarchiesi@0: if (is_string($argument) && function_exists($argument)) { danielebarchiesi@0: $tokens[$token] = $argument($variables); danielebarchiesi@0: } danielebarchiesi@0: if (is_array($argument) && function_exists($argument[0])) { danielebarchiesi@0: $function = array_shift($argument); danielebarchiesi@0: $argument = array_merge(array(&$variables), $argument); danielebarchiesi@0: $tokens[$token] = call_user_func_array($function, $argument); danielebarchiesi@0: } danielebarchiesi@0: break; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: $children = strtr($children, $tokens); danielebarchiesi@0: } danielebarchiesi@0: return $children; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_process(). danielebarchiesi@0: * danielebarchiesi@0: * Add and remove CSS classes from the variables array. We use process so that danielebarchiesi@0: * we alter anything added in the preprocess hooks. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_process(&$variables, $hook) { danielebarchiesi@0: if (!isset($variables['classes'])) { danielebarchiesi@0: return; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: $classes = drupal_static('ctools_process_classes', array()); danielebarchiesi@0: danielebarchiesi@0: // Process the classses to add. danielebarchiesi@0: if (!empty($classes[$hook]['add'])) { danielebarchiesi@0: $add_classes = array_map('drupal_clean_css_identifier', $classes[$hook]['add']); danielebarchiesi@0: $variables['classes_array'] = array_unique(array_merge($variables['classes_array'], $add_classes)); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Process the classes to remove. danielebarchiesi@0: if (!empty($classes[$hook]['remove'])) { danielebarchiesi@0: $remove_classes = array_map('drupal_clean_css_identifier', $classes[$hook]['remove']); danielebarchiesi@0: $variables['classes_array'] = array_diff($variables['classes_array'], $remove_classes); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Since this runs after template_process(), we need to re-implode the danielebarchiesi@0: // classes array. danielebarchiesi@0: $variables['classes'] = implode(' ', $variables['classes_array']); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // ----------------------------------------------------------------------- danielebarchiesi@0: // Menu callbacks that must be in the .module file. danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Determine if the current user has access via a plugin. danielebarchiesi@0: * danielebarchiesi@0: * This function is meant to be embedded in the Drupal menu system, and danielebarchiesi@0: * therefore is in the .module file since sub files can't be loaded, and danielebarchiesi@0: * takes arguments a little bit more haphazardly than ctools_access(). danielebarchiesi@0: * danielebarchiesi@0: * @param $access danielebarchiesi@0: * An access control array which contains the following information: danielebarchiesi@0: * - 'logic': and or or. Whether all tests must pass or one must pass. danielebarchiesi@0: * - 'plugins': An array of access plugins. Each contains: danielebarchiesi@0: * - - 'name': The name of the plugin danielebarchiesi@0: * - - 'settings': The settings from the plugin UI. danielebarchiesi@0: * - - 'context': Which context to use. danielebarchiesi@0: * @param ... danielebarchiesi@0: * zero or more context arguments generated from argument plugins. These danielebarchiesi@0: * contexts must have an 'id' attached to them so that they can be danielebarchiesi@0: * properly associated. The argument plugin system should set this, but danielebarchiesi@0: * if the context is coming from elsewhere it will need to be set manually. danielebarchiesi@0: * danielebarchiesi@0: * @return danielebarchiesi@0: * TRUE if access is granted, false if otherwise. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_access_menu($access) { danielebarchiesi@0: // Short circuit everything if there are no access tests. danielebarchiesi@0: if (empty($access['plugins'])) { danielebarchiesi@0: return TRUE; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: $contexts = array(); danielebarchiesi@0: foreach (func_get_args() as $arg) { danielebarchiesi@0: if (is_object($arg) && get_class($arg) == 'ctools_context') { danielebarchiesi@0: $contexts[$arg->id] = $arg; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: ctools_include('context'); danielebarchiesi@0: return ctools_access($access, $contexts); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Determine if the current user has access via checks to multiple different danielebarchiesi@0: * permissions. danielebarchiesi@0: * danielebarchiesi@0: * This function is a thin wrapper around user_access that allows multiple danielebarchiesi@0: * permissions to be easily designated for use on, for example, a menu callback. danielebarchiesi@0: * danielebarchiesi@0: * @param ... danielebarchiesi@0: * An indexed array of zero or more permission strings to be checked by danielebarchiesi@0: * user_access(). danielebarchiesi@0: * danielebarchiesi@0: * @return danielebarchiesi@0: * Iff all checks pass will this function return TRUE. If an invalid argument danielebarchiesi@0: * is passed (e.g., not a string), this function errs on the safe said and danielebarchiesi@0: * returns FALSE. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_access_multiperm() { danielebarchiesi@0: foreach (func_get_args() as $arg) { danielebarchiesi@0: if (!is_string($arg) || !user_access($arg)) { danielebarchiesi@0: return FALSE; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: return TRUE; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Check to see if the incoming menu item is js capable or not. danielebarchiesi@0: * danielebarchiesi@0: * This can be used as %ctools_js as part of a path in hook menu. CTools danielebarchiesi@0: * ajax functions will automatically change the phrase 'nojs' to 'ajax' danielebarchiesi@0: * when it attaches ajax to a link. This can be used to autodetect if danielebarchiesi@0: * that happened. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_js_load($js) { danielebarchiesi@0: if ($js == 'ajax') { danielebarchiesi@0: return TRUE; danielebarchiesi@0: } danielebarchiesi@0: return 0; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Menu _load hook. danielebarchiesi@0: * danielebarchiesi@0: * This function will be called to load an object as a replacement for danielebarchiesi@0: * %ctools_export_ui in menu paths. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_export_ui_load($item_name, $plugin_name) { danielebarchiesi@0: $return = &drupal_static(__FUNCTION__, FALSE); danielebarchiesi@0: danielebarchiesi@0: if (!$return) { danielebarchiesi@0: ctools_include('export-ui'); danielebarchiesi@0: $plugin = ctools_get_export_ui($plugin_name); danielebarchiesi@0: $handler = ctools_export_ui_get_handler($plugin); danielebarchiesi@0: danielebarchiesi@0: if ($handler) { danielebarchiesi@0: return $handler->load_item($item_name); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: return $return; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // ----------------------------------------------------------------------- danielebarchiesi@0: // Caching callbacks on behalf of export-ui. danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Menu access callback for various tasks of export-ui. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_export_ui_task_access($plugin_name, $op, $item = NULL) { danielebarchiesi@0: ctools_include('export-ui'); danielebarchiesi@0: $plugin = ctools_get_export_ui($plugin_name); danielebarchiesi@0: $handler = ctools_export_ui_get_handler($plugin); danielebarchiesi@0: danielebarchiesi@0: if ($handler) { danielebarchiesi@0: return $handler->access($op, $item); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Deny access if the handler cannot be found. danielebarchiesi@0: return FALSE; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Callback for access control ajax form on behalf of export ui. danielebarchiesi@0: * danielebarchiesi@0: * Returns the cached access config and contexts used. danielebarchiesi@0: * Note that this is assuming that access will be in $item->access -- if it danielebarchiesi@0: * is not, an export UI plugin will have to make its own callbacks. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_export_ui_ctools_access_get($argument) { danielebarchiesi@0: ctools_include('export-ui'); danielebarchiesi@0: list($plugin_name, $key) = explode(':', $argument, 2); danielebarchiesi@0: danielebarchiesi@0: $plugin = ctools_get_export_ui($plugin_name); danielebarchiesi@0: $handler = ctools_export_ui_get_handler($plugin); danielebarchiesi@0: danielebarchiesi@0: if ($handler) { danielebarchiesi@0: ctools_include('context'); danielebarchiesi@0: $item = $handler->edit_cache_get($key); danielebarchiesi@0: if (!$item) { danielebarchiesi@0: $item = ctools_export_crud_load($handler->plugin['schema'], $key); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: $contexts = ctools_context_load_contexts($item); danielebarchiesi@0: return array($item->access, $contexts); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Callback for access control ajax form on behalf of export ui danielebarchiesi@0: * danielebarchiesi@0: * Returns the cached access config and contexts used. danielebarchiesi@0: * Note that this is assuming that access will be in $item->access -- if it danielebarchiesi@0: * is not, an export UI plugin will have to make its own callbacks. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_export_ui_ctools_access_set($argument, $access) { danielebarchiesi@0: ctools_include('export-ui'); danielebarchiesi@0: list($plugin_name, $key) = explode(':', $argument, 2); danielebarchiesi@0: danielebarchiesi@0: $plugin = ctools_get_export_ui($plugin_name); danielebarchiesi@0: $handler = ctools_export_ui_get_handler($plugin); danielebarchiesi@0: danielebarchiesi@0: if ($handler) { danielebarchiesi@0: ctools_include('context'); danielebarchiesi@0: $item = $handler->edit_cache_get($key); danielebarchiesi@0: if (!$item) { danielebarchiesi@0: $item = ctools_export_crud_load($handler->plugin['schema'], $key); danielebarchiesi@0: } danielebarchiesi@0: $item->access = $access; danielebarchiesi@0: return $handler->edit_cache_set_key($item, $key); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_menu_local_tasks_alter(). danielebarchiesi@0: */ danielebarchiesi@0: function ctools_menu_local_tasks_alter(&$data, $router_item, $root_path) { danielebarchiesi@0: ctools_include('menu'); danielebarchiesi@0: _ctools_menu_add_dynamic_items($data, $router_item, $root_path); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implement hook_block_list_alter() to potentially remove blocks. danielebarchiesi@0: * danielebarchiesi@0: * This exists in order to replicate Drupal 6's "no blocks" functionality. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_block_list_alter(&$blocks) { danielebarchiesi@0: $check = drupal_static('ctools_set_no_blocks', TRUE); danielebarchiesi@0: if (!$check) { danielebarchiesi@0: foreach ($blocks as $block_id => $block) { danielebarchiesi@0: // @todo -- possibly we can set configuration for this so that users can danielebarchiesi@0: // specify which blocks will not get rendered. danielebarchiesi@0: if (strpos($block->region, 'sidebar') !== FALSE) { danielebarchiesi@0: unset($blocks[$block_id]); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implement hook_modules_enabled to clear static caches for detecting new plugins danielebarchiesi@0: */ danielebarchiesi@0: function ctools_modules_enabled($modules) { danielebarchiesi@0: ctools_include('plugins'); danielebarchiesi@0: ctools_get_plugins_reset(); danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Menu theme callback. danielebarchiesi@0: * danielebarchiesi@0: * This simply ensures that Panels ajax calls are rendered in the same danielebarchiesi@0: * theme as the original page to prevent .css file confusion. danielebarchiesi@0: * danielebarchiesi@0: * To use this, set this as the theme callback on AJAX related menu danielebarchiesi@0: * items. Since the ajax page state won't be sent during ajax requests, danielebarchiesi@0: * it should be safe to use even if ajax isn't invoked. danielebarchiesi@0: */ danielebarchiesi@0: function ctools_ajax_theme_callback() { danielebarchiesi@0: if (!empty($_POST['ajax_page_state']['theme'])) { danielebarchiesi@0: return $_POST['ajax_page_state']['theme']; danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: /** danielebarchiesi@0: * Implements hook_ctools_entity_context_alter(). danielebarchiesi@0: */ danielebarchiesi@0: function ctools_ctools_entity_context_alter(&$plugin, &$entity, $plugin_id) { danielebarchiesi@0: ctools_include('context'); danielebarchiesi@0: switch ($plugin_id) { danielebarchiesi@0: case 'entity_id:taxonomy_term': danielebarchiesi@0: $plugin['no ui'] = TRUE; danielebarchiesi@0: break; danielebarchiesi@0: case 'entity:user': danielebarchiesi@0: $plugin = ctools_get_context('user'); danielebarchiesi@0: unset($plugin['no ui']); danielebarchiesi@0: unset($plugin['no required context ui']); danielebarchiesi@0: break; danielebarchiesi@0: } danielebarchiesi@0: danielebarchiesi@0: // Apply restrictions on taxonomy term reverse relationships whose danielebarchiesi@0: // restrictions are in the settings on the field. danielebarchiesi@0: if (!empty($plugin['parent']) && danielebarchiesi@0: $plugin['parent'] == 'entity_from_field' && danielebarchiesi@0: !empty($plugin['reverse']) && danielebarchiesi@0: $plugin['to entity'] == 'taxonomy_term') { danielebarchiesi@0: $field = field_info_field($plugin['field name']); danielebarchiesi@0: if (isset($field['settings']['allowed_values'][0]['vocabulary'])) { danielebarchiesi@0: $plugin['required context']->restrictions = array('vocabulary' => array($field['settings']['allowed_values'][0]['vocabulary'])); danielebarchiesi@0: } danielebarchiesi@0: } danielebarchiesi@0: }