features.ctools.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <?php
  2. function ctools_features_declare_functions($reset = FALSE) {
  3. /**
  4. * This is called by Features to ensure ctools component functions are defined
  5. * Dynamically declare functions under a ctools component's namespace if they are not already declared.
  6. */
  7. if (function_exists('_ctools_features_get_info')) {
  8. foreach (_ctools_features_get_info(NULL, $reset) as $component => $info) {
  9. $code = '';
  10. if (!function_exists("{$info['module']}_features_api")) {
  11. $code .= 'function '. $info['module'] .'_features_api() { return ctools_component_features_api("'. $info['module'] .'"); }';
  12. }
  13. // ctools component with owner defined as "ctools"
  14. if (!function_exists("{$component}_features_api") && $info['module'] === 'ctools') {
  15. $code .= 'function '. $component .'_features_api() { return ctools_component_features_api("'. $component .'"); }';
  16. }
  17. if (!function_exists("{$component}_features_export")) {
  18. $code .= 'function '. $component .'_features_export($data, &$export, $module_name = "") { return ctools_component_features_export("'. $component .'", $data, $export, $module_name); }';
  19. }
  20. if (!function_exists("{$component}_features_export_options")) {
  21. $code .= 'function '. $component .'_features_export_options() { return ctools_component_features_export_options("'. $component .'"); }';
  22. }
  23. if (!function_exists("{$component}_features_export_render")) {
  24. $code .= 'function '. $component .'_features_export_render($module, $data, $export = NULL) { return ctools_component_features_export_render("'. $component .'", $module, $data, $export); }';
  25. }
  26. if (!function_exists("{$component}_features_revert")) {
  27. $code .= 'function '. $component .'_features_revert($module) { return ctools_component_features_revert("'. $component .'", $module); }';
  28. }
  29. eval($code);
  30. }
  31. }
  32. }
  33. /**
  34. * Implements hook_features_api().
  35. */
  36. function ctools_features_api() {
  37. return array(
  38. 'ctools' => array(
  39. 'name' => 'CTools export API',
  40. 'feature_source' => TRUE,
  41. 'duplicates' => FEATURES_DUPLICATES_ALLOWED,
  42. // CTools API integration does not include a default hook declaration as
  43. // it is not a proper default hook.
  44. // 'default_hook' => 'ctools_plugin_api',
  45. ),
  46. );
  47. }
  48. /**
  49. * Implements hook_features_export().
  50. * Adds references to the ctools mothership hook, ctools_plugin_api().
  51. */
  52. function ctools_features_export($data, &$export, $module_name = '') {
  53. // Add ctools dependency
  54. $export['dependencies']['ctools'] = 'ctools';
  55. // Add the actual ctools components which will need to be accounted for in
  56. // hook_ctools_plugin_api(). The components are actually identified by a
  57. // delimited list of values: `module_name:api:current_version`
  58. foreach ($data as $component) {
  59. if ($info = _ctools_features_get_info($component)) {
  60. $identifier = "{$info['module']}:{$info['api']}:{$info['current_version']}";
  61. $export['features']['ctools'][$identifier] = $identifier;
  62. }
  63. }
  64. return array();
  65. }
  66. /**
  67. * Implements hook_features_export_render().
  68. * Adds the ctools mothership hook, ctools_plugin_api().
  69. */
  70. function ctools_features_export_render($module, $data) {
  71. $component_exports = array();
  72. foreach ($data as $component) {
  73. $code = array();
  74. if ($info = _ctools_features_get_info($component)) {
  75. // For background on why we change the output for hook_views_api()
  76. // see http://drupal.org/node/1459120.
  77. if ($info['module'] == 'views') {
  78. $code[] = ' return array("api" => "3.0");';
  79. }
  80. else {
  81. $code[] = ' if ($module == "'. $info['module'] .'" && $api == "'. $info['api'] .'") {';
  82. $code[] = ' return array("version" => "'. $info['current_version'] .'");';
  83. $code[] = ' }';
  84. }
  85. }
  86. ctools_include('plugins');
  87. $plugin_api_hook_name = ctools_plugin_api_get_hook($info['module'], $info['api']);
  88. if (key_exists($plugin_api_hook_name, $component_exports)) {
  89. $component_exports[$plugin_api_hook_name]['code'] .= "\n" . implode("\n", $code);
  90. }
  91. else {
  92. $component_exports[$plugin_api_hook_name] = array(
  93. 'code' => implode("\n", $code),
  94. 'args' => '$module = NULL, $api = NULL',
  95. );
  96. }
  97. }
  98. return $component_exports;
  99. }
  100. /**
  101. * Master implementation of hook_features_api() for all ctools components.
  102. *
  103. * Note that this master hook does not use $component like the others, but uses the
  104. * component module's namespace instead.
  105. */
  106. function ctools_component_features_api($module_name) {
  107. $api = array();
  108. foreach (_ctools_features_get_info() as $component => $info) {
  109. // if module owner is set to "ctools" we need to compare the component
  110. if ($info['module'] == $module_name || ($info['module'] === 'ctools' && $component == $module_name) ) {
  111. $api[$component] = $info;
  112. }
  113. }
  114. return $api;
  115. }
  116. /**
  117. * Master implementation of hook_features_export_options() for all ctools components.
  118. */
  119. function ctools_component_features_export_options($component) {
  120. $options = array();
  121. ctools_include('export');
  122. $schema = ctools_export_get_schema($component);
  123. if ($schema && $schema['export']['bulk export']) {
  124. if (!empty($schema['export']['list callback']) && function_exists($schema['export']['list callback'])) {
  125. $options = $schema['export']['list callback']();
  126. }
  127. else {
  128. $options = _ctools_features_export_default_list($component, $schema);
  129. }
  130. }
  131. asort($options);
  132. return $options;
  133. }
  134. /**
  135. * Master implementation of hook_features_export() for all ctools components.
  136. */
  137. function ctools_component_features_export($component, $data, &$export, $module_name = '') {
  138. // Add the actual implementing module as a dependency
  139. $info = _ctools_features_get_info();
  140. if ($module_name !== $info[$component]['module']) {
  141. $export['dependencies'][$info[$component]['module']] = $info[$component]['module'];
  142. }
  143. // Add the components
  144. foreach ($data as $object_name) {
  145. if ($object = _ctools_features_export_crud_load($component, $object_name)) {
  146. // If this object is provided as a default by a different module, don't
  147. // export and add that module as a dependency instead.
  148. if (!empty($object->export_module) && $object->export_module !== $module_name) {
  149. $export['dependencies'][$object->export_module] = $object->export_module;
  150. if (isset($export['features'][$component][$object_name])) {
  151. unset($export['features'][$component][$object_name]);
  152. }
  153. }
  154. // Otherwise, add the component.
  155. else {
  156. $export['features'][$component][$object_name] = $object_name;
  157. }
  158. }
  159. }
  160. // Let CTools handle API integration for this component.
  161. return array('ctools' => array($component));
  162. }
  163. /**
  164. * Master implementation of hook_features_export_render() for all ctools components.
  165. */
  166. function ctools_component_features_export_render($component, $module, $data) {
  167. // Reset the export display static to prevent clashes.
  168. drupal_static_reset('panels_export_display');
  169. ctools_include('export');
  170. $schema = ctools_export_get_schema($component);
  171. if (function_exists($schema['export']['to hook code callback'])) {
  172. $export = $schema['export']['to hook code callback']($data, $module);
  173. $code = explode("{\n", $export);
  174. array_shift($code);
  175. $code = explode('}', implode($code, "{\n"));
  176. array_pop($code);
  177. $code = implode('}', $code);
  178. }
  179. else {
  180. $code = ' $export = array();'."\n\n";
  181. foreach ($data as $object_name) {
  182. if ($object = _ctools_features_export_crud_load($component, $object_name)) {
  183. $identifier = $schema['export']['identifier'];
  184. $code .= _ctools_features_export_crud_export($component, $object, ' ');
  185. $code .= " \$export[" . ctools_var_export($object_name) . "] = \${$identifier};\n\n";
  186. }
  187. }
  188. $code .= ' return $export;';
  189. }
  190. return array($schema['export']['default hook'] => $code);
  191. }
  192. /**
  193. * Master implementation of hook_features_revert() for all ctools components.
  194. */
  195. function ctools_component_features_revert($component, $module) {
  196. if ($objects = features_get_default($component, $module)) {
  197. foreach ($objects as $name => $object) {
  198. // Some things (like views) do not use the machine name as key
  199. // and need to be loaded explicitly in order to be deleted.
  200. $object = ctools_export_crud_load($component, $name);
  201. if ($object && ($object->export_type & EXPORT_IN_DATABASE)) {
  202. _ctools_features_export_crud_delete($component, $object);
  203. }
  204. }
  205. }
  206. }
  207. /**
  208. * Helper function to return various ctools information for components.
  209. */
  210. function _ctools_features_get_info($identifier = NULL, $reset = FALSE) {
  211. static $components;
  212. if (!isset($components) || $reset) {
  213. $components = array();
  214. $modules = features_get_info();
  215. ctools_include('export');
  216. drupal_static('ctools_export_get_schemas', NULL, $reset);
  217. foreach (ctools_export_get_schemas_by_module() as $module => $schemas) {
  218. foreach ($schemas as $table => $schema) {
  219. if ($schema['export']['bulk export']) {
  220. // Let the API owner take precedence as the owning module.
  221. $api_module = isset($schema['export']['api']['owner']) ? $schema['export']['api']['owner'] : $module;
  222. $components[$table] = array(
  223. 'name' => isset($modules[$api_module]->info['name']) ? $modules[$api_module]->info['name'] : $api_module,
  224. 'default_hook' => $schema['export']['default hook'],
  225. 'default_file' => FEATURES_DEFAULTS_CUSTOM,
  226. 'module' => $api_module,
  227. 'feature_source' => TRUE,
  228. );
  229. if (isset($schema['export']['api'])) {
  230. $components[$table] += array(
  231. 'api' => $schema['export']['api']['api'],
  232. 'default_filename' => $schema['export']['api']['api'],
  233. 'current_version' => $schema['export']['api']['current_version'],
  234. );
  235. }
  236. }
  237. }
  238. }
  239. }
  240. // Return information specific to a particular component.
  241. if (isset($identifier)) {
  242. // Identified by the table name.
  243. if (isset($components[$identifier])) {
  244. return $components[$identifier];
  245. }
  246. // New API identifier. Allows non-exportables related CTools APIs to be
  247. // supported by an explicit `module:api:current_version` key.
  248. else if (substr_count($identifier, ':') === 2) {
  249. list($module, $api, $current_version) = explode(':', $identifier);
  250. // If a schema component matches the provided identifier, provide that
  251. // information. This also ensures that the version number is up to date.
  252. foreach ($components as $table => $info) {
  253. if ($info['module'] == $module && $info['api'] == $api && $info['current_version'] >= $current_version) {
  254. return $info;
  255. }
  256. }
  257. // Fallback to just giving back what was provided to us.
  258. return array('module' => $module, 'api' => $api, 'current_version' => $current_version);
  259. }
  260. return FALSE;
  261. }
  262. return $components;
  263. }
  264. /**
  265. * Wrapper around ctools_export_crud_export() for < 1.7 compatibility.
  266. */
  267. function _ctools_features_export_crud_export($table, $object, $indent = '') {
  268. return ctools_api_version('1.7') ? ctools_export_crud_export($table, $object, $indent) : ctools_export_object($table, $object, $indent);
  269. }
  270. /**
  271. * Wrapper around ctools_export_crud_load() for < 1.7 compatibility.
  272. */
  273. function _ctools_features_export_crud_load($table, $name) {
  274. if (ctools_api_version('1.7')) {
  275. return ctools_export_crud_load($table, $name);
  276. }
  277. elseif ($objects = ctools_export_load_object($table, 'names', array($name))) {
  278. return array_shift($objects);
  279. }
  280. return FALSE;
  281. }
  282. /**
  283. * Wrapper around ctools_export_default_list() for < 1.7 compatibility.
  284. */
  285. function _ctools_features_export_default_list($table, $schema) {
  286. if (ctools_api_version('1.7')) {
  287. return ctools_export_default_list($table, $schema);
  288. }
  289. elseif ($objects = ctools_export_load_object($table, 'all')) {
  290. return drupal_map_assoc(array_keys($objects));
  291. }
  292. return array();
  293. }
  294. /**
  295. * Wrapper around ctools_export_crud_delete() for < 1.7 compatibility.
  296. */
  297. function _ctools_features_export_crud_delete($table, $object) {
  298. if (ctools_api_version('1.7')) {
  299. ctools_export_crud_delete($table, $object);
  300. }
  301. else {
  302. $schema = ctools_export_get_schema($table);
  303. $export = $schema['export'];
  304. db_query("DELETE FROM {{$table}} WHERE {$export['key']} = '%s'", $object->{$export['key']});
  305. }
  306. }
  307. /**
  308. * Implements hook_features_export_render() for page_manager.
  309. */
  310. function page_manager_pages_features_export_render($module, $data) {
  311. // Reset the export display static to prevent clashes.
  312. drupal_static_reset('panels_export_display');
  313. // Ensure that handlers have their code included before exporting.
  314. page_manager_get_tasks();
  315. return ctools_component_features_export_render('page_manager_pages', $module, $data);
  316. }
  317. /**
  318. * Implements hook_features_revert() for page_manager.
  319. */
  320. function page_manager_pages_features_revert($module) {
  321. if ($pages = features_get_default('page_manager_pages', $module)) {
  322. require_once drupal_get_path('module', 'ctools') . '/page_manager/plugins/tasks/page.inc';
  323. foreach ($pages as $page) {
  324. page_manager_page_delete($page);
  325. }
  326. }
  327. }
  328. /**
  329. * Implements hook_features_pipe_COMPONENT_alter() for views_view.
  330. */
  331. function views_features_pipe_views_view_alter(&$pipe, $data, $export) {
  332. // @todo Remove this check before next stable release.
  333. if (!function_exists('views_plugin_list')) {
  334. return;
  335. }
  336. $map = array_flip($data);
  337. foreach (views_plugin_list() as $plugin) {
  338. foreach ($plugin['views'] as $view_name) {
  339. if (isset($map[$view_name])) {
  340. $pipe['dependencies'][$plugin['module']] = $plugin['module'];
  341. }
  342. }
  343. }
  344. }