fe_block.module 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128
  1. <?php
  2. /**
  3. * @file
  4. * Provide features components for exporting core blocks and settings.
  5. */
  6. /**
  7. * Version number for the current fe_block export definition.
  8. */
  9. define('FE_BLOCK_VERSION', '2.0');
  10. /**
  11. * Implements hook_features_api().
  12. */
  13. function fe_block_features_api() {
  14. $info = array();
  15. $key = 'fe_block_settings';
  16. $info[$key] = array(
  17. 'name' => t('Block settings'),
  18. 'feature_source' => TRUE,
  19. 'default_hook' => 'default_' . $key,
  20. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  21. );
  22. $key = 'fe_block_boxes';
  23. $info[$key] = array(
  24. 'name' => t('Block contents (boxes)'),
  25. 'feature_source' => TRUE,
  26. 'default_hook' => 'default_' . $key,
  27. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  28. );
  29. return $info;
  30. }
  31. /**
  32. * Implements hook_features_export_options().
  33. */
  34. function fe_block_settings_features_export_options() {
  35. $options = array();
  36. $blocks = _fe_block_get_blocks();
  37. usort($blocks, '_fe_block_compare');
  38. foreach ($blocks as $block) {
  39. // @see features.block.inc
  40. if (strpos($block['module'], '-') !== FALSE) {
  41. continue;
  42. }
  43. $block_id = _fe_block_build_id($block);
  44. if (empty($block_id)) {
  45. continue;
  46. }
  47. $options[$block_id] = '[' . $block_id . '] ' . $block['info'];
  48. }
  49. return $options;
  50. }
  51. /**
  52. * Implements hook_features_export().
  53. */
  54. function fe_block_settings_features_export($data, &$export, $module_name = '') {
  55. $pipe = array();
  56. $export['dependencies']['fe_block'] = 'fe_block';
  57. $component = 'fe_block_settings';
  58. // Add the components.
  59. foreach ($data as $object_name) {
  60. $export['features'][$component][$object_name] = $object_name;
  61. // Boxes.
  62. if (strpos($object_name, 'block-') === 0) {
  63. $machine_name = substr($object_name, strlen('block-'));
  64. $pipe['fe_block_boxes'][$machine_name] = $machine_name;
  65. }
  66. // @todo Export menu blocks.
  67. // Others.
  68. else {
  69. $pipe['block'][$object_name] = $object_name;
  70. }
  71. }
  72. return $pipe;
  73. }
  74. /**
  75. * Implements hook_features_export_render().
  76. */
  77. function fe_block_settings_features_export_render($module_name = '', $data) {
  78. $code = array();
  79. $code[] = ' $export = array();';
  80. $code[] = '';
  81. // The way the blocks are exported has changed throughout the history of the
  82. // module. We provide an export format version string to provide backwards
  83. // compatibility. Note that it is ok to use the array key "version" here.
  84. // Block ids always have a '-' in their string.
  85. $code[] = ' $export[\'version\'] = \'' . FE_BLOCK_VERSION . '\';';
  86. $code[] = '';
  87. // Get a list of all active themes to cycle through.
  88. $themes = _fe_block_get_active_themes();
  89. // Retrieve block settings for all blocks in all active themes.
  90. $blocks = array();
  91. foreach ($themes as $theme) {
  92. $blocks[$theme] = _fe_block_info_by_theme($theme);
  93. }
  94. // We use the first theme's block settings as master settings. Some settings
  95. // are specific to each theme, but these are processed later in the loop.
  96. $default_theme = reset($themes);
  97. // We try to build an export for each defined data element.
  98. foreach ($data as $name) {
  99. // Check if the block still exists in the block definitions.
  100. if (!empty($blocks[$default_theme][$name])) {
  101. $block = $blocks[$default_theme][$name];
  102. // We start to build the export object for this block.
  103. // First we retrieve data that is valid for any theme.
  104. $export_block = _fe_block_get_global_settings($block);
  105. // Ensure core custom block export keys are transformed.
  106. $export_block = _fe_block_prepare_custom_blocks_for_export($export_block);
  107. // Add node type settings.
  108. $export_block['node_types'] = _fe_block_get_block_node_types($block);
  109. // Add role visibility settings.
  110. $export_block['roles'] = _fe_block_get_block_roles($block);
  111. // Add theme specific settings for every active theme.
  112. $export_block['themes'] = array();
  113. foreach ($themes as $theme) {
  114. $export_block['themes'][$theme] = _fe_block_get_theme_specific_settings($blocks[$theme][$name]);
  115. }
  116. // Sort export array keys.
  117. ksort($export_block);
  118. // Export to code.
  119. $code[] = ' $export[\'' . $name . '\'] = ' . features_var_export($export_block, ' ') . ';';
  120. // Add an empty line.
  121. $code[] = '';
  122. }
  123. }
  124. $code[] = ' return $export;';
  125. $code = implode("\n", $code);
  126. return array('default_fe_block_settings' => $code);
  127. }
  128. /**
  129. * Returns the block definitions for a specific theme.
  130. *
  131. * @param string $theme
  132. * Machine name of the theme.
  133. *
  134. * @return array
  135. * Array of block definitions.
  136. */
  137. function _fe_block_info_by_theme($theme) {
  138. $blocks = array();
  139. foreach (_fe_block_get_blocks($theme) as $block) {
  140. // Blocks are only valid for export if we got a machine name for them.
  141. if ($id = _fe_block_build_id($block)) {
  142. $blocks[$id] = $block;
  143. }
  144. }
  145. // Sort blocks by keys to get a consistent order.
  146. ksort($blocks);
  147. return $blocks;
  148. }
  149. /**
  150. * Retrieve the global (non-theme-specific) part of a block definition.
  151. *
  152. * @param array $block
  153. * A block definition.
  154. *
  155. * @return array
  156. * The block definition filtered on non-theme-specific settings.
  157. */
  158. function _fe_block_get_global_settings($block) {
  159. $theme_specific_defaults = _fe_block_theme_specific_defaults();
  160. // Filter on any keys other than the theme specific ones.
  161. $return = array_diff_key($block, $theme_specific_defaults);
  162. // Remove the serial.
  163. if (isset($return['bid'])) {
  164. unset($return['bid']);
  165. }
  166. // Remove the info from hook_block_info().
  167. if (isset($return['info'])) {
  168. unset($return['info']);
  169. }
  170. return $return;
  171. }
  172. /**
  173. * Helper to prepare a core custom block for export.
  174. *
  175. * Replaces the block delta that is used by the core block module with a unique
  176. * machine name.
  177. *
  178. * @param array $block
  179. * Block definition - can be only part of the original definition.
  180. *
  181. * @return array
  182. * Altered block array.
  183. */
  184. function _fe_block_prepare_custom_blocks_for_export($block) {
  185. if ($block['module'] == 'block') {
  186. $block['machine_name'] = fe_block_get_machine_name($block['delta']);
  187. unset($block['delta']);
  188. }
  189. return $block;
  190. }
  191. /**
  192. * Helper function. Prepares an exported core custom block for import.
  193. *
  194. * @param array $block
  195. * Block definition from the import code.
  196. *
  197. * @return array
  198. * Altered array with machine_name replaced by delta.
  199. */
  200. function _fe_block_prepare_custom_blocks_for_import($block) {
  201. if ($block['module'] == 'block') {
  202. $block['delta'] = fe_block_get_bid($block['machine_name'], TRUE);
  203. unset($block['machine_name']);
  204. }
  205. return $block;
  206. }
  207. /**
  208. * Helper function to get the theme specific settings for a block.
  209. *
  210. * @param array $block
  211. * A single block definition.
  212. *
  213. * @return array
  214. * A filtered block definition with only theme-specific settings.
  215. */
  216. function _fe_block_get_theme_specific_settings($block) {
  217. $defaults = _fe_block_theme_specific_defaults();
  218. $settings = array_intersect_key($block, $defaults);
  219. // Region.
  220. if ($settings['region'] == BLOCK_REGION_NONE) {
  221. $settings['status'] = 0;
  222. $settings['region'] = '';
  223. }
  224. ksort($settings);
  225. return $settings;
  226. }
  227. /**
  228. * Helper function for filtering theme specific settings.
  229. *
  230. * @see _fe_block_get_global_settings()
  231. * @see _fe_block_get_theme_specific_settings()
  232. *
  233. * @return array
  234. * An array of default settings, keyed by name.
  235. */
  236. function _fe_block_theme_specific_defaults() {
  237. return array(
  238. 'theme' => '',
  239. 'status' => '',
  240. 'weight' => 0,
  241. 'region' => '',
  242. );
  243. }
  244. /**
  245. * Get node type visibility settings for the specified block.
  246. *
  247. * @param array $block
  248. * Block definition array.
  249. *
  250. * @return array
  251. * Array of node types associated with the block.
  252. */
  253. function _fe_block_get_block_node_types($block) {
  254. $query = db_select('block_node_type', 'bnt')
  255. ->condition('module', $block['module'])
  256. ->condition('delta', $block['delta'])
  257. ->fields('bnt', array('type'))
  258. ->orderBy('bnt.type', 'ASC');
  259. return $query->execute()->fetchCol();
  260. }
  261. /**
  262. * Returns the blocks currently exported by modules.
  263. *
  264. * This is derived from _block_rehash().
  265. *
  266. * @param $theme
  267. * The theme to retrieve blocks for. If not provided, defaults to the
  268. * currently used theme.
  269. *
  270. * @return
  271. * Blocks currently exported by modules.
  272. */
  273. function _fe_block_get_blocks($theme = NULL) {
  274. global $theme_key;
  275. $blocks = array();
  276. drupal_theme_initialize();
  277. if (!isset($theme)) {
  278. // If theme is not specifically set, rehash for the current theme.
  279. $theme = $theme_key;
  280. }
  281. $regions = system_region_list($theme);
  282. // These are the blocks defined by code and modified by the database.
  283. $current_blocks = array();
  284. // These are {block}.bid values to be kept.
  285. $bids = array();
  286. $or = db_or();
  287. // Gather the blocks defined by modules.
  288. foreach (module_implements('block_info') as $module) {
  289. $module_blocks = module_invoke($module, 'block_info');
  290. foreach ($module_blocks as $delta => $block) {
  291. // Compile a condition to retrieve this block from the database.
  292. $condition = db_and()
  293. ->condition('module', $module)
  294. ->condition('delta', $delta);
  295. $or->condition($condition);
  296. // Add identifiers.
  297. $block['module'] = $module;
  298. $block['delta'] = $delta;
  299. $block['theme'] = $theme;
  300. $current_blocks[$module][$delta] = $block;
  301. }
  302. }
  303. // Retrieve database settings for all blocks that are defined by modules.
  304. $code_blocks = $current_blocks;
  305. $database_blocks = db_select('block', 'b')
  306. ->fields('b')
  307. ->condition($or)
  308. ->condition('theme', $theme)
  309. ->execute();
  310. foreach ($database_blocks as $block) {
  311. // Preserve info which is not in the database.
  312. $block->info = $current_blocks[$block->module][$block->delta]['info'];
  313. // The cache mode can only by set from hook_block_info(), so that has
  314. // precedence over the database's value.
  315. if (isset($current_blocks[$block->module][$block->delta]['cache'])) {
  316. $block->cache = $current_blocks[$block->module][$block->delta]['cache'];
  317. }
  318. // Blocks stored in the database override the blocks defined in code.
  319. $current_blocks[$block->module][$block->delta] = get_object_vars($block);
  320. // Preserve this block.
  321. $bids[$block->bid] = $block->bid;
  322. }
  323. drupal_alter('block_info', $current_blocks, $theme, $code_blocks);
  324. foreach ($current_blocks as $module => $module_blocks) {
  325. foreach ($module_blocks as $delta => $block) {
  326. if (!isset($block['pages'])) {
  327. // {block}.pages is type 'text', so it cannot have a
  328. // default value, and not null, so we need to provide
  329. // value if the module did not.
  330. $block['pages'] = '';
  331. }
  332. // Make sure weight is set.
  333. if (!isset($block['weight'])) {
  334. $block['weight'] = 0;
  335. }
  336. // Disable blocks that are not assigned to a region in the theme.
  337. if (!empty($block['region']) && $block['region'] != BLOCK_REGION_NONE && !isset($regions[$block['region']]) && $block['status'] == 1) {
  338. // Disabled modules are moved into the BLOCK_REGION_NONE later so no
  339. // need to move the block to another region.
  340. $block['status'] = 0;
  341. }
  342. // Set region to none if not enabled and make sure status is set.
  343. if (empty($block['status'])) {
  344. $block['status'] = 0;
  345. $block['region'] = BLOCK_REGION_NONE;
  346. }
  347. // Add to the list of blocks we return.
  348. $blocks[] = $block;
  349. }
  350. }
  351. return $blocks;
  352. }
  353. /**
  354. * Returns a list of machine names of active themes.
  355. *
  356. * @return array
  357. * An array of theme machine names.
  358. */
  359. function _fe_block_get_active_themes() {
  360. $theme_names = array();
  361. foreach (list_themes() as $machine_name => $theme) {
  362. if (!empty($theme->status)) {
  363. $theme_names[] = $machine_name;
  364. }
  365. }
  366. sort($theme_names);
  367. return $theme_names;
  368. }
  369. /**
  370. * Implements hook_features_revert().
  371. */
  372. function fe_block_settings_features_revert($module_name = NULL) {
  373. $component = 'fe_block_settings';
  374. $defaults = features_get_default($component, $module_name);
  375. if (empty($defaults)) {
  376. return;
  377. }
  378. // We remove the version, as we now want to deal with actual block settings.
  379. unset($defaults['version']);
  380. $themes_rehashed = array();
  381. $active_themes = _fe_block_get_active_themes();
  382. // The fallback theme for theme specific settings.
  383. $theme_default = variable_get('theme_default', 'bartik');
  384. foreach ($defaults as $block) {
  385. // Core custom blocks are prepared with a delta value.
  386. $block = _fe_block_prepare_custom_blocks_for_import($block);
  387. // Remove the additional settings from the block array, to process them
  388. // later. We explicitely set NULL, if no setting was given in the defaults.
  389. $block_themes = $block['themes'];
  390. $block_node_types = isset($block['node_types']) ? $block['node_types'] : NULL;
  391. $block_roles = isset($block['roles']) ? $block['roles'] : NULL;
  392. unset($block['themes']);
  393. unset($block['node_types']);
  394. unset($block['roles']);
  395. // Restore theme specific settings for every active theme.
  396. foreach ($active_themes as $theme) {
  397. // Rehash if we did not yet.
  398. if (empty($themes_rehashed[$theme])) {
  399. _block_rehash($theme);
  400. $themes_rehashed[$theme] = TRUE;
  401. }
  402. // Get the theme specific setting for the active theme.
  403. if (isset($block_themes[$theme])) {
  404. $key = $theme;
  405. }
  406. // Or fallback on the default theme.
  407. elseif (isset($block_themes[$theme_default])) {
  408. $key = $theme_default;
  409. }
  410. // Or fallback on the first available theme spec.
  411. else {
  412. $key = key($block_themes);
  413. }
  414. // Write block settings.
  415. $write = array_merge($block, $block_themes[$key]);
  416. drupal_write_record('block', $write, array('module', 'delta', 'theme'));
  417. }
  418. // Ensure global settings.
  419. _fe_block_settings_update_global_settings($block);
  420. // Set node type settings
  421. // (only if there were some defined, to avoid overwriting not yet exported
  422. // data).
  423. if (isset($block_node_types)) {
  424. _fe_block_settings_update_block_node_type_settings($block, $block_node_types);
  425. }
  426. // Apply role visibility settings.
  427. if (isset($block_roles)) {
  428. _fe_block_settings_update_block_roles($block, $block_roles);
  429. }
  430. }
  431. // Clear block cache.
  432. cache_clear_all(NULL, 'cache_block');
  433. return TRUE;
  434. }
  435. /**
  436. * Helper to update global block settings for a specific block.
  437. *
  438. * @param array $block
  439. * block definition
  440. */
  441. function _fe_block_settings_update_global_settings($block) {
  442. $globals = _fe_block_get_global_settings($block);
  443. db_update('block')
  444. ->fields($globals)
  445. ->condition('module', $block['module'])
  446. ->condition('delta', $block['delta'])
  447. ->execute();
  448. }
  449. /**
  450. * Helper to update node type settings for a given block.
  451. *
  452. * @param array $block
  453. * block definition
  454. * @param array $node_types
  455. * array of node types
  456. */
  457. function _fe_block_settings_update_block_node_type_settings($block, $node_types) {
  458. // First delete the old node type settings.
  459. db_delete('block_node_type')
  460. ->condition('module', $block['module'])
  461. ->condition('delta', $block['delta'])
  462. ->execute();
  463. if (!empty($node_types)) {
  464. $insert = db_insert('block_node_type')
  465. ->fields(array('module', 'delta', 'type'));
  466. foreach ($node_types as $type) {
  467. $insert->values(array(
  468. 'module' => $block['module'],
  469. 'delta' => $block['delta'],
  470. 'type' => $type,
  471. ));
  472. }
  473. $insert->execute();
  474. }
  475. }
  476. /**
  477. * Helper to update the block role settings for a given block.
  478. *
  479. * @param array $block
  480. *   block definiton
  481. * @param array $roles
  482. *   Associative array of roles
  483. *   - key: role name
  484. *   - value: (foreign) role id
  485. */
  486. function _fe_block_settings_update_block_roles($block, $block_roles) {
  487. static $roles;
  488. // First get the current set of roles, so we can match role names to rids.
  489. if (!isset($roles)) {
  490. $roles = db_select('role', 'r')
  491. ->fields('r', array('rid', 'name'))
  492. ->execute()
  493. ->fetchAllKeyed(1, 0);
  494. }
  495. // First delete the old block role settings.
  496. db_delete('block_role')
  497. ->condition('module', $block['module'])
  498. ->condition('delta', $block['delta'])
  499. ->execute();
  500. // Then write the new settings, if any are present.
  501. if (!empty($block_roles)) {
  502. $insert = db_insert('block_role')
  503. ->fields(array('module', 'delta', 'rid'));
  504. // We use a found flag, to avoid empty inserts if no role names match.
  505. $found = FALSE;
  506. foreach ($block_roles as $name => $rid) {
  507. // We only write for roles, matching the given role name.
  508. if (isset($roles[$name])) {
  509. $insert->values(array(
  510. 'module' => $block['module'],
  511. 'delta' => $block['delta'],
  512. 'rid' => $roles[$name],
  513. ));
  514. $found = TRUE;
  515. }
  516. }
  517. if ($found) {
  518. $insert->execute();
  519. }
  520. }
  521. }
  522. /**
  523. * Implements hook_features_disable_feature().
  524. */
  525. function fe_block_settings_features_disable_feature($module) {
  526. }
  527. /**
  528. * Implements hook_features_enable_feature().
  529. */
  530. function fe_block_settings_features_enable_feature($module) {
  531. fe_block_settings_features_revert($module);
  532. }
  533. /**
  534. * Implements hook_features_rebuild().
  535. */
  536. function fe_block_settings_features_rebuild($module) {
  537. fe_block_settings_features_revert($module);
  538. }
  539. /**
  540. * Implements hook_features_export_options().
  541. */
  542. function fe_block_boxes_features_export_options() {
  543. $table = 'fe_block_boxes';
  544. $options = array();
  545. // Defaults.
  546. $schema = ctools_export_get_schema($table);
  547. $export = $schema['export'];
  548. $defaults = _ctools_export_get_defaults($table, $export);
  549. foreach ($defaults as $obj) {
  550. $options[$obj->machine_name] = t('@name [@machine_name]', array('@name' => $obj->info, '@machine_name' => $obj->machine_name));
  551. }
  552. // Normals.
  553. $query = "SELECT * FROM {{$table}} {$table} INNER JOIN {block_custom} b ON b.bid = {$table}.bid ORDER BY b.bid ASC";
  554. $result = db_query($query);
  555. foreach ($result as $obj) {
  556. $options[$obj->machine_name] = t('@name [@machine_name]', array('@name' => $obj->info, '@machine_name' => $obj->machine_name));
  557. }
  558. ksort($options);
  559. return $options;
  560. }
  561. /**
  562. * Implements hook_features_export().
  563. */
  564. function fe_block_boxes_features_export($data, &$export, $module_name = '') {
  565. $pipe = array();
  566. $export['dependencies']['fe_block'] = 'fe_block';
  567. $table = 'fe_block_boxes';
  568. // Add the components
  569. foreach ($data as $object_name) {
  570. $export['features'][$table][$object_name] = $object_name;
  571. }
  572. return $pipe;
  573. }
  574. /**
  575. * Implements hook_features_export_render().
  576. */
  577. function fe_block_boxes_features_export_render($module_name = '', $data) {
  578. ctools_include('export');
  579. $component = 'fe_block_boxes';
  580. $schema = ctools_export_get_schema($component);
  581. $objects = ctools_export_load_object($component);
  582. $code = array();
  583. $code[] = ' $export = array();';
  584. $code[] = '';
  585. foreach ($data as $machine_name) {
  586. // The object to be exported.
  587. if (isset($objects[$machine_name]) && $object = $objects[$machine_name]) {
  588. $additions = array();
  589. // Load box.
  590. if (!empty($object->bid) && $box = block_custom_block_get($object->bid)) {
  591. $additions = (array) $box;
  592. unset($additions['bid'], $additions['body']);
  593. // Code.
  594. $identifier = $schema['export']['identifier'];
  595. $code[] = ctools_export_object($component, $object, ' ', $identifier, $additions) . ' $' . $identifier . '->body = ' . features_var_export($box['body']) . ';';
  596. $code[] = '';
  597. $code[] = ' $export[\'' . $machine_name . '\'] = $' . $identifier . ';';
  598. $code[] = '';
  599. }
  600. }
  601. }
  602. $code[] = ' return $export;';
  603. $code = implode("\n", $code);
  604. return array($schema['export']['default hook'] => $code);
  605. }
  606. /**
  607. * Implements hook_features_revert().
  608. */
  609. function fe_block_boxes_features_revert($module_name = NULL) {
  610. $defaults = features_get_default('fe_block_boxes', $module_name);
  611. if (empty($defaults)) {
  612. return;
  613. }
  614. // Revert.
  615. foreach ($defaults as $object) {
  616. if (empty($object->machine_name)) {
  617. continue;
  618. }
  619. $bid = fe_block_get_bid($object->machine_name);
  620. if (empty($bid) || !($box = block_custom_block_get($bid))) {
  621. $result = _fe_block_save_box((array) $object);
  622. if (!empty($result['bid'])) {
  623. $or = db_or()
  624. ->condition('bid', $result['bid'])
  625. ->condition('machine_name', $object->machine_name);
  626. db_delete('fe_block_boxes')
  627. ->condition($or)
  628. ->execute();
  629. db_insert('fe_block_boxes')
  630. ->fields(array(
  631. 'bid' => $result['bid'],
  632. 'machine_name' => $object->machine_name,
  633. ))
  634. ->execute();
  635. }
  636. }
  637. else {
  638. $object->bid = $bid;
  639. $result = _fe_block_save_box((array) $object);
  640. }
  641. }
  642. // Clear block cache
  643. cache_clear_all(NULL, 'cache_block');
  644. return TRUE;
  645. }
  646. /**
  647. * Implements hook_features_disable_feature().
  648. */
  649. function fe_block_boxes_features_disable_feature($module) {
  650. }
  651. /**
  652. * Implements hook_features_enable_feature().
  653. */
  654. function fe_block_boxes_features_enable_feature($module) {
  655. fe_block_boxes_features_revert($module);
  656. }
  657. /**
  658. * Implements hook_features_rebuild().
  659. */
  660. function fe_block_boxes_features_rebuild($module) {
  661. fe_block_boxes_features_revert($module);
  662. }
  663. /**
  664. * Implements hook_form_alter().
  665. */
  666. function fe_block_form_alter(&$form, $form_state, $form_id) {
  667. $default_values = array();
  668. if ($form_id == 'block_add_block_form' && $form['module']['#value'] == 'block' && user_access('administer features')) {
  669. $default_values['machine_name'] = '';
  670. $default_values['bid'] = 0;
  671. }
  672. elseif ($form_id == 'block_admin_configure' && $form['module']['#value'] == 'block' && user_access('administer features')) {
  673. $bid = $form['delta']['#value'];
  674. $machine_name = fe_block_get_machine_name($bid);
  675. $default_values['machine_name'] = empty($machine_name) ? '' : $machine_name;
  676. $default_values['bid'] = $bid;
  677. }
  678. // Delete a block.
  679. elseif ($form_id == 'block_box_delete') {
  680. $form['#submit'][] = 'fe_block_machine_name_delete';
  681. }
  682. // Add & edit
  683. if (!empty($default_values)) {
  684. $form['settings']['machine_name'] = array(
  685. '#type' => 'textfield',
  686. '#title' => t('Machine name'),
  687. '#default_value' => $default_values['machine_name'],
  688. '#maxlength' => 32,
  689. '#description' => t('Give the block a machine name to make it exportable with "!features" module.', array('!features' => l('Features', 'http://drupal.org/project/features'))),
  690. '#weight' => -50,
  691. );
  692. $form['bid'] = array(
  693. '#type' => 'value',
  694. '#value' => $default_values['bid'],
  695. );
  696. // Validate & submit.
  697. $form['#validate'][] = 'fe_block_machine_name_validate';
  698. $form['#submit'][] = 'fe_block_machine_name_submit';
  699. }
  700. }
  701. /**
  702. * Validate machine name.
  703. */
  704. function fe_block_machine_name_validate($form, &$form_state) {
  705. if (empty($form_state['values']['machine_name'])) {
  706. return;
  707. }
  708. $table = 'fe_block_boxes';
  709. $query = db_select($table)
  710. ->condition('bid', $form_state['values']['bid'], '<>')
  711. ->condition('machine_name', $form_state['values']['machine_name']);
  712. $count = $query->countQuery()->execute()->fetchField();
  713. if (!preg_match('!^[a-z0-9_]+$!', $form_state['values']['machine_name'])) {
  714. form_set_error('machine_name', t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
  715. }
  716. elseif ($count > 0) {
  717. form_set_error('machine_name', t('The machine-readable name has been taken. Please pick another one.'));
  718. }
  719. }
  720. /**
  721. * Save machine name.
  722. */
  723. function fe_block_machine_name_submit($form, &$form_state) {
  724. // Insert
  725. if (empty($form_state['values']['bid'])) {
  726. $form_state['values']['bid'] = db_select('block_custom')
  727. ->fields('block_custom', array('bid'))
  728. ->condition('info', $form_state['values']['info'])
  729. ->execute()->fetch()->bid;
  730. }
  731. if (empty($form_state['values']['bid'])) {
  732. return;
  733. }
  734. $table = 'fe_block_boxes';
  735. db_delete($table)
  736. ->condition('bid', $form_state['values']['bid'])
  737. ->execute();
  738. if (!empty($form_state['values']['machine_name'])) {
  739. drupal_write_record($table, $form_state['values']);
  740. }
  741. }
  742. /**
  743. * Delete machine name.
  744. */
  745. function fe_block_machine_name_delete($form, &$form_state) {
  746. $table = 'fe_block_boxes';
  747. db_delete($table)->condition('bid', $form_state['values']['bid']);
  748. }
  749. /**
  750. * Public APIs.
  751. *
  752. * TODO
  753. */
  754. /**
  755. * Internal functions.
  756. */
  757. /**
  758. * Sort blocks with "module" and "delta".
  759. */
  760. function _fe_block_compare($a, $b) {
  761. $module_cmp = strcmp($a['module'], $b['module']);
  762. if (!empty($module_cmp)) {
  763. return $module_cmp;
  764. }
  765. return strcmp($a['delta'], $b['delta']);
  766. }
  767. /**
  768. * Provided for backwards compatibility. Use fe_block_get_machine_name().
  769. */
  770. function _fe_block_get_machine_name($bid) {
  771. debug(t('The function @function is deprecated.', array('@function' => __FUNCTION__ . '()')));
  772. return fe_block_get_machine_name($bid);
  773. }
  774. /**
  775. * Returns the machine name that corresponds to a given block id.
  776. *
  777. * @param int $bid
  778. * The block id for which to retrieve the machine name.
  779. *
  780. * @return string | FALSE
  781. * The machine name, or FALSE if it could not be found.
  782. */
  783. function fe_block_get_machine_name($bid) {
  784. $machine_names = &drupal_static(__FUNCTION__);
  785. if (!isset($machine_names[$bid])) {
  786. $result = db_select('fe_block_boxes')
  787. ->fields('fe_block_boxes', array('machine_name'))
  788. ->condition('bid', $bid)
  789. ->execute()
  790. ->fetch();
  791. if (empty($result)) {
  792. return FALSE;
  793. }
  794. $machine_names[$bid] = $result->machine_name;
  795. }
  796. return $machine_names[$bid];
  797. }
  798. /**
  799. * Provided for backwards compatibility. Use fe_block_get_bid() instead.
  800. */
  801. function _fe_block_get_bid($machine_name, $reset = FALSE) {
  802. debug(t('The function @function is deprecated.', array('@function' => __FUNCTION__ . '()')));
  803. return fe_block_get_bid($machine_name, $reset);
  804. }
  805. /**
  806. * Returns the block id that corresponds to a given machine name.
  807. *
  808. * @param string $machine_name
  809. * The machine name of a block for which to retrieve the block id.
  810. *
  811. * @return int | FALSE
  812. * The block id, or FALSE if the machine name was not found.
  813. */
  814. function fe_block_get_bid($machine_name, $reset = FALSE) {
  815. $bids = &drupal_static(__FUNCTION__);
  816. if (!isset($bids[$machine_name]) || $reset) {
  817. $result = db_select('fe_block_boxes')
  818. ->fields('fe_block_boxes', array('bid'))
  819. ->condition('machine_name', $machine_name)
  820. ->execute()
  821. ->fetch();
  822. if (empty($result)) {
  823. return FALSE;
  824. }
  825. $bids[$machine_name] = (int) $result->bid;
  826. }
  827. return $bids[$machine_name];
  828. }
  829. /**
  830. * Generate block ID.
  831. */
  832. function _fe_block_build_id($block) {
  833. if (empty($block['module']) || (empty($block['delta']) && !is_numeric($block['delta']))) {
  834. return NULL;
  835. }
  836. if ($block['module'] == 'block') {
  837. $machine_name = fe_block_get_machine_name($block['delta']);
  838. if (empty($machine_name)) {
  839. return NULL;
  840. }
  841. return $block['module'] . '-' . $machine_name;
  842. }
  843. else {
  844. return $block['module'] . '-' . $block['delta'];
  845. }
  846. }
  847. /**
  848. * Save a box.
  849. *
  850. * @param $settings
  851. * @return array
  852. */
  853. function _fe_block_save_box($settings = array()) {
  854. if (empty($settings['info'])) {
  855. return FALSE;
  856. }
  857. // 'info' must be unique.
  858. if (empty($settings['bid'])) {
  859. $conflict = db_query("SELECT COUNT(*) as count FROM {block_custom} WHERE info = :info", array('info' => $settings['info']));
  860. }
  861. else {
  862. $conflict = db_query("SELECT COUNT(*) as count FROM {block_custom} WHERE info = :info AND bid <> :bid", array('info' => $settings['info'], ':bid' => $settings['bid']));
  863. }
  864. if (!empty($conflict->fetch()->count)) {
  865. return FALSE;
  866. }
  867. // Defaults
  868. $default_settings = array(
  869. 'info' => '',
  870. 'body' => '',
  871. 'format' => 'FILTER_FORMAT_DEFAULT',
  872. );
  873. $settings = array_merge($default_settings, $settings);
  874. // Save
  875. if (empty($settings['bid'])) {
  876. drupal_write_record('block_custom', $settings);
  877. }
  878. else {
  879. drupal_write_record('block_custom', $settings, 'bid');
  880. }
  881. return $settings;
  882. }
  883. /**
  884. * Implements hook_module_implements_alter().
  885. */
  886. function fe_block_module_implements_alter(&$implementations, $hook) {
  887. if ($hook == 'default_fe_block_settings_alter') {
  888. // Ensure fe_block is the first imlementation to be called, so we can
  889. // convert to the newest format.
  890. $group = $implementations['fe_block'];
  891. unset($implementations['fe_block']);
  892. $rest = array_reverse($implementations, TRUE);
  893. $rest['fe_block'] = $group;
  894. $implementations = array_reverse($rest, TRUE);
  895. }
  896. }
  897. /**
  898. * Implements hook_default_fe_block_settings_alter().
  899. */
  900. function fe_block_default_fe_block_settings_alter(&$defaults) {
  901. // Convert the settings in the newest format.
  902. $defaults = _fe_block_settings_convert($defaults);
  903. }
  904. /**
  905. * Helper function to get the block roles visibility settings.
  906. *
  907. * @param array $block
  908. * the block definition array
  909. *
  910. * @return array
  911. * associated role settings for the block
  912. * - key: role name
  913. * - value: role id
  914. */
  915. function _fe_block_get_block_roles($block) {
  916. $query = db_select('block_role', 'br')
  917. ->condition('br.module', $block['module'])
  918. ->condition('br.delta', $block['delta']);
  919. $query->innerJoin('role', 'r', 'r.rid = br.rid');
  920. $query->fields('r', array('name', 'rid'))
  921. ->orderBy('r.name', 'ASC');
  922. $roles = $query->execute()->fetchAllKeyed(0, 1);
  923. return $roles;
  924. }
  925. /**
  926. * Helper function to convert an older export into the new format.
  927. *
  928. * @param array $defaults
  929. * array of fe_block_settings definition.
  930. *
  931. * @return array
  932. * array of current fe_block_settings definition
  933. */
  934. function _fe_block_settings_convert($defaults) {
  935. $version = (isset($defaults['version'])) ? $defaults['version'] : 0;
  936. // Directly return if the version is the current one.
  937. if ($version == FE_BLOCK_VERSION) {
  938. return $defaults;
  939. }
  940. elseif ($version == '1.0') {
  941. // We try to get the default theme for the global definitions, else we take
  942. // the first.
  943. $theme_default = variable_get('theme_default', 'bartik');
  944. if (!isset($defaults['theme'][$theme_default])) {
  945. $theme_default = key($defaults['theme']);
  946. }
  947. $blocks = array();
  948. // We get the basic blocks from the visibility array.
  949. foreach ($defaults['visibility'] as $block_id => $base) {
  950. $node_types = array();
  951. if (isset($base['node_type'])) {
  952. // Node types were specified in node_type => TRUE/FALSE. Now we simply
  953. // list the selected node types.
  954. $node_types = array_keys(array_filter($base));
  955. unset($base['node_type']);
  956. }
  957. $block = $base;
  958. $block['node_types'] = $node_types;
  959. // Global settings.
  960. $globals = _fe_block_get_global_settings($defaults['theme'][$theme_default][$block_id]);
  961. $block = array_merge($globals, $block);
  962. // Build theme specific array.
  963. $block['themes'] = array();
  964. foreach ($defaults['theme'] as $theme => $items) {
  965. $block['themes'][$theme] = _fe_block_get_theme_specific_settings($items[$block_id]);
  966. }
  967. $blocks[$block_id] = $block;
  968. }
  969. // Set current version so we can compare it with current version defaults.
  970. $blocks['version'] = FE_BLOCK_VERSION;
  971. return $blocks;
  972. }
  973. // The oldest version.
  974. // There we got an array of themes that holded the block settings.
  975. elseif ($version == 0) {
  976. // We try to get the default theme for the global definitions, else we take
  977. // the first.
  978. $theme_default = variable_get('theme_default', 'bartik');
  979. if (!isset($defaults[$theme_default])) {
  980. $theme_default = key($defaults);
  981. }
  982. $blocks = array();
  983. foreach ($defaults as $theme => $items) {
  984. foreach ($items as $block_id => $item) {
  985. // Avoid php notices.
  986. if (!isset($blocks[$block_id])) {
  987. $blocks[$block_id] = array(
  988. 'themes' => array(),
  989. );
  990. }
  991. // Set theme specific settings.
  992. $blocks[$block_id]['themes'][$theme] = _fe_block_get_theme_specific_settings($item);
  993. // We add the global settings for the default theme.
  994. if ($theme == $theme_default) {
  995. $globals = _fe_block_get_global_settings($item);
  996. $blocks[$block_id] = array_merge($blocks[$block_id], $globals);
  997. }
  998. }
  999. }
  1000. // Set current version so we can compare it with current version defaults.
  1001. $blocks['version'] = FE_BLOCK_VERSION;
  1002. return $blocks;
  1003. }
  1004. }