block.inc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <?php
  2. /**
  3. * @file
  4. * Provide Drupal blocks as content.
  5. *
  6. * Since blocks don't provide all of the features we do, we have to do a little
  7. * extra work, including providing icons and categories for core blocks. Blocks
  8. * from contrib modules get to provide their own stuff, or get relegated to
  9. * the old "Miscellaneous" category.
  10. */
  11. /**
  12. * Plugins are described by creating a $plugin array which will be used
  13. * by the system that includes this file.
  14. */
  15. $plugin = array(
  16. // And this is just the administrative title.
  17. // All our callbacks are named according to the standard pattern and can be deduced.
  18. 'title' => t('Block'),
  19. 'content type' => 'ctools_block_content_type_content_type',
  20. );
  21. /**
  22. * Return the block content types with the specified $subtype_id.
  23. */
  24. function ctools_block_content_type_content_type($subtype_id) {
  25. list($module, $delta) = explode('-', $subtype_id, 2);
  26. $module_blocks = module_invoke($module, 'block_info');
  27. if (isset($module_blocks[$delta])) {
  28. return _ctools_block_content_type_content_type($module, $delta, $module_blocks[$delta]);
  29. }
  30. }
  31. /**
  32. * Return all block content types available.
  33. *
  34. * Modules wanting to make special adjustments the way that CTools handles their blocks
  35. * can implement an extension to the hook_block() family, where the function name is
  36. * of the form "$module . '_ctools_block_info'".
  37. */
  38. function ctools_block_content_type_content_types() {
  39. $types = &drupal_static(__FUNCTION__);
  40. if (isset($types)) {
  41. return $types;
  42. }
  43. $types = array();
  44. foreach (module_implements('block_info') as $module) {
  45. $module_blocks = module_invoke($module, 'block_info');
  46. if ($module_blocks) {
  47. foreach ($module_blocks as $delta => $block) {
  48. $info = _ctools_block_content_type_content_type($module, $delta, $block);
  49. // this check means modules can remove their blocks; particularly useful
  50. // if they offer the block some other way (like we do for views)
  51. if ($info) {
  52. $types["$module-$delta"] = $info;
  53. }
  54. }
  55. }
  56. }
  57. return $types;
  58. }
  59. /**
  60. * Return an info array for a specific block.
  61. */
  62. function _ctools_block_content_type_content_type($module, $delta, $block) {
  63. // strip_tags used because it goes through check_plain and that
  64. // just looks bad.
  65. $info = array(
  66. 'title' => strip_tags($block['info']),
  67. );
  68. // Ask around for further information by invoking the hook_block() extension.
  69. $function = $module . '_ctools_block_info';
  70. if (!function_exists($function)) {
  71. $function = 'ctools_default_block_info';
  72. }
  73. $function($module, $delta, $info);
  74. return $info;
  75. }
  76. /**
  77. * Load block info from the database.
  78. *
  79. * This is copied from _block_load_blocks(). It doesn't use that
  80. * function because _block_load_blocks sorts by region, and it
  81. * doesn't cache its results anyway.
  82. */
  83. function _ctools_block_load_blocks() {
  84. if (!module_exists('block')) {
  85. return array();
  86. }
  87. $blocks = &drupal_static(__FUNCTION__, NULL);
  88. if (!isset($blocks)) {
  89. global $theme_key;
  90. $query = db_select('block', 'b');
  91. $result = $query
  92. ->fields('b')
  93. ->condition('b.theme', $theme_key)
  94. ->orderBy('b.region')
  95. ->orderBy('b.weight')
  96. ->orderBy('b.module')
  97. ->addTag('block_load')
  98. ->addTag('translatable')
  99. ->execute();
  100. $block_info = $result->fetchAllAssoc('bid');
  101. // Allow modules to modify the block list.
  102. drupal_alter('block_list', $block_info);
  103. $blocks = array();
  104. foreach ($block_info as $block) {
  105. $blocks["{$block->module}_{$block->delta}"] = $block;
  106. }
  107. }
  108. return $blocks;
  109. }
  110. /**
  111. * Fetch the stored info for a block.
  112. *
  113. * The primary reason to use this is so that modules which perform alters
  114. * can have their alters make it to the block.
  115. */
  116. function _ctools_get_block_info($module, $delta) {
  117. $blocks = _ctools_block_load_blocks();
  118. $key = $module . '_' . $delta;
  119. if (isset($blocks[$key])) {
  120. return $blocks[$key];
  121. }
  122. }
  123. /**
  124. * Output function for the 'block' content type. Outputs a block
  125. * based on the module and delta supplied in the configuration.
  126. */
  127. function ctools_block_content_type_render($subtype, $conf) {
  128. list($module, $delta) = _ctools_block_get_module_delta($subtype, $conf);
  129. $info = _ctools_get_block_info($module, $delta);
  130. $block = module_invoke($module, 'block_view', $delta);
  131. if (!empty($info)) {
  132. // Valid PHP function names cannot contain hyphens.
  133. $block_delta = str_replace('-', '_', $delta);
  134. // Allow modules to modify the block before it is viewed, via either
  135. // hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
  136. drupal_alter(array('block_view', "block_view_{$module}_{$block_delta}"), $block, $info);
  137. }
  138. if (empty($block)) {
  139. return;
  140. }
  141. $block = (object) $block;
  142. $block->module = $module;
  143. $block->delta = $delta;
  144. if (!isset($block->title)) {
  145. if ($module == 'block' && !empty($info) && isset($info->title)) {
  146. $block->title = $info->title;
  147. }
  148. else if (isset($block->subject)) {
  149. $block->title = $block->subject;
  150. }
  151. else {
  152. $block->title = NULL;
  153. }
  154. }
  155. if (module_exists('block') && user_access('administer blocks')) {
  156. $block->admin_links = array(
  157. array(
  158. 'title' => t('Configure block'),
  159. 'href' => "admin/structure/block/manage/$module/$delta/configure",
  160. 'query' => drupal_get_destination(),
  161. ),
  162. );
  163. }
  164. return $block;
  165. }
  166. /**
  167. * Empty form so we can have the default override title.
  168. */
  169. function ctools_block_content_type_edit_form($form, &$form_state) {
  170. // Does nothing!
  171. return $form;
  172. }
  173. /**
  174. * Submit function to fix the subtype for really old panel panes.
  175. */
  176. function ctools_block_content_type_edit_form_submit($form, &$form_state) {
  177. if (empty($form_state['subtype']) && isset($form_state['pane'])) {
  178. $form_state['pane']->subtype = $form_state['conf']['module'] . '-' . $form_state['conf']['delta'];
  179. unset($form_state['conf']['module']);
  180. unset($form_state['conf']['delta']);
  181. }
  182. }
  183. /**
  184. * Returns an edit form for a block.
  185. */
  186. //function ctools_block_content_type_edit_form($id, $parents, $conf) {
  187. // if (user_access('administer advanced pane settings')) {
  188. // $form['block_visibility'] = array(
  189. // '#type' => 'checkbox',
  190. // '#title' => t('Use block visibility settings (see block config)'),
  191. // '#default_value' => !empty($conf['block_visibility']),
  192. // '#description' => t('If checked, the block visibility settings for this block will apply to this block.'),
  193. // );
  194. // // Module-specific block configurations.
  195. // if ($settings = module_invoke($module, 'block', 'configure', $delta)) {
  196. // // Specifically modify a couple of core block forms.
  197. // if ($module == 'block') {
  198. // unset($settings['submit']);
  199. // $settings['info']['#type'] = 'value';
  200. // $settings['info']['#value'] = $settings['info']['#default_value'];
  201. // }
  202. // ctools_admin_fix_block_tree($settings);
  203. // $form['block_settings'] = array(
  204. // '#type' => 'fieldset',
  205. // '#title' => t('Block settings'),
  206. // '#description' => t('Settings in this section are global and are for all blocks of this type, anywhere in the system.'),
  207. // '#tree' => FALSE,
  208. // );
  209. //
  210. //
  211. // $form['block_settings'] += $settings;
  212. // }
  213. // }
  214. //
  215. // return $form;
  216. //}
  217. //function ctools_admin_submit_block(&$form_values) {
  218. // if (!empty($form_values['block_settings'])) {
  219. // module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values['block_settings']);
  220. // }
  221. //}
  222. //
  223. ///**
  224. // * Because form api cannot collapse just part of a tree, and the block settings
  225. // * assume no tree, we have to collapse the tree ourselves.
  226. // */
  227. //function ctools_admin_fix_block_tree(&$form, $key = NULL) {
  228. // if ($key) {
  229. // if (!empty($form['#parents'])) {
  230. // $form['#parents'] = array_merge(array('configuration', 'block_settings'), $form['#parents']);
  231. // }
  232. // else if (empty($form['#tree'])) {
  233. // $form['#parents'] = array('configuration', 'block_settings', $key);
  234. // }
  235. // }
  236. //
  237. // if (isset($form['#type']) && $form['#type'] == 'textarea' && !empty($form['#rows']) && $form['#rows'] > 10) {
  238. // $form['#rows'] = 10;
  239. // }
  240. //
  241. // foreach (element_children($form) as $key) {
  242. // ctools_admin_fix_block_tree($form[$key], $key);
  243. // }
  244. //}
  245. /**
  246. * Returns the administrative title for a type.
  247. */
  248. function ctools_block_content_type_admin_title($subtype, $conf) {
  249. list($module, $delta) = _ctools_block_get_module_delta($subtype, $conf);
  250. $block = module_invoke($module, 'block_info');
  251. if (empty($block) || empty($block[$delta])) {
  252. return t('Deleted/missing block @module-@delta', array('@module' => $module, '@delta' => $delta));
  253. }
  254. // The block description reported by hook_block() is plain text, but the title
  255. // reported by this hook should be HTML.
  256. $title = check_plain($block[$delta]['info']);
  257. return $title;
  258. }
  259. /**
  260. * Output function for the 'block' content type. Outputs a block
  261. * based on the module and delta supplied in the configuration.
  262. */
  263. function ctools_block_content_type_admin_info($subtype, $conf) {
  264. list($module, $delta) = _ctools_block_get_module_delta($subtype, $conf);
  265. $block = (object) module_invoke($module, 'block_view', $delta);
  266. if (!empty($block)) {
  267. // Sanitize the block because <script> tags can hose javascript up:
  268. if (!empty($block->content)) {
  269. $block->content = filter_xss_admin(render($block->content));
  270. }
  271. if (!empty($block->subject)) {
  272. $block->title = $block->subject;
  273. }
  274. elseif (empty($block->title)) {
  275. $block->title = t('No title');
  276. }
  277. return $block;
  278. }
  279. }
  280. function _ctools_block_get_module_delta($subtype, $conf) {
  281. if (strpos($subtype, '-')) {
  282. return explode('-', $subtype, 2);
  283. }
  284. else {
  285. return array($conf['module'], $conf['delta']);
  286. }
  287. }
  288. /**
  289. * Provide default icon and categories for blocks when modules don't do this
  290. * for us.
  291. */
  292. function ctools_default_block_info($module, $delta, &$info) {
  293. $core_modules = array('aggregator', 'block', 'blog', 'blogapi', 'book', 'color', 'comment', 'contact', 'drupal', 'filter', 'forum', 'help', 'legacy', 'locale', 'menu', 'node', 'path', 'ping', 'poll', 'profile', 'search', 'statistics', 'taxonomy', 'throttle', 'tracker', 'upload', 'user', 'watchdog', 'system');
  294. if (in_array($module, $core_modules)) {
  295. $info['icon'] = 'icon_core_block.png';
  296. $info['category'] = t('Miscellaneous');
  297. }
  298. else {
  299. $info['icon'] = 'icon_contrib_block.png';
  300. $info['category'] = t('Miscellaneous');
  301. }
  302. }
  303. // These are all on behalf of modules that don't implement ctools but that
  304. // we care about.
  305. function menu_ctools_block_info($module, $delta, &$info) {
  306. $info['icon'] = 'icon_core_block_menu.png';
  307. $info['category'] = t('Menus');
  308. if ($delta == 'primary-links' || $delta == 'secondary-links') {
  309. $info['icon'] = 'icon_core_primarylinks.png';
  310. }
  311. }
  312. function forum_ctools_block_info($module, $delta, &$info) {
  313. $info['category'] = t('Activity');
  314. switch ($delta) {
  315. case 'active':
  316. $info['icon'] = 'icon_core_activeforumtopics.png';
  317. break;
  318. case 'new':
  319. $info['icon'] = 'icon_core_newforumtopics.png';
  320. break;
  321. default:
  322. // safety net
  323. ctools_default_block_info($module, $delta, $info);
  324. }
  325. }
  326. function profile_ctools_block_info($module, $delta, &$info) {
  327. // Hide the author information block which isn't as rich as what we can
  328. // do with context.
  329. $info = NULL;
  330. }
  331. function book_ctools_block_info($module, $delta, &$info) {
  332. $info['title'] = t('Book navigation menu');
  333. $info['icon'] = 'icon_core_block_menu.png';
  334. $info['category'] = t('Node');
  335. }
  336. function blog_ctools_block_info($module, $delta, &$info) {
  337. $info['icon'] = 'icon_core_recentblogposts.png';
  338. $info['category'] = t('Activity');
  339. }
  340. function poll_ctools_block_info($module, $delta, &$info) {
  341. $info['icon'] = 'icon_core_recentpoll.png';
  342. $info['category'] = t('Activity');
  343. }
  344. function comment_ctools_block_info($module, $delta, &$info) {
  345. $info['icon'] = 'icon_core_recentcomments.png';
  346. $info['category'] = t('Activity');
  347. }
  348. function search_ctools_block_info($module, $delta, &$info) {
  349. $info['icon'] = 'icon_core_searchform.png';
  350. $info['category'] = t('Widgets');
  351. }
  352. function node_ctools_block_info($module, $delta, &$info) {
  353. $info['icon'] = 'icon_core_syndicate.png';
  354. $info['category'] = t('Widgets');
  355. }
  356. function aggregator_ctools_block_info($module, $delta, &$info) {
  357. $info['icon'] = 'icon_core_syndicate.png';
  358. $info['category'] = t('Feeds');
  359. }
  360. function block_ctools_block_info($module, $delta, &$info) {
  361. $info['icon'] = 'icon_core_block_empty.png';
  362. $info['category'] = t('Custom blocks');
  363. // The title of custom blocks from the block module is stored in the
  364. // {block} table. Look for it in the default theme as a reasonable
  365. // default value for the title.
  366. $block_info_cache = drupal_static(__FUNCTION__);
  367. if (!isset($block_info_cache)) {
  368. $block_info_cache = db_select('block', 'b')
  369. ->fields('b')
  370. ->condition('b.module', 'block')
  371. ->condition('b.theme', variable_get('theme_default', 'bartik'))
  372. ->addTag('block_load')
  373. ->addTag('translatable')
  374. ->execute()
  375. ->fetchAllAssoc('delta');
  376. }
  377. if (isset($block_info_cache[$delta])) {
  378. $info['defaults'] = array(
  379. 'override_title' => TRUE,
  380. 'override_title_text' => $block_info_cache[$delta]->title,
  381. );
  382. }
  383. }
  384. function user_ctools_block_info($module, $delta, &$info) {
  385. $info['category'] = t('Activity');
  386. switch ($delta) {
  387. case 'login':
  388. $info['icon'] = 'icon_core_userlogin.png';
  389. $info['category'] = t('Widgets');
  390. // Provide a custom render callback, because the default login block
  391. // will not render on /user, /user/login, or any other URL beginning
  392. // /user (unless it's a user-specific page such as /user/123).
  393. $info['render callback'] = 'ctools_user_login_pane_render';
  394. break;
  395. case 'new':
  396. $info['icon'] = 'icon_core_whosnew.png';
  397. break;
  398. case 'online':
  399. $info['icon'] = 'icon_core_whosonline.png';
  400. break;
  401. default:
  402. // safety net
  403. ctools_default_block_info($module, $delta, $info);
  404. }
  405. }
  406. function locale_ctools_block_info($module, $delta, &$info) {
  407. $info['icon'] = 'icon_core_languageswitcher.png';
  408. $info['category'] = t('Widgets');
  409. }
  410. function statistics_ctools_block_info($module, $delta, &$info) {
  411. $info['icon'] = 'icon_core_popularcontent.png';
  412. $info['category'] = t('Activity');
  413. }
  414. function system_ctools_block_info($module, $delta, &$info) {
  415. // Remove the main content fake block.
  416. if ($delta == 'main') {
  417. $info = NULL;
  418. return;
  419. }
  420. $menus = array('main-menu', 'management', 'navigation', 'user-menu');
  421. if (in_array($delta, $menus)) {
  422. $info['icon'] = 'icon_core_block_menu.png';
  423. $info['category'] = t('Menus');
  424. if ($delta == 'navigation') {
  425. $info['icon'] = 'icon_core_navigation.png';
  426. }
  427. return;
  428. }
  429. $info['icon'] = 'icon_core_drupal.png';
  430. if ($delta == 'help') {
  431. $info['category'] = t('Page elements');
  432. return;
  433. }
  434. $info['category'] = t('Widgets');
  435. }
  436. function ctools_user_login_pane_render($subtype, $conf, $panel_args, $contexts) {
  437. list($module, $delta) = _ctools_block_get_module_delta($subtype, $conf);
  438. // The login form is only visible to anonymous users.
  439. global $user;
  440. if ($user->uid) {
  441. return;
  442. }
  443. $info = new stdClass;
  444. $info->module = $module;
  445. $info->delta = $delta;
  446. $block = array();
  447. $block['subject'] = t('User login');
  448. // Manually set the content (rather than invoking block_view) because the
  449. // block implementation won't render on certain URLs.
  450. $block['content'] = drupal_get_form('user_login_block');
  451. // Allow modules to modify the block before it is viewed, via either
  452. // hook_block_view_alter() or hook_block_view_MODULE_DELTA_alter().
  453. drupal_alter(array('block_view', "block_view_{$module}_{$delta}"), $block, $info);
  454. $block = (object) $block;
  455. if (empty($block)) {
  456. return;
  457. }
  458. $block->module = $module;
  459. $block->delta = $delta;
  460. // $block->title is not set for the blocks returned by block_block() (the
  461. // Block module adds the title in block_list() instead), so we look it up
  462. // manually, unless the title is overridden and does not use the %title
  463. // placeholder.
  464. if ($module == 'block') {
  465. $block->title = $info->title;
  466. }
  467. else if (isset($block->subject)) {
  468. $block->title = $block->subject;
  469. }
  470. else {
  471. $block->title = NULL;
  472. }
  473. if (isset($block->subject)) {
  474. $block->title = $block->subject;
  475. }
  476. else {
  477. $block->title = NULL;
  478. }
  479. if (user_access('administer blocks')) {
  480. $block->admin_links = array(
  481. array(
  482. 'title' => t('Configure block'),
  483. 'href' => "admin/structure/block/manage/$module/$delta/configure",
  484. 'query' => drupal_get_destination(),
  485. ),
  486. );
  487. }
  488. return $block;
  489. }