plugins.inc

  1. cis7 sites/all/modules/ulmus/ctools/includes/plugins.inc
  2. cis7 sites/all/modules/ulmus/views/includes/plugins.inc
  3. cis7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  4. cle7 sites/all/modules/ulmus/ctools/includes/plugins.inc
  5. cle7 sites/all/modules/ulmus/views/includes/plugins.inc
  6. cle7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  7. ecd7 sites/all/modules/ulmus/ctools/includes/plugins.inc
  8. ecd7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  9. ecd7 sites/all/modules/ulmus/views/includes/plugins.inc
  10. elmsmedia7 sites/all/modules/ulmus/ctools/includes/plugins.inc
  11. elmsmedia7 sites/all/modules/ulmus/views/includes/plugins.inc
  12. elmsmedia7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  13. harmony7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  14. harmony7 sites/all/modules/ulmus/views/includes/plugins.inc
  15. harmony7 sites/all/modules/ulmus/ctools/includes/plugins.inc
  16. icor7 sites/all/modules/ulmus/ctools/includes/plugins.inc
  17. icor7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  18. icor7 sites/all/modules/ulmus/views/includes/plugins.inc
  19. meedjum_blog7 sites/all/modules/ulmus/views/includes/plugins.inc
  20. meedjum_blog7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  21. meedjum_blog7 sites/all/modules/ulmus/ctools/includes/plugins.inc
  22. mooc7 sites/all/modules/ulmus/views/includes/plugins.inc
  23. mooc7 sites/all/themes/ulmus/adaptivetheme/at_core/inc/plugins.inc
  24. mooc7 sites/all/modules/ulmus/ctools/includes/plugins.inc

Contains routines to organize and load plugins. It allows a special variation of the hook system so that plugins can be kept in separate .inc files, and can be either loaded all at once or loaded only when necessary.

Functions

Namesort descending Description
ctools_find_base_themes Find all the base themes for the specified theme.
ctools_get_plugins Fetch a group of plugins by name.
ctools_get_plugins_reset Reset all static caches that affect the result of ctools_get_plugins().
ctools_plugin_api_get_hook Find out what hook to use to determine if modules support an API.
ctools_plugin_api_include Load a group of API files.
ctools_plugin_api_info Get an array of information about modules that support an API.
ctools_plugin_get_class Get a class from a plugin, if it exists. If the plugin is not already loaded, try ctools_plugin_load_class() instead.
ctools_plugin_get_directories Get a list of directories to search for plugins of the given type.
ctools_plugin_get_function Get a function from a plugin, if it exists. If the plugin is not already loaded, try ctools_plugin_load_function() instead.
ctools_plugin_get_info Ask a module for info about a particular plugin type.
ctools_plugin_get_plugin_type_info Return the full list of plugin type info for all plugin types registered in the current system.
ctools_plugin_load_class Load a plugin and get a class name from it, returning success only if the class exists.
ctools_plugin_load_function Load a plugin and get a function name from it, returning success only if the function exists.
ctools_plugin_load_hooks Load plugin info for the provided hook; this is handled separately from plugins from files.
ctools_plugin_load_includes Load plugins from a directory.
ctools_plugin_process Process a single hook implementation of a ctools plugin.
ctools_plugin_process_info Process an info file for plugin information, rather than a hook.
ctools_plugin_sort Sort callback for sorting plugins naturally.
_ctools_list_themes Helper function to build a ctools-friendly list of themes capable of providing plugins.
_ctools_process_data Fill in default values and run hooks for data loaded for one or more plugins.

File

