| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: panels_renderer_standard.class.php,v 1.1.2.32 2011/01/12 23:48:44 merlinofchaos Exp $ 3 4 /** 5 * The standard render pipeline for a Panels display object. 6 * 7 * Given a fully-loaded panels_display object, this class will turn its 8 * combination of layout, panes, and styles into HTML, invoking caching 9 * appropriately along the way. Interacting with the renderer externally is 10 * very simple - just pass it the display object and call the render() method: 11 * 12 * @code 13 * // given that $display is a fully loaded Panels display object 14 * $renderer = panels_get_renderer_handler('standard', $display) 15 * $html_output = $renderer->render(); 16 * @endcode 17 * 18 * Internally, the render pipeline is divided into two phases, prepare and 19 * render: 20 * - The prepare phase transforms the skeletal data on the provided 21 * display object into a structure that is expected by the render phase. 22 * It is divided into a series of discrete sub-methods and operates 23 * primarily by passing parameters, all with the intention of making 24 * subclassing easier. 25 * - The render phase relies primarily on data stored in the renderer object's 26 * properties, presumably set in the prepare phase. It iterates through the 27 * rendering of each pane, pane styling, placement in panel regions, region 28 * styling, and finally the arrangement of rendered regions in the layout. 29 * Caching, if in use, is triggered per pane, or on the entire display. 30 * 31 * In short: prepare builds conf, render renders conf. Subclasses should respect 32 * this separation of responsibilities by adhering to these loose guidelines, 33 * given a loaded display object: 34 * - If your renderer needs to modify the datastructure representing what is 35 * to be rendered (panes and their conf, styles, caching, etc.), it should 36 * use the prepare phase. 37 * - If your renderer needs to modify the manner in which that renderable 38 * datastructure data is rendered, it should use the render phase. 39 * 40 * In the vast majority of use cases, this standard renderer will be sufficient 41 * and need not be switched out/subclassed; style and/or layout plugins can 42 * accommodate nearly every use case. If you think you might need a custom 43 * renderer, consider the following criteria/examples: 44 * - Some additional markup needs to be added to EVERY SINGLE panel. 45 * - Given a full display object, just render one pane. 46 * - Show a Panels admin interface. 47 * 48 * The system is almost functionally identical to the old procedural approach, 49 * with some exceptions (@see panels_renderer_legacy for details). The approach 50 * here differs primarily in its friendliness to tweaking in subclasses. 51 */ 52 class panels_renderer_standard { 53 /** 54 * The fully-loaded Panels display object that is to be rendered. "Fully 55 * loaded" is defined as: 56 * 1. Having been produced by panels_load_displays(), whether or this page 57 * request or at some time in the past and the object was exported. 58 * 2. Having had some external code attach context data ($display->context), 59 * in the exact form expected by panes. Context matching is delicate, 60 * typically relying on exact string matches, so special attention must 61 * be taken. 62 * 63 * @var panels_display 64 */ 65 var $display; 66 67 /** 68 * An associative array of loaded plugins. Used primarily as a central 69 * location for storing plugins that require additional loading beyond 70 * reading the plugin definition, which is already statically cached by 71 * ctools_get_plugins(). An example is layout plugins, which can optionally 72 * have a callback that determines the set of panel regions available at 73 * runtime. 74 * 75 * @var array 76 */ 77 var $plugins = array(); 78 79 /** 80 * A multilevel array of rendered data. The first level of the array 81 * indicates the type of rendered data, typically with up to three keys: 82 * 'layout', 'regions', and 'panes'. The relevant rendered data is stored as 83 * the value for each of these keys as it is generated: 84 * - 'panes' are an associative array of rendered output, keyed on pane id. 85 * - 'regions' are an associative array of rendered output, keyed on region 86 * name. 87 * - 'layout' is the whole of the rendered output. 88 * 89 * @var array 90 */ 91 var $rendered = array(); 92 93 /** 94 * A multilevel array of data prepared for rendering. The first level of the 95 * array indicates the type of prepared data. The standard renderer populates 96 * and uses two top-level keys, 'panes' and 'regions': 97 * - 'panes' are an associative array of pane objects to be rendered, keyed 98 * on pane id and sorted into proper rendering order. 99 * - 'regions' are an associative array of regions, keyed on region name, 100 * each of which is itself an indexed array of pane ids in the order in 101 * which those panes appear in that region. 102 * 103 * @var array 104 */ 105 var $prepared = array(); 106 107 /** 108 * Boolean state variable, indicating whether or not the prepare() method has 109 * been run. 110 * 111 * This state is checked in panels_renderer_standard::render_layout() to 112 * determine whether the prepare method should be automatically triggered. 113 * 114 * @var bool 115 */ 116 var $prep_run = FALSE; 117 118 /** 119 * The plugin that defines this handler. 120 */ 121 var $plugin = FALSE; 122 123 /** 124 * TRUE if this renderer is rendering in administrative mode 125 * which will allow layouts to have extra functionality. 126 * 127 * @var bool 128 */ 129 var $admin = FALSE; 130 131 /** 132 * Where to add standard meta information. There are three possibilities: 133 * - standard: Put the meta information in the normal location. Default. 134 * - inline: Put the meta information directly inline. This will 135 * not work for javascript. 136 * 137 * @var string 138 */ 139 var $meta_location = 'standard'; 140 141 /** 142 * Include rendered HTML prior to the layout. 143 * 144 * @var string 145 */ 146 var $prefix = ''; 147 148 /** 149 * Include rendered HTML after the layout. 150 * 151 * @var string 152 */ 153 var $suffix = ''; 154 155 /** 156 * Receive and store the display object to be rendered. 157 * 158 * This is a psuedo-constructor that should typically be called immediately 159 * after object construction. 160 * 161 * @param array $plugin 162 * The definition of the renderer plugin. 163 * @param panels_display $display 164 * The panels display object to be rendered. 165 */ 166 function init($plugin, &$display) { 167 $this->plugin = $plugin; 168 $layout = panels_get_layout($display->layout); 169 $this->display = &$display; 170 $this->plugins['layout'] = $layout; 171 if (!isset($layout['panels'])) { 172 $this->plugins['layout']['panels'] = panels_get_regions($layout, $display); 173 } 174 175 if (empty($this->plugins['layout'])) { 176 watchdog('panels', "Layout: @layout couldn't been found, maybe the theme is disabled.", array('@layout' => $display->layout)); 177 } 178 } 179 180 /** 181 * Prepare the attached display for rendering. 182 * 183 * This is the outermost prepare method. It calls several sub-methods as part 184 * of the overall preparation process. This compartmentalization is intended 185 * to ease the task of modifying renderer behavior in child classes. 186 * 187 * If you override this method, it is important that you either call this 188 * method via parent::prepare(), or manually set $this->prep_run = TRUE. 189 * 190 * @param mixed $external_settings 191 * An optional parameter allowing external code to pass in additional 192 * settings for use in the preparation process. Not used in the default 193 * renderer, but included for interface consistency. 194 */ 195 function prepare($external_settings = NULL) { 196 $this->prepare_panes($this->display->content); 197 $this->prepare_regions($this->display->panels, $this->display->panel_settings); 198 $this->prep_run = TRUE; 199 } 200 201 /** 202 * Prepare the list of panes to be rendered, accounting for visibility/access 203 * settings and rendering order. 204 * 205 * This method represents the standard approach for determining the list of 206 * panes to be rendered that is compatible with all parts of the Panels 207 * architecture. It first applies visibility & access checks, then sorts panes 208 * into their proper rendering order, and returns the result as an array. 209 * 210 * Inheriting classes should override this method if that renderer needs to 211 * regularly make additions to the set of panes that will be rendered. 212 * 213 * @param array $panes 214 * An associative array of pane data (stdClass objects), keyed on pane id. 215 * @return array 216 * An associative array of panes to be rendered, keyed on pane id and sorted 217 * into proper rendering order. 218 */ 219 function prepare_panes($panes) { 220 ctools_include('content'); 221 // Use local variables as writing to them is very slightly faster 222 $first = $normal = $last = array(); 223 224 // Prepare the list of panes to be rendered 225 foreach ($panes as $pid => $pane) { 226 if (empty($this->admin)) { 227 // TODO remove in 7.x and ensure the upgrade path weeds out any stragglers; it's been long enough 228 $pane->shown = !empty($pane->shown); // guarantee this field exists. 229 // If this pane is not visible to the user, skip out and do the next one 230 if (!$pane->shown || !panels_pane_access($pane, $this->display)) { 231 continue; 232 } 233 } 234 235 $content_type = ctools_get_content_type($pane->type); 236 237 // If this pane wants to render last, add it to the $last array. We allow 238 // this because some panes need to be rendered after other panes, 239 // primarily so they can do things like the leftovers of forms. 240 if (!empty($content_type['render last'])) { 241 $last[$pid] = $pane; 242 } 243 // If it wants to render first, add it to the $first array. This is used 244 // by panes that need to do some processing before other panes are 245 // rendered. 246 else if (!empty($content_type['render first'])) { 247 $first[$pid] = $pane; 248 } 249 // Otherwise, render it in the normal order. 250 else { 251 $normal[$pid] = $pane; 252 } 253 } 254 $this->prepared['panes'] = $first + $normal + $last; 255 return $this->prepared['panes']; 256 } 257 258 /** 259 * Prepare the list of regions to be rendered. 260 * 261 * This method is primarily about properly initializing the style plugin that 262 * will be used to render the region. This is crucial as regions cannot be 263 * rendered without a style plugin (in keeping with Panels' philosophy of 264 * hardcoding none of its output), but for most regions no style has been 265 * explicitly set. The logic here is what accommodates that situation: 266 * - If a region has had its style explicitly set, then we fetch that plugin 267 * and continue. 268 * - If the region has no explicit style, but a style was set at the display 269 * level, then inherit the style from the display. 270 * - If neither the region nor the dispay have explicitly set styles, then 271 * fall back to the hardcoded 'default' style, a very minimal style. 272 * 273 * The other important task accomplished by this method is ensuring that even 274 * regions without any panes are still properly prepared for the rendering 275 * process. This is essential because the way Panels loads display objects 276 * (@see panels_load_displays) results only in a list of regions that 277 * contain panes - not necessarily all the regions defined by the layout 278 * plugin, which can only be determined by asking the plugin at runtime. This 279 * method consults that retrieved list of regions and prepares all of those, 280 * ensuring none are inadvertently skipped. 281 * 282 * @param array $region_pane_list 283 * An associative array of pane ids, keyed on the region to which those pids 284 * are assigned. In the default case, this is $display->panels. 285 * @param array $settings 286 * All known region style settings, including both the top-level display's 287 * settings (if any) and all region-specific settings (if any). 288 * @return array 289 * An array of regions prepared for rendering. 290 */ 291 function prepare_regions($region_pane_list, $settings) { 292 // Initialize defaults to be used for regions without their own explicit 293 // settings. Use display settings if they exist, else hardcoded defaults. 294 $default = array( 295 'style' => panels_get_style(!empty($settings['style']) ? $settings['style'] : 'default'), 296 'style settings' => isset($settings['style_settings']['default']) ? $settings['style_settings']['default'] : array(), 297 ); 298 299 $regions = array(); 300 if (empty($settings)) { 301 // No display/panel region settings exist, init all with the defaults. 302 foreach ($this->plugins['layout']['panels'] as $region_id => $title) { 303 // Ensure this region has at least an empty panes array. 304 $panes = !empty($region_pane_list[$region_id]) ? $region_pane_list[$region_id] : array(); 305 306 $regions[$region_id] = $default; 307 $regions[$region_id]['pids'] = $panes; 308 } 309 } 310 else { 311 // Some settings exist; iterate through each region and set individually. 312 foreach ($this->plugins['layout']['panels'] as $region_id => $title) { 313 // Ensure this region has at least an empty panes array. 314 $panes = !empty($region_pane_list[$region_id]) ? $region_pane_list[$region_id] : array(); 315 316 if (empty($settings[$region_id]['style']) || $settings[$region_id]['style'] == -1) { 317 $regions[$region_id] = $default; 318 } 319 else { 320 $regions[$region_id]['style'] = panels_get_style($settings[$region_id]['style']); 321 $regions[$region_id]['style settings'] = isset($settings['style_settings'][$region_id]) ? $settings['style_settings'][$region_id] : array(); 322 } 323 $regions[$region_id]['pids'] = $panes; 324 } 325 } 326 327 $this->prepared['regions'] = $regions; 328 return $this->prepared['regions']; 329 } 330 331 /** 332 * Build inner content, then hand off to layout-specified theme function for 333 * final render step. 334 * 335 * This is the outermost method in the Panels render pipeline. It calls the 336 * inner methods, which return a content array, which is in turn passed to the 337 * theme function specified in the layout plugin. 338 * 339 * @return string 340 * Themed & rendered HTML output. 341 */ 342 function render() { 343 // Attach out-of-band data first. 344 $this->add_meta(); 345 346 if (empty($this->display->cache['method']) || !empty($this->display->skip_cache)) { 347 return $this->render_layout(); 348 } 349 else { 350 $cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context); 351 if ($cache === FALSE) { 352 $cache = new panels_cache_object(); 353 $cache->set_content($this->render_layout()); 354 panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context); 355 } 356 return $cache->content; 357 } 358 } 359 360 /** 361 * Perform display/layout-level render operations. 362 * 363 * This method triggers all the inner pane/region rendering processes, passes 364 * that to the layout plugin's theme callback, and returns the rendered HTML. 365 * 366 * If display-level caching is enabled and that cache is warm, this method 367 * will not be called. 368 * 369 * @return string 370 * The HTML string representing the entire rendered, themed panel. 371 */ 372 function render_layout() { 373 if (empty($this->prep_run)) { 374 $this->prepare(); 375 } 376 $this->render_panes(); 377 $this->render_regions(); 378 379 if ($this->admin && !empty($this->plugins['layout']['admin theme'])) { 380 $theme = $this->plugins['layout']['admin theme']; 381 } 382 else { 383 $theme = $this->plugins['layout']['theme']; 384 } 385 $this->rendered['layout'] = theme($theme, check_plain($this->display->css_id), $this->rendered['regions'], $this->display->layout_settings, $this->display, $this->plugins['layout'], $this); 386 return $this->prefix . $this->rendered['layout'] . $this->suffix; 387 } 388 389 /** 390 * Attach out-of-band page metadata (e.g., CSS and JS). 391 * 392 * This must be done before render, because panels-within-panels must have 393 * their CSS added in the right order: inner content before outer content. 394 */ 395 function add_meta() { 396 if (!empty($this->plugins['layout']['css'])) { 397 if (file_exists(path_to_theme() . '/' . $this->plugins['layout']['css'])) { 398 $this->add_css(path_to_theme() . '/' . $this->plugins['layout']['css']); 399 } 400 else { 401 $this->add_css($this->plugins['layout']['path'] . '/' . $this->plugins['layout']['css']); 402 } 403 } 404 405 if ($this->admin && isset($this->plugins['layout']['admin css'])) { 406 $this->add_css($this->plugins['layout']['path'] . '/' . $this->plugins['layout']['admin css']); 407 } 408 } 409 410 /** 411 * Add CSS information to the renderer. 412 * 413 * To facilitate previews over Views, CSS can now be added in a manner 414 * that does not necessarily mean just using drupal_add_css. Therefore, 415 * during the panel rendering process, this method can be used to add 416 * css and make certain that ti gets to the proper location. 417 * 418 * The arguments should exactly match drupal_add_css(). 419 * 420 * @see drupal_add_css 421 */ 422 function add_css($filename, $type = 'module', $media = 'all', $preprocess = TRUE) { 423 $path = file_create_path($filename); 424 switch ($this->meta_location) { 425 case 'standard': 426 if ($path) { 427 // Use CTools CSS add because it can handle temporary CSS in private 428 // filesystem. 429 ctools_include('css'); 430 ctools_css_add_css($filename, $type, $media, $preprocess); 431 } 432 else { 433 drupal_add_css($filename, $type, $media, $preprocess); 434 } 435 break; 436 case 'inline': 437 if ($path) { 438 $url = file_create_url($filename); 439 } 440 else { 441 $url = base_path() . $filename; 442 } 443 444 $this->prefix .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . $url . '" />'."\n"; 445 break; 446 } 447 } 448 449 /** 450 * Render all prepared panes, first by dispatching to their plugin's render 451 * callback, then handing that output off to the pane's style plugin. 452 * 453 * @return array 454 * The array of rendered panes, keyed on pane pid. 455 */ 456 function render_panes() { 457 ctools_include('content'); 458 459 // First, render all the panes into little boxes. 460 $this->rendered['panes'] = array(); 461 foreach ($this->prepared['panes'] as $pid => $pane) { 462 $content = $this->render_pane($pane); 463 if ($content) { 464 $this->rendered['panes'][$pid] = $content; 465 } 466 } 467 return $this->rendered['panes']; 468 } 469 470 /** 471 * Render a pane using its designated style. 472 * 473 * This method also manages 'title pane' functionality, where the title from 474 * an individual pane can be bubbled up to take over the title for the entire 475 * display. 476 * 477 * @param stdClass $pane 478 * A Panels pane object, as loaded from the database. 479 */ 480 function render_pane(&$pane) { 481 $content = $this->render_pane_content($pane); 482 if ($this->display->hide_title == PANELS_TITLE_PANE && !empty($this->display->title_pane) && $this->display->title_pane == $pane->pid) { 483 484 // If the user selected to override the title with nothing, and selected 485 // this as the title pane, assume the user actually wanted the original 486 // title to bubble up to the top but not actually be used on the pane. 487 if (empty($content->title) && !empty($content->original_title)) { 488 $this->display->stored_pane_title = $content->original_title; 489 } 490 else { 491 $this->display->stored_pane_title = !empty($content->title) ? $content->title : ''; 492 } 493 } 494 495 if (!empty($content->content)) { 496 if (!empty($pane->style['style'])) { 497 $style = panels_get_style($pane->style['style']); 498 499 if (isset($style) && isset($style['render pane'])) { 500 $output = theme($style['render pane'], $content, $pane, $this->display, $style); 501 502 // This could be null if no theme function existed. 503 if (isset($output)) { 504 return $output; 505 } 506 } 507 } 508 509 // fallback 510 return theme('panels_pane', $content, $pane, $this->display); 511 } 512 } 513 514 /** 515 * Render the interior contents of a single pane. 516 * 517 * This method retrieves pane content and produces a ready-to-render content 518 * object. It also manages pane-specific caching. 519 * 520 * @param stdClass $pane 521 * A Panels pane object, as loaded from the database. 522 * @return stdClass $content 523 * A renderable object, containing a subject, content, etc. Based on the 524 * renderable objects used by the block system. 525 */ 526 function render_pane_content(&$pane) { 527 ctools_include('context'); 528 // TODO finally safe to remove this check? 529 if (!is_array($this->display->context)) { 530 watchdog('panels', 'renderer::render_pane_content() hit with a non-array for the context', $this->display, WATCHDOG_DEBUG); 531 $this->display->context = array(); 532 } 533 534 $content = FALSE; 535 $caching = !empty($pane->cache['method']) && empty($this->display->skip_cache); 536 if ($caching && ($cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context, $pane))) { 537 $content = $cache->content; 538 } 539 else { 540 $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, array(), $this->display->args, $this->display->context); 541 foreach (module_implements('panels_pane_content_alter') as $module) { 542 $function = $module . '_panels_pane_content_alter'; 543 $function($content, $pane, $this->display->args, $this->display->context); 544 } 545 if ($caching) { 546 $cache = new panels_cache_object(); 547 $cache->set_content($content); 548 panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $pane); 549 $content = $cache->content; 550 } 551 } 552 553 // Pass long the css_id that is usually available. 554 if (!empty($pane->css['css_id'])) { 555 $content->css_id = check_plain($pane->css['css_id']); 556 } 557 558 // Pass long the css_class that is usually available. 559 if (!empty($pane->css['css_class'])) { 560 $content->css_class = check_plain($pane->css['css_class']); 561 } 562 563 return $content; 564 } 565 566 /** 567 * Render all prepared regions, placing already-rendered panes into their 568 * appropriate positions therein. 569 * 570 * @return array 571 * An array of rendered panel regions, keyed on the region name. 572 */ 573 function render_regions() { 574 $this->rendered['regions'] = array(); 575 576 // Loop through all panel regions, put all panes that belong to the current 577 // region in an array, then render the region. Primarily this ensures that 578 // the panes are arranged in the proper order. 579 $content = array(); 580 foreach ($this->prepared['regions'] as $region_id => $conf) { 581 $region_panes = array(); 582 foreach ($conf['pids'] as $pid) { 583 // Only include panes for region rendering if they had some output. 584 if (!empty($this->rendered['panes'][$pid])) { 585 $region_panes[$pid] = $this->rendered['panes'][$pid]; 586 } 587 } 588 $this->rendered['regions'][$region_id] = $this->render_region($region_id, $region_panes); 589 } 590 591 return $this->rendered['regions']; 592 } 593 594 /** 595 * Render a single panel region. 596 * 597 * Primarily just a passthrough to the panel region rendering callback 598 * specified by the style plugin that is attached to the current panel region. 599 * 600 * @param $region_id 601 * The ID of the panel region being rendered 602 * @param $panes 603 * An array of panes that are assigned to the panel that's being rendered. 604 * 605 * @return string 606 * The rendered, HTML string output of the passed-in panel region. 607 */ 608 function render_region($region_id, $panes) { 609 $style = $this->prepared['regions'][$region_id]['style']; 610 $style_settings = $this->prepared['regions'][$region_id]['style settings']; 611 612 // Retrieve the pid (can be a panel page id, a mini panel id, etc.), this 613 // might be used (or even necessary) for some panel display styles. 614 // TODO: Got to fix this to use panel page name instead of pid, since pid is 615 // no longer guaranteed. This needs an API to be able to set the final id. 616 $owner_id = 0; 617 if (isset($this->display->owner) && is_object($this->display->owner) && isset($this->display->owner->id)) { 618 $owner_id = $this->display->owner->id; 619 } 620 621 return theme($style['render region'], $this->display, $owner_id, $panes, $style_settings, $region_id, $style); 622 } 623 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |