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