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