fe_block.module 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  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 block_class support.
  112. if (module_exists('block_class')) {
  113. $export_block['css_class'] = _fe_block_get_block_css_class($block);
  114. }
  115. // Add i18n_block support.
  116. if (module_exists('i18n_block')) {
  117. $export_block['i18n_block_language'] = _fe_block_get_block_i18n_block_language($block);
  118. }
  119. // Add theme specific settings for every active theme.
  120. $export_block['themes'] = array();
  121. foreach ($themes as $theme) {
  122. $export_block['themes'][$theme] = _fe_block_get_theme_specific_settings($blocks[$theme][$name]);
  123. }
  124. // Sort export array keys.
  125. ksort($export_block);
  126. // Export to code.
  127. $code[] = ' $export[\'' . $name . '\'] = ' . features_var_export($export_block, ' ') . ';';
  128. // Add an empty line.
  129. $code[] = '';
  130. }
  131. }
  132. $code[] = ' return $export;';
  133. $code = implode("\n", $code);
  134. return array('default_fe_block_settings' => $code);
  135. }
  136. /**
  137. * Returns the block definitions for a specific theme.
  138. *
  139. * @param string $theme
  140. * Machine name of the theme.
  141. *
  142. * @return array
  143. * Array of block definitions.
  144. */
  145. function _fe_block_info_by_theme($theme) {
  146. $blocks = array();
  147. foreach (_fe_block_get_blocks($theme) as $block) {
  148. // Blocks are only valid for export if we got a machine name for them.
  149. if ($id = _fe_block_build_id($block)) {
  150. $blocks[$id] = $block;
  151. }
  152. }
  153. // Sort blocks by keys to get a consistent order.
  154. ksort($blocks);
  155. return $blocks;
  156. }
  157. /**
  158. * Retrieve the global (non-theme-specific) part of a block definition.
  159. *
  160. * @param array $block
  161. * A block definition.
  162. *
  163. * @return array
  164. * The block definition filtered on non-theme-specific settings.
  165. */
  166. function _fe_block_get_global_settings($block) {
  167. $theme_specific_defaults = _fe_block_theme_specific_defaults();
  168. // Filter on any keys other than the theme specific ones.
  169. $return = array_diff_key($block, $theme_specific_defaults);
  170. // Remove the serial.
  171. if (isset($return['bid'])) {
  172. unset($return['bid']);
  173. }
  174. // Remove the info from hook_block_info().
  175. if (isset($return['info'])) {
  176. unset($return['info']);
  177. }
  178. return $return;
  179. }
  180. /**
  181. * Helper to prepare a core custom block for export.
  182. *
  183. * Replaces the block delta that is used by the core block module with a unique
  184. * machine name.
  185. *
  186. * @param array $block
  187. * Block definition - can be only part of the original definition.
  188. *
  189. * @return array
  190. * Altered block array.
  191. */
  192. function _fe_block_prepare_custom_blocks_for_export($block) {
  193. if ($block['module'] == 'block') {
  194. $block['machine_name'] = fe_block_get_machine_name($block['delta']);
  195. unset($block['delta']);
  196. }
  197. return $block;
  198. }
  199. /**
  200. * Helper function. Prepares an exported core custom block for import.
  201. *
  202. * @param array $block
  203. * Block definition from the import code.
  204. *
  205. * @return array
  206. * Altered array with machine_name replaced by delta.
  207. */
  208. function _fe_block_prepare_custom_blocks_for_import($block) {
  209. if ($block['module'] == 'block') {
  210. $block['delta'] = fe_block_get_bid($block['machine_name'], TRUE);
  211. unset($block['machine_name']);
  212. }
  213. return $block;
  214. }
  215. /**
  216. * Helper function to get the theme specific settings for a block.
  217. *
  218. * @param array $block
  219. * A single block definition.
  220. *
  221. * @return array
  222. * A filtered block definition with only theme-specific settings.
  223. */
  224. function _fe_block_get_theme_specific_settings($block) {
  225. $defaults = _fe_block_theme_specific_defaults();
  226. $settings = array_intersect_key($block, $defaults);
  227. // Region.
  228. if ($settings['region'] == BLOCK_REGION_NONE) {
  229. $settings['status'] = 0;
  230. $settings['region'] = '';
  231. }
  232. ksort($settings);
  233. return $settings;
  234. }
  235. /**
  236. * Helper function for filtering theme specific settings.
  237. *
  238. * @see _fe_block_get_global_settings()
  239. * @see _fe_block_get_theme_specific_settings()
  240. *
  241. * @return array
  242. * An array of default settings, keyed by name.
  243. */
  244. function _fe_block_theme_specific_defaults() {
  245. return array(
  246. 'theme' => '',
  247. 'status' => '',
  248. 'weight' => 0,
  249. 'region' => '',
  250. );
  251. }
  252. /**
  253. * Get node type visibility settings for the specified block.
  254. *
  255. * @param array $block
  256. * Block definition array.
  257. *
  258. * @return array
  259. * Array of node types associated with the block.
  260. */
  261. function _fe_block_get_block_node_types($block) {
  262. $query = db_select('block_node_type', 'bnt')
  263. ->condition('module', $block['module'])
  264. ->condition('delta', $block['delta'])
  265. ->fields('bnt', array('type'))
  266. ->orderBy('bnt.type', 'ASC');
  267. return $query->execute()->fetchCol();
  268. }
  269. /**
  270. * Returns the blocks currently exported by modules.
  271. *
  272. * This is derived from _block_rehash().
  273. *
  274. * @param string $theme
  275. * The theme to retrieve blocks for. If not provided, defaults to the
  276. * currently used theme.
  277. *
  278. * @return array
  279. * Blocks currently exported by modules.
  280. */
  281. function _fe_block_get_blocks($theme = NULL) {
  282. global $theme_key;
  283. $blocks = array();
  284. drupal_theme_initialize();
  285. if (!isset($theme)) {
  286. // If theme is not specifically set, rehash for the current theme.
  287. $theme = $theme_key;
  288. }
  289. $regions = system_region_list($theme);
  290. // These are the blocks defined by code and modified by the database.
  291. $current_blocks = array();
  292. // These are {block}.bid values to be kept.
  293. $bids = array();
  294. $or = db_or();
  295. // Gather the blocks defined by modules.
  296. foreach (module_implements('block_info') as $module) {
  297. $module_blocks = module_invoke($module, 'block_info');
  298. foreach ($module_blocks as $delta => $block) {
  299. // Compile a condition to retrieve this block from the database.
  300. $condition = db_and()
  301. ->condition('module', $module)
  302. ->condition('delta', $delta);
  303. $or->condition($condition);
  304. // Add identifiers.
  305. $block['module'] = $module;
  306. $block['delta'] = $delta;
  307. $block['theme'] = $theme;
  308. $current_blocks[$module][$delta] = $block;
  309. }
  310. }
  311. // Retrieve database settings for all blocks that are defined by modules.
  312. $code_blocks = $current_blocks;
  313. $database_blocks = db_select('block', 'b')
  314. ->fields('b')
  315. ->condition($or)
  316. ->condition('theme', $theme)
  317. ->execute();
  318. foreach ($database_blocks as $block) {
  319. // Preserve info which is not in the database.
  320. $block->info = $current_blocks[$block->module][$block->delta]['info'];
  321. // The cache mode can only by set from hook_block_info(), so that has
  322. // precedence over the database's value.
  323. if (isset($current_blocks[$block->module][$block->delta]['cache'])) {
  324. $block->cache = $current_blocks[$block->module][$block->delta]['cache'];
  325. }
  326. // Blocks stored in the database override the blocks defined in code.
  327. $current_blocks[$block->module][$block->delta] = get_object_vars($block);
  328. // Preserve this block.
  329. $bids[$block->bid] = $block->bid;
  330. }
  331. drupal_alter('block_info', $current_blocks, $theme, $code_blocks);
  332. foreach ($current_blocks as $module => $module_blocks) {
  333. foreach ($module_blocks as $delta => $block) {
  334. if (!isset($block['pages'])) {
  335. // {block}.pages is type 'text', so it cannot have a
  336. // default value, and not null, so we need to provide
  337. // value if the module did not.
  338. $block['pages'] = '';
  339. }
  340. // Make sure weight is set.
  341. if (!isset($block['weight'])) {
  342. $block['weight'] = 0;
  343. }
  344. // Disable blocks that are not assigned to a region in the theme.
  345. if (!empty($block['region']) && $block['region'] != BLOCK_REGION_NONE && !isset($regions[$block['region']]) && $block['status'] == 1) {
  346. // Disabled modules are moved into the BLOCK_REGION_NONE later so no
  347. // need to move the block to another region.
  348. $block['status'] = 0;
  349. }
  350. // Set region to none if not enabled and make sure status is set.
  351. if (empty($block['status'])) {
  352. $block['status'] = 0;
  353. $block['region'] = BLOCK_REGION_NONE;
  354. }
  355. // Add to the list of blocks we return.
  356. $blocks[] = $block;
  357. }
  358. }
  359. return $blocks;
  360. }
  361. /**
  362. * Returns a list of machine names of active themes.
  363. *
  364. * @return array
  365. * An array of theme machine names.
  366. */
  367. function _fe_block_get_active_themes() {
  368. $theme_names = array();
  369. foreach (system_list('theme') as $machine_name => $theme) {
  370. if (!empty($theme->status)) {
  371. $theme_names[] = $machine_name;
  372. }
  373. }
  374. sort($theme_names);
  375. return $theme_names;
  376. }
  377. /**
  378. * Implements hook_features_revert().
  379. */
  380. function fe_block_settings_features_revert($module_name = NULL) {
  381. $component = 'fe_block_settings';
  382. $defaults = features_get_default($component, $module_name);
  383. if (empty($defaults)) {
  384. return;
  385. }
  386. // We remove the version, as we now want to deal with actual block settings.
  387. unset($defaults['version']);
  388. $themes_rehashed = array();
  389. $active_themes = _fe_block_get_active_themes();
  390. // The fallback theme for theme specific settings.
  391. $theme_default = variable_get('theme_default', 'bartik');
  392. foreach ($defaults as $block) {
  393. // Core custom blocks are prepared with a delta value.
  394. $block = _fe_block_prepare_custom_blocks_for_import($block);
  395. // Remove the additional settings from the block array, to process them
  396. // later. We explicitely set NULL, if no setting was given in the defaults.
  397. $block_themes = $block['themes'];
  398. $block_node_types = isset($block['node_types']) ? $block['node_types'] : NULL;
  399. $block_roles = isset($block['roles']) ? $block['roles'] : NULL;
  400. $block_css_class = isset($block['css_class']) ? $block['css_class'] : NULL;
  401. $block_i18n_block_language = isset($block['i18n_block_language']) ? $block['i18n_block_language'] : NULL;
  402. unset($block['themes']);
  403. unset($block['node_types']);
  404. unset($block['roles']);
  405. unset($block['css_class']);
  406. unset($block['i18n_block_language']);
  407. // Restore theme specific settings for every active theme.
  408. foreach ($active_themes as $theme) {
  409. // Rehash if we did not yet.
  410. if (empty($themes_rehashed[$theme])) {
  411. _block_rehash($theme);
  412. $themes_rehashed[$theme] = TRUE;
  413. }
  414. // Get the theme specific setting for the active theme.
  415. if (isset($block_themes[$theme])) {
  416. $key = $theme;
  417. }
  418. // Or fallback on the default theme.
  419. elseif (isset($block_themes[$theme_default])) {
  420. $key = $theme_default;
  421. }
  422. // Or fallback on the first available theme spec.
  423. else {
  424. $key = key($block_themes);
  425. }
  426. // Write block settings.
  427. $write = array_merge($block, $block_themes[$key]);
  428. drupal_write_record('block', $write, array('module', 'delta', 'theme'));
  429. }
  430. // Ensure global settings.
  431. _fe_block_settings_update_global_settings($block);
  432. // Set node type settings
  433. // (only if there were some defined, to avoid overwriting not yet exported
  434. // data).
  435. if (isset($block_node_types)) {
  436. _fe_block_settings_update_block_node_type_settings($block, $block_node_types);
  437. }
  438. // Apply role visibility settings.
  439. if (isset($block_roles)) {
  440. _fe_block_settings_update_block_roles($block, $block_roles);
  441. }
  442. // Update block CSS classes.
  443. if (isset($block_css_class) && module_exists('block_class')) {
  444. _fe_block_settings_update_block_css_class($block, $block_css_class);
  445. }
  446. // Set i18n_block languages.
  447. if (module_exists('i18n_block') && isset($block_i18n_block_language)) {
  448. _fe_block_settings_update_i18n_block_language($block, $block_i18n_block_language);
  449. }
  450. // Apply blockcache_alter settings.
  451. if (module_exists('blockcache_alter')) {
  452. _fe_block_settings_update_block_cache_alter($block);
  453. }
  454. }
  455. // Clear block cache.
  456. cache_clear_all(NULL, 'cache_block');
  457. return TRUE;
  458. }
  459. /**
  460. * Helper to update global block settings for a specific block.
  461. *
  462. * @param array $block
  463. * Block definition.
  464. */
  465. function _fe_block_settings_update_global_settings($block) {
  466. $globals = _fe_block_get_global_settings($block);
  467. // Filter out any keys that do not correspond to fields in the block table.
  468. $fields = drupal_schema_fields_sql('block');
  469. $globals = array_intersect_key($globals, array_flip($fields));
  470. db_update('block')
  471. ->fields($globals)
  472. ->condition('module', $block['module'])
  473. ->condition('delta', $block['delta'])
  474. ->execute();
  475. }
  476. /**
  477. * Helper to update node type settings for a given block.
  478. *
  479. * @param array $block
  480. * Block definition.
  481. * @param array $node_types
  482. * Array of node types.
  483. */
  484. function _fe_block_settings_update_block_node_type_settings($block, $node_types) {
  485. // First delete the old node type settings.
  486. db_delete('block_node_type')
  487. ->condition('module', $block['module'])
  488. ->condition('delta', $block['delta'])
  489. ->execute();
  490. if (!empty($node_types)) {
  491. $insert = db_insert('block_node_type')
  492. ->fields(array('module', 'delta', 'type'));
  493. foreach ($node_types as $type) {
  494. $insert->values(array(
  495. 'module' => $block['module'],
  496. 'delta' => $block['delta'],
  497. 'type' => $type,
  498. ));
  499. }
  500. $insert->execute();
  501. }
  502. }
  503. /**
  504. * Helper to update the block role settings for a given block.
  505. *
  506. * @param array $block
  507. * Block definition.
  508. * @param array $block_roles
  509. * Associative array of roles.
  510. * - key: role name.
  511. * - value: (foreign) role id.
  512. */
  513. function _fe_block_settings_update_block_roles($block, $block_roles) {
  514. static $roles;
  515. // First get the current set of roles, so we can match role names to rids.
  516. if (!isset($roles)) {
  517. $roles = db_select('role', 'r')
  518. ->fields('r', array('rid', 'name'))
  519. ->execute()
  520. ->fetchAllKeyed(1, 0);
  521. }
  522. // First delete the old block role settings.
  523. db_delete('block_role')
  524. ->condition('module', $block['module'])
  525. ->condition('delta', $block['delta'])
  526. ->execute();
  527. // Then write the new settings, if any are present.
  528. if (!empty($block_roles)) {
  529. $insert = db_insert('block_role')
  530. ->fields(array('module', 'delta', 'rid'));
  531. // We use a found flag, to avoid empty inserts if no role names match.
  532. $found = FALSE;
  533. foreach ($block_roles as $name => $rid) {
  534. // We only write for roles, matching the given role name.
  535. if (isset($roles[$name])) {
  536. $insert->values(array(
  537. 'module' => $block['module'],
  538. 'delta' => $block['delta'],
  539. 'rid' => $roles[$name],
  540. ));
  541. $found = TRUE;
  542. }
  543. }
  544. if ($found) {
  545. $insert->execute();
  546. }
  547. }
  548. }
  549. /**
  550. * Helper to update the block class settings for a given block.
  551. *
  552. * @param array $block
  553. * Block definition of the block to update.
  554. * @param string $block_css_class
  555. * List of CSS classes to apply to the block.
  556. */
  557. function _fe_block_settings_update_block_css_class($block, $block_css_class) {
  558. // This functionality is provided by the Block Class module.
  559. if (module_exists('block_class')) {
  560. // Block Class 1.x maintained its own table.
  561. if (db_table_exists('block_class')) {
  562. // First delete the old block_class settings, if any.
  563. db_delete('block_class')
  564. ->condition('module', $block['module'])
  565. ->condition('delta', $block['delta'])
  566. ->execute();
  567. // Then write the new settings, if any are present.
  568. if (!empty($block_css_class)) {
  569. db_insert('block_class')
  570. ->fields(array('module', 'delta', 'css_class'))
  571. ->values(array(
  572. 'module' => $block['module'],
  573. 'delta' => $block['delta'],
  574. 'css_class' => $block_css_class,
  575. ))
  576. ->execute();
  577. }
  578. }
  579. // Block Class 2.x extends the core Block table.
  580. elseif (db_field_exists('block', 'css_class')) {
  581. db_update('block')
  582. ->fields(array('css_class' => $block_css_class))
  583. ->condition('module', $block['module'])
  584. ->condition('delta', $block['delta'])
  585. ->execute();
  586. }
  587. }
  588. }
  589. /**
  590. * Helper to update the i18n block language settings for a specific block.
  591. *
  592. * @param array $block
  593. * Block definition.
  594. * @param array $block_languages
  595. * Array of associated languages.
  596. */
  597. function _fe_block_settings_update_i18n_block_language($block, $block_languages) {
  598. // First remove the old settings.
  599. db_delete('i18n_block_language')
  600. ->condition('module', $block['module'])
  601. ->condition('delta', $block['delta'])
  602. ->execute();
  603. // Then write the new settings.
  604. if (!empty($block_languages)) {
  605. $insert = db_insert('i18n_block_language')
  606. ->fields(array('module', 'delta', 'language'));
  607. foreach ($block_languages as $langcode) {
  608. $insert->values(array(
  609. 'module' => $block['module'],
  610. 'delta' => $block['delta'],
  611. 'language' => $langcode,
  612. ));
  613. }
  614. $insert->execute();
  615. }
  616. }
  617. /**
  618. * Helper to update the blockcache_alter settings for a specific block.
  619. *
  620. * @param array $block
  621. * Block definition.
  622. */
  623. function _fe_block_settings_update_block_cache_alter($block){
  624. $bids = db_select('block', 'b')
  625. ->fields('b', array('bid'))
  626. ->condition('module', $block['module'])
  627. ->condition('delta', $block['delta'])
  628. ->execute()
  629. ->fetchCol();
  630. $blockcache_alter = db_select('blockcache_alter', 'b')
  631. ->fields('b', array('bid'))
  632. ->condition('module', $block['module'])
  633. ->condition('delta', $block['delta'])
  634. ->execute()
  635. ->fetchCol();
  636. foreach ($bids as $bid) {
  637. $block['bid'] = $bid;
  638. if (in_array($bid, $blockcache_alter)) {
  639. drupal_write_record('blockcache_alter', $block, array('bid'));
  640. }
  641. else {
  642. drupal_write_record('blockcache_alter', $block);
  643. }
  644. }
  645. }
  646. /**
  647. * Implements hook_features_disable_feature().
  648. */
  649. function fe_block_settings_features_disable_feature($module) {
  650. }
  651. /**
  652. * Implements hook_features_enable_feature().
  653. */
  654. function fe_block_settings_features_enable_feature($module) {
  655. fe_block_settings_features_revert($module);
  656. }
  657. /**
  658. * Implements hook_features_rebuild().
  659. */
  660. function fe_block_settings_features_rebuild($module) {
  661. fe_block_settings_features_revert($module);
  662. }
  663. /**
  664. * Implements hook_features_export_options().
  665. */
  666. function fe_block_boxes_features_export_options() {
  667. $table = 'fe_block_boxes';
  668. $options = array();
  669. // Defaults.
  670. $schema = ctools_export_get_schema($table);
  671. $export = $schema['export'];
  672. $defaults = _ctools_export_get_defaults($table, $export);
  673. foreach ($defaults as $obj) {
  674. $options[$obj->machine_name] = t('@name [@machine_name]', array('@name' => $obj->info, '@machine_name' => $obj->machine_name));
  675. }
  676. // Normals.
  677. $query = "SELECT * FROM {{$table}} {$table} INNER JOIN {block_custom} b ON b.bid = {$table}.bid ORDER BY b.bid ASC";
  678. $result = db_query($query);
  679. foreach ($result as $obj) {
  680. $options[$obj->machine_name] = t('@name [@machine_name]', array('@name' => $obj->info, '@machine_name' => $obj->machine_name));
  681. }
  682. ksort($options);
  683. return $options;
  684. }
  685. /**
  686. * Implements hook_features_export().
  687. */
  688. function fe_block_boxes_features_export($data, &$export, $module_name = '') {
  689. $pipe = array();
  690. $export['dependencies']['fe_block'] = 'fe_block';
  691. $table = 'fe_block_boxes';
  692. // Add the components.
  693. foreach ($data as $object_name) {
  694. $export['features'][$table][$object_name] = $object_name;
  695. }
  696. return $pipe;
  697. }
  698. /**
  699. * Implements hook_features_export_render().
  700. */
  701. function fe_block_boxes_features_export_render($module_name = '', $data) {
  702. ctools_include('export');
  703. $component = 'fe_block_boxes';
  704. $schema = ctools_export_get_schema($component);
  705. $objects = ctools_export_load_object($component);
  706. $code = array();
  707. $code[] = ' $export = array();';
  708. $code[] = '';
  709. foreach ($data as $machine_name) {
  710. // The object to be exported.
  711. if (isset($objects[$machine_name]) && $object = $objects[$machine_name]) {
  712. $additions = array();
  713. // Load box.
  714. if (!empty($object->bid) && $box = block_custom_block_get($object->bid)) {
  715. $additions = (array) $box;
  716. unset($additions['bid'], $additions['body']);
  717. // Code.
  718. $identifier = $schema['export']['identifier'];
  719. $code[] = ctools_export_object($component, $object, ' ', $identifier, $additions) . ' $' . $identifier . '->body = ' . features_var_export($box['body']) . ';';
  720. $code[] = '';
  721. $code[] = ' $export[\'' . $machine_name . '\'] = $' . $identifier . ';';
  722. $code[] = '';
  723. }
  724. }
  725. }
  726. $code[] = ' return $export;';
  727. $code = implode("\n", $code);
  728. return array($schema['export']['default hook'] => $code);
  729. }
  730. /**
  731. * Implements hook_features_revert().
  732. */
  733. function fe_block_boxes_features_revert($module_name = NULL) {
  734. $defaults = features_get_default('fe_block_boxes', $module_name);
  735. if (empty($defaults)) {
  736. return;
  737. }
  738. // Revert.
  739. foreach ($defaults as $object) {
  740. if (empty($object->machine_name)) {
  741. continue;
  742. }
  743. $bid = fe_block_get_bid($object->machine_name);
  744. if (empty($bid) || !($box = block_custom_block_get($bid))) {
  745. $result = _fe_block_save_box((array) $object);
  746. if (!empty($result['bid'])) {
  747. $or = db_or()
  748. ->condition('bid', $result['bid'])
  749. ->condition('machine_name', $object->machine_name);
  750. db_delete('fe_block_boxes')
  751. ->condition($or)
  752. ->execute();
  753. db_insert('fe_block_boxes')
  754. ->fields(array(
  755. 'bid' => $result['bid'],
  756. 'machine_name' => $object->machine_name,
  757. ))
  758. ->execute();
  759. }
  760. }
  761. else {
  762. $object->bid = $bid;
  763. $result = _fe_block_save_box((array) $object);
  764. }
  765. }
  766. // Clear block cache.
  767. cache_clear_all(NULL, 'cache_block');
  768. return TRUE;
  769. }
  770. /**
  771. * Implements hook_features_disable_feature().
  772. */
  773. function fe_block_boxes_features_disable_feature($module) {
  774. }
  775. /**
  776. * Implements hook_features_enable_feature().
  777. */
  778. function fe_block_boxes_features_enable_feature($module) {
  779. fe_block_boxes_features_revert($module);
  780. }
  781. /**
  782. * Implements hook_features_rebuild().
  783. */
  784. function fe_block_boxes_features_rebuild($module) {
  785. fe_block_boxes_features_revert($module);
  786. }
  787. /**
  788. * Implements hook_form_alter().
  789. */
  790. function fe_block_form_alter(&$form, $form_state, $form_id) {
  791. $default_values = array();
  792. if ($form_id == 'block_add_block_form' && $form['module']['#value'] == 'block' && user_access('administer features')) {
  793. $default_values['machine_name'] = '';
  794. $default_values['bid'] = 0;
  795. }
  796. elseif ($form_id == 'block_admin_configure' && $form['module']['#value'] == 'block' && user_access('administer features')) {
  797. $bid = $form['delta']['#value'];
  798. $machine_name = fe_block_get_machine_name($bid);
  799. $default_values['machine_name'] = empty($machine_name) ? '' : $machine_name;
  800. $default_values['bid'] = $bid;
  801. }
  802. // Delete a block.
  803. elseif ($form_id == 'block_box_delete') {
  804. $form['#submit'][] = 'fe_block_machine_name_delete';
  805. }
  806. // Add & edit.
  807. if (!empty($default_values)) {
  808. $form['settings']['machine_name'] = array(
  809. '#type' => 'textfield',
  810. '#title' => t('Machine name'),
  811. '#default_value' => $default_values['machine_name'],
  812. '#maxlength' => 32,
  813. '#description' => t('Give the block a machine name to make it exportable with "!features" module.', array('!features' => l('Features', 'http://drupal.org/project/features'))),
  814. '#weight' => -50,
  815. );
  816. $form['bid'] = array(
  817. '#type' => 'value',
  818. '#value' => $default_values['bid'],
  819. );
  820. // Validate & submit.
  821. $form['#validate'][] = 'fe_block_machine_name_validate';
  822. $form['#submit'][] = 'fe_block_machine_name_submit';
  823. }
  824. }
  825. /**
  826. * Validate machine name.
  827. */
  828. function fe_block_machine_name_validate($form, &$form_state) {
  829. if (empty($form_state['values']['machine_name'])) {
  830. return;
  831. }
  832. $table = 'fe_block_boxes';
  833. $query = db_select($table)
  834. ->condition('bid', $form_state['values']['bid'], '<>')
  835. ->condition('machine_name', $form_state['values']['machine_name']);
  836. $count = $query->countQuery()->execute()->fetchField();
  837. if (!preg_match('!^[a-z0-9_]+$!', $form_state['values']['machine_name'])) {
  838. form_set_error('machine_name', t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
  839. }
  840. elseif ($count > 0) {
  841. form_set_error('machine_name', t('The machine-readable name has been taken. Please pick another one.'));
  842. }
  843. }
  844. /**
  845. * Save machine name.
  846. */
  847. function fe_block_machine_name_submit($form, &$form_state) {
  848. // If a block id is not given, retrieve it from the database.
  849. if (empty($form_state['values']['bid'])) {
  850. $form_state['values']['bid'] = db_select('block_custom')
  851. ->fields('block_custom', array('bid'))
  852. ->condition('info', $form_state['values']['info'])
  853. ->execute()->fetch()->bid;
  854. }
  855. if (empty($form_state['values']['bid'])) {
  856. return;
  857. }
  858. $table = 'fe_block_boxes';
  859. db_delete($table)
  860. ->condition('bid', $form_state['values']['bid'])
  861. ->execute();
  862. if (!empty($form_state['values']['machine_name'])) {
  863. drupal_write_record($table, $form_state['values']);
  864. }
  865. }
  866. /**
  867. * Delete machine name.
  868. */
  869. function fe_block_machine_name_delete($form, &$form_state) {
  870. $table = 'fe_block_boxes';
  871. db_delete($table)->condition('bid', $form_state['values']['bid']);
  872. }
  873. /**
  874. * Callback for usort(). Sorts blocks on "module" and "delta".
  875. */
  876. function _fe_block_compare($a, $b) {
  877. $module_cmp = strcmp($a['module'], $b['module']);
  878. if (!empty($module_cmp)) {
  879. return $module_cmp;
  880. }
  881. return strcmp($a['delta'], $b['delta']);
  882. }
  883. /**
  884. * Provided for backwards compatibility. Use fe_block_get_machine_name().
  885. */
  886. function _fe_block_get_machine_name($bid) {
  887. debug(t('The function @function is deprecated.', array('@function' => __FUNCTION__ . '()')));
  888. return fe_block_get_machine_name($bid);
  889. }
  890. /**
  891. * Returns the machine name that corresponds to a given block id.
  892. *
  893. * @param int $bid
  894. * The block id for which to retrieve the machine name.
  895. *
  896. * @return string | FALSE
  897. * The machine name, or FALSE if it could not be found.
  898. */
  899. function fe_block_get_machine_name($bid) {
  900. $machine_names = &drupal_static(__FUNCTION__);
  901. if (!isset($machine_names[$bid])) {
  902. $result = db_select('fe_block_boxes')
  903. ->fields('fe_block_boxes', array('machine_name'))
  904. ->condition('bid', $bid)
  905. ->execute()
  906. ->fetch();
  907. if (empty($result)) {
  908. return FALSE;
  909. }
  910. $machine_names[$bid] = $result->machine_name;
  911. }
  912. return $machine_names[$bid];
  913. }
  914. /**
  915. * Provided for backwards compatibility. Use fe_block_get_bid() instead.
  916. */
  917. function _fe_block_get_bid($machine_name, $reset = FALSE) {
  918. debug(t('The function @function is deprecated.', array('@function' => __FUNCTION__ . '()')));
  919. return fe_block_get_bid($machine_name, $reset);
  920. }
  921. /**
  922. * Returns the block id that corresponds to a given machine name.
  923. *
  924. * @param string $machine_name
  925. * The machine name of a block for which to retrieve the block id.
  926. *
  927. * @return int | FALSE
  928. * The block id, or FALSE if the machine name was not found.
  929. */
  930. function fe_block_get_bid($machine_name, $reset = FALSE) {
  931. $bids = &drupal_static(__FUNCTION__);
  932. if (!isset($bids[$machine_name]) || $reset) {
  933. $result = db_select('fe_block_boxes')
  934. ->fields('fe_block_boxes', array('bid'))
  935. ->condition('machine_name', $machine_name)
  936. ->execute()
  937. ->fetch();
  938. if (empty($result)) {
  939. return FALSE;
  940. }
  941. $bids[$machine_name] = (int) $result->bid;
  942. }
  943. return $bids[$machine_name];
  944. }
  945. /**
  946. * Generate block ID.
  947. */
  948. function _fe_block_build_id($block) {
  949. if (empty($block['module']) || (empty($block['delta']) && !is_numeric($block['delta']))) {
  950. return NULL;
  951. }
  952. if ($block['module'] == 'block') {
  953. $machine_name = fe_block_get_machine_name($block['delta']);
  954. if (empty($machine_name)) {
  955. return NULL;
  956. }
  957. return $block['module'] . '-' . $machine_name;
  958. }
  959. else {
  960. return $block['module'] . '-' . $block['delta'];
  961. }
  962. }
  963. /**
  964. * Save a box.
  965. *
  966. * @param array $settings
  967. * A box settings array.
  968. *
  969. * @return array
  970. * Updated settings array.
  971. */
  972. function _fe_block_save_box($settings = array()) {
  973. if (empty($settings['info'])) {
  974. return FALSE;
  975. }
  976. // 'info' must be unique.
  977. if (empty($settings['bid'])) {
  978. $conflict = db_query("SELECT COUNT(*) as count FROM {block_custom} WHERE info = :info", array('info' => $settings['info']));
  979. }
  980. else {
  981. $conflict = db_query("SELECT COUNT(*) as count FROM {block_custom} WHERE info = :info AND bid <> :bid", array('info' => $settings['info'], ':bid' => $settings['bid']));
  982. }
  983. if (!empty($conflict->fetch()->count)) {
  984. return FALSE;
  985. }
  986. // Provide some default settings.
  987. $default_settings = array(
  988. 'info' => '',
  989. 'body' => '',
  990. 'format' => 'FILTER_FORMAT_DEFAULT',
  991. );
  992. $settings = array_merge($default_settings, $settings);
  993. // Save the block settings.
  994. if (empty($settings['bid'])) {
  995. drupal_write_record('block_custom', $settings);
  996. }
  997. else {
  998. drupal_write_record('block_custom', $settings, 'bid');
  999. }
  1000. return $settings;
  1001. }
  1002. /**
  1003. * Implements hook_module_implements_alter().
  1004. */
  1005. function fe_block_module_implements_alter(&$implementations, $hook) {
  1006. if ($hook == 'default_fe_block_settings_alter') {
  1007. // Ensure fe_block is the first imlementation to be called, so we can
  1008. // convert to the newest format.
  1009. $group = $implementations['fe_block'];
  1010. unset($implementations['fe_block']);
  1011. $rest = array_reverse($implementations, TRUE);
  1012. $rest['fe_block'] = $group;
  1013. $implementations = array_reverse($rest, TRUE);
  1014. }
  1015. }
  1016. /**
  1017. * Implements hook_default_fe_block_settings_alter().
  1018. */
  1019. function fe_block_default_fe_block_settings_alter(&$defaults) {
  1020. // Convert the settings in the newest format.
  1021. $defaults = _fe_block_settings_convert($defaults);
  1022. }
  1023. /**
  1024. * Helper function to get the block roles visibility settings.
  1025. *
  1026. * @param array $block
  1027. * the block definition array
  1028. *
  1029. * @return array
  1030. * associated role settings for the block
  1031. * - key: role name
  1032. * - value: role id
  1033. */
  1034. function _fe_block_get_block_roles($block) {
  1035. $query = db_select('block_role', 'br')
  1036. ->condition('br.module', $block['module'])
  1037. ->condition('br.delta', $block['delta']);
  1038. $query->innerJoin('role', 'r', 'r.rid = br.rid');
  1039. $query->fields('r', array('name', 'rid'))
  1040. ->orderBy('r.name', 'ASC');
  1041. $roles = $query->execute()->fetchAllKeyed(0, 1);
  1042. return $roles;
  1043. }
  1044. /**
  1045. * Helper function to get block class settings.
  1046. *
  1047. * @param array $block
  1048. * The block definition.
  1049. *
  1050. * @return string
  1051. * Class name(s) for the block.
  1052. */
  1053. function _fe_block_get_block_css_class($block) {
  1054. // This functionality depends on the Block Class module.
  1055. if (module_exists('block_class')) {
  1056. // Block Class 2.x extends the core Block table, so we have the data.
  1057. if (!empty($block['css_class'])) {
  1058. return $block['css_class'];
  1059. }
  1060. // Block Class 1.x maintains its own table.
  1061. if (db_table_exists('block_class')) {
  1062. $css_class = db_select('block_class', 'b')
  1063. ->fields('b', array('css_class'))
  1064. ->condition('module', $block['module'])
  1065. ->condition('delta', $block['delta'])
  1066. ->execute()
  1067. ->fetchField();
  1068. }
  1069. }
  1070. return !empty($css_class) ? $css_class : '';
  1071. }
  1072. /**
  1073. * Get i18n block language from i18n_block.
  1074. *
  1075. * @param array $block
  1076. * Block definition array.
  1077. *
  1078. * @return array
  1079. * Array of language codes for the specified block.
  1080. */
  1081. function _fe_block_get_block_i18n_block_language($block) {
  1082. $query = db_select('i18n_block_language', 'bl')
  1083. ->condition('module', $block['module'])
  1084. ->condition('delta', $block['delta'])
  1085. ->fields('bl', array('language'))
  1086. ->orderBy('bl.language');
  1087. return $query->execute()->fetchCol();
  1088. }
  1089. /**
  1090. * Helper function to convert an older export into the new format.
  1091. *
  1092. * @param array $defaults
  1093. * array of fe_block_settings definition.
  1094. *
  1095. * @return array
  1096. * array of current fe_block_settings definition
  1097. */
  1098. function _fe_block_settings_convert($defaults) {
  1099. $version = (isset($defaults['version'])) ? $defaults['version'] : 0;
  1100. // Directly return if the version is the current one.
  1101. if ($version == FE_BLOCK_VERSION) {
  1102. return $defaults;
  1103. }
  1104. elseif ($version == '1.0') {
  1105. // We try to get the default theme for the global definitions, else we take
  1106. // the first.
  1107. $theme_default = variable_get('theme_default', 'bartik');
  1108. if (!isset($defaults['theme'][$theme_default])) {
  1109. $theme_default = key($defaults['theme']);
  1110. }
  1111. $blocks = array();
  1112. // We get the basic blocks from the visibility array.
  1113. foreach ($defaults['visibility'] as $block_id => $base) {
  1114. $node_types = array();
  1115. if (isset($base['node_type'])) {
  1116. // Node types were specified in node_type => TRUE/FALSE. Now we simply
  1117. // list the selected node types.
  1118. $node_types = array_keys(array_filter($base));
  1119. unset($base['node_type']);
  1120. }
  1121. $block = $base;
  1122. $block['node_types'] = $node_types;
  1123. // Global settings.
  1124. $globals = _fe_block_get_global_settings($defaults['theme'][$theme_default][$block_id]);
  1125. $block = array_merge($globals, $block);
  1126. // Build theme specific array.
  1127. $block['themes'] = array();
  1128. foreach ($defaults['theme'] as $theme => $items) {
  1129. $block['themes'][$theme] = _fe_block_get_theme_specific_settings($items[$block_id]);
  1130. }
  1131. $blocks[$block_id] = $block;
  1132. }
  1133. // Set current version so we can compare it with current version defaults.
  1134. $blocks['version'] = FE_BLOCK_VERSION;
  1135. return $blocks;
  1136. }
  1137. // The oldest version.
  1138. // There we got an array of themes that holded the block settings.
  1139. elseif ($version == 0) {
  1140. // We try to get the default theme for the global definitions, else we take
  1141. // the first.
  1142. $theme_default = variable_get('theme_default', 'bartik');
  1143. if (!isset($defaults[$theme_default])) {
  1144. $theme_default = key($defaults);
  1145. }
  1146. $blocks = array();
  1147. foreach ($defaults as $theme => $items) {
  1148. foreach ($items as $block_id => $item) {
  1149. // Avoid php notices.
  1150. if (!isset($blocks[$block_id])) {
  1151. $blocks[$block_id] = array(
  1152. 'themes' => array(),
  1153. );
  1154. }
  1155. // Set theme specific settings.
  1156. $blocks[$block_id]['themes'][$theme] = _fe_block_get_theme_specific_settings($item);
  1157. // We add the global settings for the default theme.
  1158. if ($theme == $theme_default) {
  1159. $globals = _fe_block_get_global_settings($item);
  1160. $blocks[$block_id] = array_merge($blocks[$block_id], $globals);
  1161. }
  1162. }
  1163. }
  1164. // Set current version so we can compare it with current version defaults.
  1165. $blocks['version'] = FE_BLOCK_VERSION;
  1166. return $blocks;
  1167. }
  1168. }