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