sites/all/modules/ulmus/ctools/includes/plugins.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. *
  5. * Contains routines to organize and load plugins. It allows a special
  6. * variation of the hook system so that plugins can be kept in separate
  7. * .inc files, and can be either loaded all at once or loaded only when
  8. * necessary.
  9. */
  10. /**
  11. * Get an array of information about modules that support an API.
  12. *
  13. * This will ask each module if they support the given API, and if they do
  14. * it will return an array of information about the modules that do.
  15. *
  16. * This function invokes hook_ctools_api. This invocation is statically
  17. * cached, so feel free to call it as often per page run as you like, it
  18. * will cost very little.
  19. *
  20. * This function can be used as an alternative to module_implements and can
  21. * thus be used to find a precise list of modules that not only support
  22. * a given hook (aka 'api') but also restrict to only modules that use
  23. * the given version. This will allow multiple modules moving at different
  24. * paces to still be able to work together and, in the event of a mismatch,
  25. * either fall back to older behaviors or simply cease loading, which is
  26. * still better than a crash.
  27. *
  28. * @param $owner
  29. * The name of the module that controls the API.
  30. * @param $api
  31. * The name of the api. The api name forms the file name:
  32. * $module.$api.inc
  33. * @param $minimum_version
  34. * The lowest version API that is compatible with this one. If a module
  35. * reports its API as older than this, its files will not be loaded. This
  36. * should never change during operation.
  37. * @param $current_version
  38. * The current version of the api. If a module reports its minimum API as
  39. * higher than this, its files will not be loaded. This should never change
  40. * during operation.
  41. *
  42. * @return
  43. * An array of API information, keyed by module. Each module's information will
  44. * contain:
  45. * - 'version': The version of the API required by the module. The module
  46. * should use the lowest number it can support so that the widest range
  47. * of supported versions can be used.
  48. * - 'path': If not provided, this will be the module's path. This is
  49. * where the module will store any subsidiary files. This differs from
  50. * plugin paths which are figured separately.
  51. *
  52. * APIs can request any other information to be placed here that they might
  53. * need. This should be in the documentation for that particular API.
  54. */
  55. function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version) {
  56. $cache = &drupal_static(__FUNCTION__, array());
  57. if (!isset($cache[$owner][$api])) {
  58. $cache[$owner][$api] = array();
  59. $hook = ctools_plugin_api_get_hook($owner, $api);
  60. foreach (module_implements($hook) as $module) {
  61. $function = $module . '_' . $hook;
  62. $info = $function($owner, $api);
  63. $version = NULL;
  64. // This is added to make hook_views_api() compatible with this, since
  65. // views used a different version key.
  66. if (isset($info['version'])) {
  67. $version = $info['version'];
  68. }
  69. else if (isset($info['api'])) {
  70. $version = $info['api'];
  71. }
  72. if (!isset($version)) {
  73. continue;
  74. }
  75. // Only process if version is between minimum and current, inclusive.
  76. if (version_compare($version, $minimum_version, '>=') && version_compare($version, $current_version, '<=')) {
  77. if (!isset($info['path'])) {
  78. $info['path'] = drupal_get_path('module', $module);
  79. }
  80. $cache[$owner][$api][$module] = $info;
  81. }
  82. }
  83. // And allow themes to implement these as well.
  84. $themes = _ctools_list_themes();
  85. foreach ($themes as $name => $theme) {
  86. if (!empty($theme->info['api'][$owner][$api])) {
  87. $info = $theme->info['api'][$owner][$api];
  88. if (!isset($info['version'])) {
  89. continue;
  90. }
  91. // Only process if version is between minimum and current, inclusive.
  92. if (version_compare($info['version'], $minimum_version, '>=') && version_compare($info['version'], $current_version, '<=')) {
  93. if (!isset($info['path'])) {
  94. $info['path'] = '';
  95. }
  96. // Because themes can't easily specify full path, we add it here
  97. // even though we do not for modules:
  98. $info['path'] = drupal_get_path('theme', $name) . '/' . $info['path'];
  99. $cache[$owner][$api][$name] = $info;
  100. }
  101. }
  102. }
  103. // Allow other modules to hook in.
  104. drupal_alter($hook, $cache[$owner][$api]);
  105. }
  106. return $cache[$owner][$api];
  107. }
  108. /**
  109. * Load a group of API files.
  110. *
  111. * This will ask each module if they support the given API, and if they do
  112. * it will load the specified file name. The API and the file name
  113. * coincide by design.
  114. *
  115. * @param $owner
  116. * The name of the module that controls the API.
  117. * @param $api
  118. * The name of the api. The api name forms the file name:
  119. * $module.$api.inc, though this can be overridden by the module's response.
  120. * @param $minimum_version
  121. * The lowest version API that is compatible with this one. If a module
  122. * reports its API as older than this, its files will not be loaded. This
  123. * should never change during operation.
  124. * @param $current_version
  125. * The current version of the api. If a module reports its minimum API as
  126. * higher than this, its files will not be loaded. This should never change
  127. * during operation.
  128. *
  129. * @return
  130. * The API information, in case you need it.
  131. */
  132. function ctools_plugin_api_include($owner, $api, $minimum_version, $current_version) {
  133. static $already_done = array();
  134. $info = ctools_plugin_api_info($owner, $api, $minimum_version, $current_version);
  135. foreach ($info as $module => $plugin_info) {
  136. if (!isset($already_done[$owner][$api][$module])) {
  137. if (isset($plugin_info["$api file"])) {
  138. $file = $plugin_info["$api file"];
  139. }
  140. else if (isset($plugin_info['file'])) {
  141. $file = $plugin_info['file'];
  142. }
  143. else {
  144. $file = "$module.$api.inc";
  145. }
  146. if (file_exists(DRUPAL_ROOT . "/$plugin_info[path]/$file")) {
  147. require_once DRUPAL_ROOT . "/$plugin_info[path]/$file";
  148. }
  149. else if (file_exists(DRUPAL_ROOT . "/$file")) {
  150. require_once DRUPAL_ROOT . "/$file";
  151. }
  152. $already_done[$owner][$api][$module] = TRUE;
  153. }
  154. }
  155. return $info;
  156. }
  157. /**
  158. * Find out what hook to use to determine if modules support an API.
  159. *
  160. * By default, most APIs will use hook_ctools_plugin_api, but some modules
  161. * want sole ownership. This technique lets modules define what hook
  162. * to use.
  163. */
  164. function ctools_plugin_api_get_hook($owner, $api) {
  165. // Allow modules to use their own hook for this. The only easy way to do
  166. // this right now is with a magically named function.
  167. if (function_exists($function = $owner . '_' . $api . '_hook_name')) {
  168. $hook = $function();
  169. }
  170. else if (function_exists($function = $owner . '_ctools_plugin_api_hook_name')) {
  171. $hook = $function();
  172. }
  173. // Do this last so that if the $function above failed to return, we have a
  174. // sane default.
  175. if (empty($hook)) {
  176. $hook = 'ctools_plugin_api';
  177. }
  178. return $hook;
  179. }
  180. /**
  181. * Fetch a group of plugins by name.
  182. *
  183. * @param $module
  184. * The name of the module that utilizes this plugin system. It will be
  185. * used to call hook_ctools_plugin_$plugin() to get more data about the plugin.
  186. * @param $type
  187. * The type identifier of the plugin.
  188. * @param $id
  189. * If specified, return only information about plugin with this identifier.
  190. * The system will do its utmost to load only plugins with this id.
  191. *
  192. * @return
  193. * An array of information arrays about the plugins received. The contents
  194. * of the array are specific to the plugin.
  195. */
  196. function ctools_get_plugins($module, $type, $id = NULL) {
  197. // Store local caches of plugins and plugin info so we don't have to do full
  198. // lookups everytime.
  199. $plugins = &drupal_static('ctools_plugins', array());
  200. $info = ctools_plugin_get_plugin_type_info();
  201. // Bail out noisily if an invalid module/type combination is requested.
  202. if (!isset($info[$module][$type])) {
  203. watchdog('ctools', 'Invalid plugin module/type combination requested: module @module and type @type', array('@module' => $module, '@type' => $type), WATCHDOG_ERROR);
  204. return array();
  205. }
  206. // Make sure our plugins array is populated.
  207. if (!isset($plugins[$module][$type])) {
  208. $plugins[$module][$type] = array();
  209. }
  210. // Attempt to shortcut this whole piece of code if we already have
  211. // the requested plugin:
  212. if ($id && array_key_exists($id, $plugins[$module][$type])) {
  213. return $plugins[$module][$type][$id];
  214. }
  215. // Store the status of plugin loading. If a module plugin type pair is true,
  216. // then it is fully loaded and no searching or setup needs to be done.
  217. $setup = &drupal_static('ctools_plugin_setup', array());
  218. // We assume we don't need to build a cache.
  219. $build_cache = FALSE;
  220. // If the plugin info says this can be cached, check cache first.
  221. if ($info[$module][$type]['cache'] && empty($setup[$module][$type])) {
  222. $cache = cache_get("plugins:$module:$type", $info[$module][$type]['cache table']);
  223. if (!empty($cache->data)) {
  224. // Cache load succeeded so use the cached plugin list.
  225. $plugins[$module][$type] = $cache->data;
  226. // Set $setup to true so we know things where loaded.
  227. $setup[$module][$type] = TRUE;
  228. }
  229. else {
  230. // Cache load failed so store that we need to build and write the cache.
  231. $build_cache = TRUE;
  232. }
  233. }
  234. // Always load all hooks if we need them. Note we only need them now if the
  235. // plugin asks for them. We can assume that if we have plugins we've already
  236. // called the global hook.
  237. if (!empty($info[$module][$type]['use hooks']) && empty($plugins[$module][$type])) {
  238. $plugins[$module][$type] = ctools_plugin_load_hooks($info[$module][$type]);
  239. }
  240. // Then see if we should load all files. We only do this if we
  241. // want a list of all plugins or there was a cache miss.
  242. if (empty($setup[$module][$type]) && ($build_cache || !$id)) {
  243. $setup[$module][$type] = TRUE;
  244. $plugins[$module][$type] = array_merge($plugins[$module][$type], ctools_plugin_load_includes($info[$module][$type]));
  245. // If the plugin can have child plugins, and we're loading all plugins,
  246. // go through the list of plugins we have and find child plugins.
  247. if (!$id && !empty($info[$module][$type]['child plugins'])) {
  248. // If a plugin supports children, go through each plugin and ask.
  249. $temp = array();
  250. foreach ($plugins[$module][$type] as $name => $plugin) {
  251. // The strpos ensures that we don't try to find children for plugins
  252. // that are already children.
  253. if (!empty($plugin['get children']) && function_exists($plugin['get children']) && strpos($name, ':') === FALSE) {
  254. $temp = array_merge($plugin['get children']($plugin, $name), $temp);
  255. }
  256. else {
  257. $temp[$name] = $plugin;
  258. }
  259. }
  260. $plugins[$module][$type] = $temp;
  261. }
  262. }
  263. // If we were told earlier that this is cacheable and the cache was
  264. // empty, give something back.
  265. if ($build_cache) {
  266. cache_set("plugins:$module:$type", $plugins[$module][$type], $info[$module][$type]['cache table']);
  267. }
  268. // If no id was requested, we are finished here:
  269. if (!$id) {
  270. // Use array_filter because looking for unknown plugins could cause NULL
  271. // entries to appear in the list later.
  272. return array_filter($plugins[$module][$type]);
  273. }
  274. // Check to see if we need to look for the file
  275. if (!array_key_exists($id, $plugins[$module][$type])) {
  276. // If we can have child plugins, check to see if the plugin name is in the
  277. // format of parent:child and break it up if it is.
  278. if (!empty($info[$module][$type]['child plugins']) && strpos($id, ':') !== FALSE) {
  279. list($parent, $child) = explode(':', $id, 2);
  280. }
  281. else {
  282. $parent = $id;
  283. }
  284. if (!array_key_exists($parent, $plugins[$module][$type])) {
  285. $result = ctools_plugin_load_includes($info[$module][$type], $parent);
  286. // Set to either what was returned or NULL.
  287. $plugins[$module][$type][$parent] = isset($result[$parent]) ? $result[$parent] : NULL;
  288. }
  289. // If we are looking for a child, and have the parent, ask the parent for the child.
  290. if (!empty($child) && !empty($plugins[$module][$type][$parent]) && function_exists($plugins[$module][$type][$parent]['get child'])) {
  291. $plugins[$module][$type][$id] = $plugins[$module][$type][$parent]['get child']($plugins[$module][$type][$parent], $parent, $child);
  292. }
  293. }
  294. // At this point we should either have the plugin, or a NULL.
  295. return $plugins[$module][$type][$id];
  296. }
  297. /**
  298. * Return the full list of plugin type info for all plugin types registered in
  299. * the current system.
  300. *
  301. * This function manages its own cache getting/setting, and should always be
  302. * used as the way to initially populate the list of plugin types. Make sure you
  303. * call this function to properly populate the ctools_plugin_type_info static
  304. * variable.
  305. *
  306. * @return array
  307. * A multilevel array of plugin type info, the outer array keyed on module
  308. * name and each inner array keyed on plugin type name.
  309. */
  310. function ctools_plugin_get_plugin_type_info($flush = FALSE) {
  311. static $drupal_static_fast;
  312. if (!isset($drupal_static_fast)) {
  313. $drupal_static_fast['info_loaded'] = &drupal_static('ctools_plugin_type_info_loaded', FALSE);
  314. $drupal_static_fast['all_type_info'] = &drupal_static('ctools_plugin_type_info', array());
  315. }
  316. $info_loaded = &$drupal_static_fast['info_loaded'];
  317. $all_type_info = &$drupal_static_fast['all_type_info'];
  318. // Only trigger info loading once.
  319. if ($info_loaded && !$flush) {
  320. return $all_type_info;
  321. }
  322. $info_loaded = TRUE;
  323. $cache = cache_get('ctools_plugin_type_info');
  324. if (!empty($cache->data) && !$flush) {
  325. // Plugin type info cache is warm, use it.
  326. $all_type_info = $cache->data;
  327. }
  328. else {
  329. // Cache expired, refill it.
  330. foreach (module_implements('ctools_plugin_type') as $module) {
  331. $module_infos = array();
  332. $function = $module . '_ctools_plugin_type';
  333. $module_infos = $function();
  334. foreach ($module_infos as $plugin_type_name => $plugin_type_info) {
  335. // Apply defaults. Array addition will not overwrite pre-existing keys.
  336. $plugin_type_info += array(
  337. 'module' => $module,
  338. 'type' => $plugin_type_name,
  339. 'cache' => FALSE,
  340. 'cache table' => 'cache',
  341. 'classes' => array(),
  342. 'use hooks' => FALSE,
  343. 'defaults' => array(),
  344. 'process' => '',
  345. 'alterable' => TRUE,
  346. 'extension' => 'inc',
  347. 'info file' => FALSE,
  348. 'hook' => $module . '_' . $plugin_type_name,
  349. 'load themes' => FALSE,
  350. );
  351. $all_type_info[$module][$plugin_type_name] = $plugin_type_info;
  352. }
  353. }
  354. cache_set('ctools_plugin_type_info', $all_type_info);
  355. }
  356. return $all_type_info;
  357. }
  358. /**
  359. * Reset all static caches that affect the result of ctools_get_plugins().
  360. */
  361. function ctools_get_plugins_reset() {
  362. drupal_static_reset('ctools_plugins');
  363. drupal_static_reset('ctools_plugin_setup');
  364. drupal_static_reset('ctools_plugin_load_includes');
  365. drupal_static_reset('ctools_plugin_api_info');
  366. }
  367. /**
  368. * Load plugins from a directory.
  369. *
  370. * @param $info
  371. * The plugin info as returned by ctools_plugin_get_info()
  372. * @param $file
  373. * The file to load if we're looking for just one particular plugin.
  374. *
  375. * @return
  376. * An array of information created for this plugin.
  377. */
  378. function ctools_plugin_load_includes($info, $filename = NULL) {
  379. // Keep a static array so we don't hit file_scan_directory more than necessary.
  380. $all_files = &drupal_static(__FUNCTION__, array());
  381. // store static of plugin arrays for reference because they can't be reincluded.
  382. static $plugin_arrays = array();
  383. // If we're being asked for all plugins of a type, skip any caching
  384. // we may have done because this is an admin task and it's ok to
  385. // spend the extra time.
  386. if (!isset($filename)) {
  387. $all_files[$info['module']][$info['type']] = NULL;
  388. }
  389. if (!isset($all_files[$info['module']][$info['type']])) {
  390. // If a filename was set, we will try to load our list of files from
  391. // cache. This is considered normal operation and we try to reduce
  392. // the time spent finding files.
  393. if (isset($filename)) {
  394. $cache = cache_get("ctools_plugin_files:$info[module]:$info[type]");
  395. if ($cache) {
  396. $all_files[$info['module']][$info['type']] = $cache->data;
  397. }
  398. }
  399. if (!isset($all_files[$info['module']][$info['type']])) {
  400. $all_files[$info['module']][$info['type']] = array();
  401. // Load all our plugins.
  402. $directories = ctools_plugin_get_directories($info);
  403. $extension = (empty($info['info file']) || ($info['extension'] != 'inc')) ? $info['extension'] : 'info';
  404. foreach ($directories as $module => $path) {
  405. $all_files[$info['module']][$info['type']][$module] = file_scan_directory($path, '/\.' . $extension . '$/', array('key' => 'name'));
  406. }
  407. cache_set("ctools_plugin_files:$info[module]:$info[type]", $all_files[$info['module']][$info['type']]);
  408. }
  409. }
  410. $file_list = $all_files[$info['module']][$info['type']];
  411. $plugins = array();
  412. // Iterate through all the plugin .inc files, load them and process the hook
  413. // that should now be available.
  414. foreach (array_filter($file_list) as $module => $files) {
  415. if ($filename) {
  416. $files = isset($files[$filename]) ? array($filename => $files[$filename]) : array();
  417. }
  418. foreach ($files as $file) {
  419. if (!empty($info['info file'])) {
  420. // Parse a .info file
  421. $result = ctools_plugin_process_info($info, $module, $file);
  422. }
  423. else {
  424. // Parse a hook.
  425. $plugin = NULL; // ensure that we don't have something leftover from earlier.
  426. if (isset($plugin_arrays[$file->uri])) {
  427. $identifier = $plugin_arrays[$file->uri];
  428. }
  429. else {
  430. require_once DRUPAL_ROOT . '/' . $file->uri;
  431. // .inc files have a special format for the hook identifier.
  432. // For example, 'foo.inc' in the module 'mogul' using the plugin
  433. // whose hook is named 'borg_type' should have a function named (deep breath)
  434. // mogul_foo_borg_type()
  435. // If, however, the .inc file set the quasi-global $plugin array, we
  436. // can use that and not even call a function. Set the $identifier
  437. // appropriately and ctools_plugin_process() will handle it.
  438. if (isset($plugin)) {
  439. $plugin_arrays[$file->uri] = $plugin;
  440. $identifier = $plugin;
  441. }
  442. else {
  443. $identifier = $module . '_' . $file->name;
  444. }
  445. }
  446. $result = ctools_plugin_process($info, $module, $identifier, dirname($file->uri), basename($file->uri), $file->name);
  447. }
  448. if (is_array($result)) {
  449. $plugins = array_merge($plugins, $result);
  450. }
  451. }
  452. }
  453. return $plugins;
  454. }
  455. /**
  456. * Get a list of directories to search for plugins of the given type.
  457. *
  458. * This utilizes hook_ctools_plugin_directory() to determine a complete list of
  459. * directories. Only modules that implement this hook and return a string
  460. * value will have their directories included.
  461. *
  462. * @param $info
  463. * The $info array for the plugin as returned by ctools_plugin_get_info().
  464. *
  465. * @return array $directories
  466. * An array of directories to search.
  467. */
  468. function ctools_plugin_get_directories($info) {
  469. $directories = array();
  470. foreach (module_implements('ctools_plugin_directory') as $module) {
  471. $function = $module . '_ctools_plugin_directory';
  472. $result = $function($info['module'], $info['type']);
  473. if ($result && is_string($result)) {
  474. $directories[$module] = drupal_get_path('module', $module) . '/' . $result;
  475. }
  476. }
  477. if (!empty($info['load themes'])) {
  478. $themes = _ctools_list_themes();
  479. foreach ($themes as $name => $theme) {
  480. if (!empty($theme->info['plugins'][$info['module']][$info['type']])) {
  481. $directories[$name] = drupal_get_path('theme', $name) . '/' . $theme->info['plugins'][$info['module']][$info['type']];
  482. }
  483. }
  484. }
  485. return $directories;
  486. }
  487. /**
  488. * Helper function to build a ctools-friendly list of themes capable of
  489. * providing plugins.
  490. *
  491. * @return array $themes
  492. * A list of themes that can act as plugin providers, sorted parent-first with
  493. * the active theme placed last.
  494. */
  495. function _ctools_list_themes() {
  496. static $themes;
  497. if (is_null($themes)) {
  498. $current = variable_get('theme_default', FALSE);
  499. $themes = $active = array();
  500. $all_themes = list_themes();
  501. foreach ($all_themes as $name => $theme) {
  502. // Only search from active themes
  503. if (empty($theme->status) && $theme->name != $current) {
  504. continue;
  505. }
  506. $active[$name] = $theme;
  507. // Prior to drupal 6.14, $theme->base_themes does not exist. Build it.
  508. if (!isset($theme->base_themes) && !empty($theme->base_theme)) {
  509. $active[$name]->base_themes = ctools_find_base_themes($all_themes, $name);
  510. }
  511. }
  512. // Construct a parent-first list of all themes
  513. foreach ($active as $name => $theme) {
  514. $base_themes = isset($theme->base_themes) ? $theme->base_themes : array();
  515. $themes = array_merge($themes, $base_themes, array($name => $theme->info['name']));
  516. }
  517. // Put the actual theme info objects into the array
  518. foreach (array_keys($themes) as $name) {
  519. if (isset($all_themes[$name])) {
  520. $themes[$name] = $all_themes[$name];
  521. }
  522. }
  523. // Make sure the current default theme always gets the last word
  524. if ($current_key = array_search($current, array_keys($themes))) {
  525. $themes += array_splice($themes, $current_key, 1);
  526. }
  527. }
  528. return $themes;
  529. }
  530. /**
  531. * Find all the base themes for the specified theme.
  532. *
  533. * Themes can inherit templates and function implementations from earlier themes.
  534. *
  535. * NOTE: this is a verbatim copy of system_find_base_themes(), which was not
  536. * implemented until 6.14. It is included here only as a fallback for outdated
  537. * versions of drupal core.
  538. *
  539. * @param $themes
  540. * An array of available themes.
  541. * @param $key
  542. * The name of the theme whose base we are looking for.
  543. * @param $used_keys
  544. * A recursion parameter preventing endless loops.
  545. * @return
  546. * Returns an array of all of the theme's ancestors; the first element's value
  547. * will be NULL if an error occurred.
  548. */
  549. function ctools_find_base_themes($themes, $key, $used_keys = array()) {
  550. $base_key = $themes[$key]->info['base theme'];
  551. // Does the base theme exist?
  552. if (!isset($themes[$base_key])) {
  553. return array($base_key => NULL);
  554. }
  555. $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
  556. // Is the base theme itself a child of another theme?
  557. if (isset($themes[$base_key]->info['base theme'])) {
  558. // Do we already know the base themes of this theme?
  559. if (isset($themes[$base_key]->base_themes)) {
  560. return $themes[$base_key]->base_themes + $current_base_theme;
  561. }
  562. // Prevent loops.
  563. if (!empty($used_keys[$base_key])) {
  564. return array($base_key => NULL);
  565. }
  566. $used_keys[$base_key] = TRUE;
  567. return ctools_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
  568. }
  569. // If we get here, then this is our parent theme.
  570. return $current_base_theme;
  571. }
  572. /**
  573. * Load plugin info for the provided hook; this is handled separately from
  574. * plugins from files.
  575. *
  576. * @param $info
  577. * The info array about the plugin as created by ctools_plugin_get_info()
  578. *
  579. * @return
  580. * An array of info supplied by any hook implementations.
  581. */
  582. function ctools_plugin_load_hooks($info) {
  583. $hooks = array();
  584. foreach (module_implements($info['hook']) as $module) {
  585. $result = ctools_plugin_process($info, $module, $module, drupal_get_path('module', $module));
  586. if (is_array($result)) {
  587. $hooks = array_merge($hooks, $result);
  588. }
  589. }
  590. return $hooks;
  591. }
  592. /**
  593. * Process a single hook implementation of a ctools plugin.
  594. *
  595. * @param $info
  596. * The $info array about the plugin as returned by ctools_plugin_get_info()
  597. * @param $module
  598. * The module that implements the plugin being processed.
  599. * @param $identifier
  600. * The plugin identifier, which is used to create the name of the hook
  601. * function being called.
  602. * @param $path
  603. * The path where files utilized by this plugin will be found.
  604. * @param $file
  605. * The file that was loaded for this plugin, if it exists.
  606. * @param $base
  607. * The base plugin name to use. If a file was loaded for the plugin, this
  608. * is the plugin to assume must be present. This is used to automatically
  609. * translate the array to make the syntax more friendly to plugin
  610. * implementors.
  611. */
  612. function ctools_plugin_process($info, $module, $identifier, $path, $file = NULL, $base = NULL) {
  613. if (is_array($identifier)) {
  614. $result = $identifier;
  615. }
  616. else {
  617. $function = $identifier . '_' . $info['hook'];
  618. if (!function_exists($function)) {
  619. return NULL;
  620. }
  621. $result = $function();
  622. if (!isset($result) || !is_array($result)) {
  623. return NULL;
  624. }
  625. }
  626. // Automatically convert to the proper format that lets plugin implementations
  627. // not nest arrays as deeply as they used to, but still support the older
  628. // format where they do:
  629. if ($base && (!isset($result[$base]) || !is_array($result[$base]))) {
  630. $result = array($base => $result);
  631. }
  632. return _ctools_process_data($result, $info, $module, $path, $file);
  633. }
  634. /**
  635. * Fill in default values and run hooks for data loaded for one or
  636. * more plugins.
  637. */
  638. function _ctools_process_data($result, $plugin_type_info, $module, $path, $file) {
  639. // Fill in global defaults.
  640. foreach ($result as $name => $plugin) {
  641. $result[$name] += array(
  642. 'module' => $module,
  643. 'name' => $name,
  644. 'path' => $path,
  645. 'file' => $file,
  646. 'plugin module' => $plugin_type_info['module'],
  647. 'plugin type' => $plugin_type_info['type'],
  648. );
  649. // Fill in plugin-specific defaults, if they exist.
  650. if (!empty($plugin_type_info['defaults'])) {
  651. if (is_array($plugin_type_info['defaults'])) {
  652. $result[$name] += $plugin_type_info['defaults'];
  653. }
  654. }
  655. // Allow the plugin to be altered before processing.
  656. if (!empty($plugin_type_info['alterable']) && $plugin_type_info['alterable']) {
  657. drupal_alter('ctools_plugin_pre', $result[$name], $plugin_type_info);
  658. }
  659. // Allow the plugin owner to do additional processing.
  660. if (!empty($plugin_type_info['process']) && $function = ctools_plugin_get_function($plugin_type_info, 'process')) {
  661. $function($result[$name], $plugin_type_info);
  662. }
  663. // Allow the plugin to be altered after processing.
  664. if (!empty($plugin_type_info['alterable']) && $plugin_type_info['alterable']) {
  665. drupal_alter('ctools_plugin_post', $result[$name], $plugin_type_info);
  666. }
  667. }
  668. return $result;
  669. }
  670. /**
  671. * Process an info file for plugin information, rather than a hook.
  672. */
  673. function ctools_plugin_process_info($info, $module, $file) {
  674. $result = drupal_parse_info_file($file->uri);
  675. if ($result) {
  676. $result = array($file->name => $result);
  677. return _ctools_process_data($result, $info, $module, dirname($file->uri), basename($file->uri));
  678. }
  679. }
  680. /**
  681. * Ask a module for info about a particular plugin type.
  682. */
  683. function ctools_plugin_get_info($module, $type) {
  684. $all_info = ctools_plugin_get_plugin_type_info();
  685. return isset($all_info[$module][$type]) ? $all_info[$module][$type] : array();
  686. }
  687. /**
  688. * Get a function from a plugin, if it exists. If the plugin is not already
  689. * loaded, try ctools_plugin_load_function() instead.
  690. *
  691. * @param $plugin_definition
  692. * The loaded plugin type.
  693. * @param $function_name
  694. * The identifier of the function. For example, 'settings form'.
  695. *
  696. * @return
  697. * The actual name of the function to call, or NULL if the function
  698. * does not exist.
  699. */
  700. function ctools_plugin_get_function($plugin_definition, $function_name) {
  701. // If cached the .inc file may not have been loaded. require_once is quite safe
  702. // and fast so it's okay to keep calling it.
  703. if (isset($plugin_definition['file'])) {
  704. // Plugins that are loaded from info files have the info file as
  705. // $plugin['file']. Don't try to run those.
  706. $info = ctools_plugin_get_info($plugin_definition['plugin module'], $plugin_definition['plugin type']);
  707. if (empty($info['info file'])) {
  708. require_once DRUPAL_ROOT . '/' . $plugin_definition['path'] . '/' . $plugin_definition['file'];
  709. }
  710. }
  711. if (!isset($plugin_definition[$function_name])) {
  712. return;
  713. }
  714. if (is_array($plugin_definition[$function_name]) && isset($plugin_definition[$function_name]['function'])) {
  715. $function = $plugin_definition[$function_name]['function'];
  716. if (isset($plugin_definition[$function_name]['file'])) {
  717. $file = $plugin_definition[$function_name]['file'];
  718. if (isset($plugin_definition[$function_name]['path'])) {
  719. $file = $plugin_definition[$function_name]['path'] . '/' . $file;
  720. }
  721. require_once DRUPAL_ROOT . '/' . $file;
  722. }
  723. }
  724. else {
  725. $function = $plugin_definition[$function_name];
  726. }
  727. if (function_exists($function)) {
  728. return $function;
  729. }
  730. }
  731. /**
  732. * Load a plugin and get a function name from it, returning success only
  733. * if the function exists.
  734. *
  735. * @param $module
  736. * The module that owns the plugin type.
  737. * @param $type
  738. * The type of plugin.
  739. * @param $id
  740. * The id of the specific plugin to load.
  741. * @param $function_name
  742. * The identifier of the function. For example, 'settings form'.
  743. *
  744. * @return
  745. * The actual name of the function to call, or NULL if the function
  746. * does not exist.
  747. */
  748. function ctools_plugin_load_function($module, $type, $id, $function_name) {
  749. $plugin = ctools_get_plugins($module, $type, $id);
  750. return ctools_plugin_get_function($plugin, $function_name);
  751. }
  752. /**
  753. * Get a class from a plugin, if it exists. If the plugin is not already
  754. * loaded, try ctools_plugin_load_class() instead.
  755. *
  756. * @param $plugin_definition
  757. * The loaded plugin type.
  758. * @param $class_name
  759. * The identifier of the class. For example, 'handler'.
  760. *
  761. * @return
  762. * The actual name of the class to call, or NULL if the class does not exist.
  763. */
  764. function ctools_plugin_get_class($plugin_definition, $class_name) {
  765. // If cached the .inc file may not have been loaded. require_once is quite safe
  766. // and fast so it's okay to keep calling it.
  767. if (isset($plugin_definition['file'])) {
  768. // Plugins that are loaded from info files have the info file as
  769. // $plugin['file']. Don't try to run those.
  770. $info = ctools_plugin_get_info($plugin_definition['plugin module'], $plugin_definition['plugin type']);
  771. if (empty($info['info file'])) {
  772. require_once DRUPAL_ROOT . '/' . $plugin_definition['path'] . '/' . $plugin_definition['file'];
  773. }
  774. }
  775. $return = FALSE;
  776. if (!isset($plugin_definition[$class_name])) {
  777. return;
  778. }
  779. else if (is_string($plugin_definition[$class_name])) {
  780. // Plugin uses the string form shorthand.
  781. $return = $plugin_definition[$class_name];
  782. }
  783. else if (isset($plugin_definition[$class_name]['class'])) {
  784. // Plugin uses the verbose array form.
  785. $return = $plugin_definition[$class_name]['class'];
  786. }
  787. // @todo consider adding an else {watchdog(...)} here
  788. return ($return && class_exists($return)) ? $return : NULL;
  789. }
  790. /**
  791. * Load a plugin and get a class name from it, returning success only if the
  792. * class exists.
  793. *
  794. * @param $module
  795. * The module that owns the plugin type.
  796. * @param $type
  797. * The type of plugin.
  798. * @param $id
  799. * The id of the specific plugin to load.
  800. * @param $class_name
  801. * The identifier of the class. For example, 'handler'.
  802. *
  803. * @return
  804. * The actual name of the class to call, or NULL if the class does not exist.
  805. */
  806. function ctools_plugin_load_class($module, $type, $id, $class_name) {
  807. $plugin = ctools_get_plugins($module, $type, $id);
  808. return ctools_plugin_get_class($plugin, $class_name);
  809. }
  810. /**
  811. * Sort callback for sorting plugins naturally.
  812. *
  813. * Sort first by weight, then by title.
  814. */
  815. function ctools_plugin_sort($a, $b) {
  816. if (is_object($a)) {
  817. $a = (array) $a;
  818. }
  819. if (is_object($b)) {
  820. $b = (array) $b;
  821. }
  822. if (empty($a['weight'])) {
  823. $a['weight'] = 0;
  824. }
  825. if (empty($b['weight'])) {
  826. $b['weight'] = 0;
  827. }
  828. if ($a['weight'] == $b['weight']) {
  829. return strnatcmp(strtolower($a['title']), strtolower($b['title']));
  830. }
  831. return ($a['weight'] < $b['weight']) ? -1 : 1;
  832. }