Projet

Général

Profil

Paste
Télécharger (26,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / panels / plugins / display_renderers / panels_renderer_standard.class.php @ 64156087

1
<?php
2

    
3
/**
4
 * @file
5
 */
6

    
7
/**
8
 * The standard render pipeline for a Panels display object.
9
 *
10
 * Given a fully-loaded panels_display object, this class will turn its
11
 * combination of layout, panes, and styles into HTML, invoking caching
12
 * appropriately along the way. Interacting with the renderer externally is
13
 * very simple - just pass it the display object and call the render() method:
14
 *
15
 * @code
16
 *   // given that $display is a fully loaded Panels display object
17
 *   $renderer = panels_get_renderer_handler('standard', $display)
18
 *   $html_output = $renderer->render();
19
 * @endcode
20
 *
21
 * Internally, the render pipeline is divided into two phases, prepare and
22
 * render:
23
 *   - The prepare phase transforms the skeletal data on the provided
24
 *     display object into a structure that is expected by the render phase.
25
 *     It is divided into a series of discrete sub-methods and operates
26
 *     primarily by passing parameters, all with the intention of making
27
 *     subclassing easier.
28
 *   - The render phase relies primarily on data stored in the renderer object's
29
 *     properties, presumably set in the prepare phase. It iterates through the
30
 *     rendering of each pane, pane styling, placement in panel regions, region
31
 *     styling, and finally the arrangement of rendered regions in the layout.
32
 *     Caching, if in use, is triggered per pane, or on the entire display.
33
 *
34
 * In short: prepare builds conf, render renders conf. Subclasses should respect
35
 * this separation of responsibilities by adhering to these loose guidelines,
36
 * given a loaded display object:
37
 *   - If your renderer needs to modify the datastructure representing what is
38
 *     to be rendered (panes and their conf, styles, caching, etc.), it should
39
 *     use the prepare phase.
40
 *   - If your renderer needs to modify the manner in which that renderable
41
 *     datastructure data is rendered, it should use the render phase.
42
 *
43
 * In the vast majority of use cases, this standard renderer will be sufficient
44
 * and need not be switched out/subclassed; style and/or layout plugins can
45
 * accommodate nearly every use case. If you think you might need a custom
46
 * renderer, consider the following criteria/examples:
47
 *   - Some additional markup needs to be added to EVERY SINGLE panel.
48
 *   - Given a full display object, just render one pane.
49
 *   - Show a Panels admin interface.
50
 *
51
 * The system is almost functionally identical to the old procedural approach,
52
 * with some exceptions (@see panels_renderer_legacy for details). The approach
53
 * here differs primarily in its friendliness to tweaking in subclasses.
54
 */
55
/**
56
 *
57
 */
58
class panels_renderer_standard {
59
  /**
60
   * The fully-loaded Panels display object that is to be rendered. "Fully
61
   * loaded" is defined as:
62
   *   1. Having been produced by panels_load_displays(), whether or this page
63
   *      request or at some time in the past and the object was exported.
64
   *   2. Having had some external code attach context data ($display->context),
65
   *      in the exact form expected by panes. Context matching is delicate,
66
   *      typically relying on exact string matches, so special attention must
67
   *      be taken.
68
   *
69
   * @var panels_display
70
   */
71
  var $display;
72

    
73
  /**
74
   * An associative array of loaded plugins. Used primarily as a central
75
   * location for storing plugins that require additional loading beyond
76
   * reading the plugin definition, which is already statically cached by
77
   * ctools_get_plugins(). An example is layout plugins, which can optionally
78
   * have a callback that determines the set of panel regions available at
79
   * runtime.
80
   *
81
   * @var array
82
   */
83
  var $plugins = array();
84

    
85
  /**
86
   * A multilevel array of rendered data. The first level of the array
87
   * indicates the type of rendered data, typically with up to three keys:
88
   * 'layout', 'regions', and 'panes'. The relevant rendered data is stored as
89
   * the value for each of these keys as it is generated:
90
   *  - 'panes' are an associative array of rendered output, keyed on pane id.
91
   *  - 'regions' are an associative array of rendered output, keyed on region
92
   *    name.
93
   *  - 'layout' is the whole of the rendered output.
94
   *
95
   * @var array
96
   */
97
  var $rendered = array();
98

    
99
  /**
100
   * A multilevel array of data prepared for rendering. The first level of the
101
   * array indicates the type of prepared data. The standard renderer populates
102
   * and uses two top-level keys, 'panes' and 'regions':
103
   *  - 'panes' are an associative array of pane objects to be rendered, keyed
104
   *    on pane id and sorted into proper rendering order.
105
   *  - 'regions' are an associative array of regions, keyed on region name,
106
   *    each of which is itself an indexed array of pane ids in the order in
107
   *    which those panes appear in that region.
108
   *
109
   * @var array
110
   */
111
  var $prepared = array();
112

    
113
  /**
114
   * Boolean state variable, indicating whether or not the prepare() method has
115
   * been run.
116
   *
117
   * This state is checked in panels_renderer_standard::render_layout() to
118
   * determine whether the prepare method should be automatically triggered.
119
   *
120
   * @var bool
121
   */
122
  var $prep_run = FALSE;
123

    
124
  /**
125
   * The plugin that defines this handler.
126
   */
127
  var $plugin = FALSE;
128

    
129
  /**
130
   * TRUE if this renderer is rendering in administrative mode
131
   * which will allow layouts to have extra functionality.
132
   *
133
   * @var bool
134
   */
135
  var $admin = FALSE;
136

    
137
  /**
138
   * Where to add standard meta information. There are three possibilities:
139
   * - standard: Put the meta information in the normal location. Default.
140
   * - inline: Put the meta information directly inline. This will
141
   *   not work for javascript.
142
   *
143
   * @var string
144
   */
145
  var $meta_location = 'standard';
146

    
147
  /**
148
   * Include rendered HTML prior to the layout.
149
   *
150
   * @var string
151
   */
152
  var $prefix = '';
153

    
154
  /**
155
   * Include rendered HTML after the layout.
156
   *
157
   * @var string
158
   */
159
  var $suffix = '';
160

    
161
  /**
162
   * Boolean flag indicating whether to render the layout even if all rendered
163
   * regions are blank. If FALSE, the layout renders as an empty string (without
164
   * prefix or suffix) if not in administrative mode.
165
   *
166
   * @var bool
167
   */
168
  var $show_empty_layout = TRUE;
169

    
170
  /**
171
   * Receive and store the display object to be rendered.
172
   *
173
   * This is a psuedo-constructor that should typically be called immediately
174
   * after object construction.
175
   *
176
   * @param array $plugin
177
   *   The definition of the renderer plugin.
178
   * @param panels_display $display
179
   *   The panels display object to be rendered.
180
   */
181
  function init($plugin, &$display) {
182
    $this->plugin = $plugin;
183
    $layout = panels_get_layout($display->layout);
184
    $this->display = &$display;
185
    $this->plugins['layout'] = $layout;
186
    if (!isset($layout['regions'])) {
187
      $this->plugins['layout']['regions'] = panels_get_regions($layout, $display);
188
    }
189

    
190
    if (empty($this->plugins['layout'])) {
191
      watchdog('panels', "Layout: @layout couldn't been found, maybe the theme is disabled.", array('@layout' => $display->layout));
192
    }
193
  }
194

    
195
  /**
196
   * Get the Panels storage oparation for a given renderer AJAX method.
197
   *
198
   * @param string $method
199
   *   The method name.
200
   *
201
   * @return string
202
   *   The Panels storage op.
203
   */
204
  function get_panels_storage_op_for_ajax($method) {
205
    return 'read';
206
  }
207

    
208
  /**
209
   * Prepare the attached display for rendering.
210
   *
211
   * This is the outermost prepare method. It calls several sub-methods as part
212
   * of the overall preparation process. This compartmentalization is intended
213
   * to ease the task of modifying renderer behavior in child classes.
214
   *
215
   * If you override this method, it is important that you either call this
216
   * method via parent::prepare(), or manually set $this->prep_run = TRUE.
217
   *
218
   * @param mixed $external_settings
219
   *   An optional parameter allowing external code to pass in additional
220
   *   settings for use in the preparation process. Not used in the default
221
   *   renderer, but included for interface consistency.
222
   */
223
  function prepare($external_settings = NULL) {
224
    $this->prepare_panes($this->display->content);
225
    $this->prepare_regions($this->display->panels, $this->display->panel_settings);
226
    $this->prep_run = TRUE;
227
  }
228

    
229
  /**
230
   * Prepare the list of panes to be rendered, accounting for visibility/access
231
   * settings and rendering order.
232
   *
233
   * This method represents the standard approach for determining the list of
234
   * panes to be rendered that is compatible with all parts of the Panels
235
   * architecture. It first applies visibility & access checks, then sorts panes
236
   * into their proper rendering order, and returns the result as an array.
237
   *
238
   * Inheriting classes should override this method if that renderer needs to
239
   * regularly make additions to the set of panes that will be rendered.
240
   *
241
   * @param array $panes
242
   *   An associative array of pane data (stdClass objects), keyed on pane id.
243
   *
244
   * @return array
245
   *  An associative array of panes to be rendered, keyed on pane id and sorted
246
   *  into proper rendering order.
247
   */
248
  function prepare_panes($panes) {
249
    ctools_include('content');
250
    // Use local variables as writing to them is very slightly faster.
251
    $first = $normal = $last = array();
252

    
253
    // Prepare the list of panes to be rendered.
254
    foreach ($panes as $pid => $pane) {
255
      if (empty($this->admin)) {
256
        // TODO remove in 7.x and ensure the upgrade path weeds out any stragglers; it's been long enough.
257
        $pane->shown = !empty($pane->shown);
258
        // Guarantee this field exists.
259
        // If this pane is not visible to the user, skip out and do the next one.
260
        if (!$pane->shown || !panels_pane_access($pane, $this->display)) {
261
          continue;
262
        }
263
      }
264

    
265
      // If the pane's subtype is unique, get it so that
266
      // hook_ctools_content_subtype_alter() and/or
267
      // hook_ctools_block_info() will be called.
268
      if ($pane->type != $pane->subtype) {
269
        $content_type = ctools_content_get_subtype($pane->type, $pane->subtype);
270
      }
271
      else {
272
        $content_type = ctools_get_content_type($pane->type);
273
      }
274

    
275
      // If this pane wants to render last, add it to the $last array. We allow
276
      // this because some panes need to be rendered after other panes,
277
      // primarily so they can do things like the leftovers of forms.
278
      if (!empty($content_type['render last'])) {
279
        $last[$pid] = $pane;
280
      }
281
      // If it wants to render first, add it to the $first array. This is used
282
      // by panes that need to do some processing before other panes are
283
      // rendered.
284
      elseif (!empty($content_type['render first'])) {
285
        $first[$pid] = $pane;
286
      }
287
      // Otherwise, render it in the normal order.
288
      else {
289
        $normal[$pid] = $pane;
290
      }
291
    }
292
    $this->prepared['panes'] = $first + $normal + $last;
293

    
294
    // Allow other modules the alter the prepared panes array.
295
    drupal_alter('panels_panes_prepared', $this->prepared['panes'], $this);
296

    
297
    return $this->prepared['panes'];
298
  }
299

    
300
  /**
301
   * Prepare the list of regions to be rendered.
302
   *
303
   * This method is primarily about properly initializing the style plugin that
304
   * will be used to render the region. This is crucial as regions cannot be
305
   * rendered without a style plugin (in keeping with Panels' philosophy of
306
   * hardcoding none of its output), but for most regions no style has been
307
   * explicitly set. The logic here is what accommodates that situation:
308
   *  - If a region has had its style explicitly set, then we fetch that plugin
309
   *    and continue.
310
   *  - If the region has no explicit style, but a style was set at the display
311
   *    level, then inherit the style from the display.
312
   *  - If neither the region nor the dispay have explicitly set styles, then
313
   *    fall back to the hardcoded 'default' style, a very minimal style.
314
   *
315
   * The other important task accomplished by this method is ensuring that even
316
   * regions without any panes are still properly prepared for the rendering
317
   * process. This is essential because the way Panels loads display objects
318
   * (@see panels_load_displays) results only in a list of regions that
319
   * contain panes - not necessarily all the regions defined by the layout
320
   * plugin, which can only be determined by asking the plugin at runtime. This
321
   * method consults that retrieved list of regions and prepares all of those,
322
   * ensuring none are inadvertently skipped.
323
   *
324
   * @param array $region_pane_list
325
   *   An associative array of pane ids, keyed on the region to which those pids
326
   *   are assigned. In the default case, this is $display->panels.
327
   * @param array $settings
328
   *   All known region style settings, including both the top-level display's
329
   *   settings (if any) and all region-specific settings (if any).
330
   *
331
   * @return array
332
   *   An array of regions prepared for rendering.
333
   */
334
  function prepare_regions($region_pane_list, $settings) {
335
    // Initialize defaults to be used for regions without their own explicit
336
    // settings. Use display settings if they exist, else hardcoded defaults.
337
    $default = array(
338
      'style' => panels_get_style(!empty($settings['style']) ? $settings['style'] : 'default'),
339
      'style settings' => isset($settings['style_settings']['default']) ? $settings['style_settings']['default'] : array(),
340
    );
341

    
342
    $regions = array();
343
    if (empty($settings)) {
344
      // No display/panel region settings exist, init all with the defaults.
345
      foreach ($this->plugins['layout']['regions'] as $region_id => $title) {
346
        // Ensure this region has at least an empty panes array.
347
        $panes = !empty($region_pane_list[$region_id]) ? $region_pane_list[$region_id] : array();
348

    
349
        $regions[$region_id] = $default;
350
        $regions[$region_id]['pids'] = $panes;
351
      }
352
    }
353
    else {
354
      // Some settings exist; iterate through each region and set individually.
355
      foreach ($this->plugins['layout']['regions'] as $region_id => $title) {
356
        // Ensure this region has at least an empty panes array.
357
        $panes = !empty($region_pane_list[$region_id]) ? $region_pane_list[$region_id] : array();
358
        if (empty($settings[$region_id]['style']) || $settings[$region_id]['style'] == -1) {
359
          $regions[$region_id] = $default;
360
        }
361
        else {
362
          $regions[$region_id]['style'] = panels_get_style($settings[$region_id]['style']);
363
          $regions[$region_id]['style settings'] = isset($settings['style_settings'][$region_id]) ? $settings['style_settings'][$region_id] : array();
364
        }
365
        $regions[$region_id]['pids'] = $panes;
366
      }
367
    }
368

    
369
    $this->prepared['regions'] = $regions;
370
    return $this->prepared['regions'];
371
  }
372

    
373
  /**
374
   * Build inner content, then hand off to layout-specified theme function for
375
   * final render step.
376
   *
377
   * This is the outermost method in the Panels render pipeline. It calls the
378
   * inner methods, which return a content array, which is in turn passed to the
379
   * theme function specified in the layout plugin.
380
   *
381
   * @return string
382
   *  Themed & rendered HTML output.
383
   */
384
  function render() {
385
    // Let the display refer back to the renderer.
386
    $this->display->renderer_handler = $this;
387

    
388
    // Attach out-of-band data first.
389
    $this->add_meta();
390

    
391
    if (empty($this->display->cache['method']) || !empty($this->display->skip_cache)) {
392
      return $this->render_layout();
393
    }
394
    else {
395
      $cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context);
396
      if ($cache === FALSE) {
397
        $cache = new panels_cache_object();
398
        $cache->set_content($this->render_layout());
399
        panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context);
400
      }
401
      return $cache->content;
402
    }
403
  }
404

    
405
  /**
406
   * Perform display/layout-level render operations.
407
   *
408
   * This method triggers all the inner pane/region rendering processes, passes
409
   * that to the layout plugin's theme callback, and returns the rendered HTML.
410
   *
411
   * If display-level caching is enabled and that cache is warm, this method
412
   * will not be called.
413
   *
414
   * @return string
415
   *   The HTML string representing the entire rendered, themed panel.
416
   */
417
  function render_layout() {
418
    if (empty($this->prep_run)) {
419
      $this->prepare();
420
    }
421
    $this->render_panes();
422
    $this->render_regions();
423

    
424
    if ($this->admin && !empty($this->plugins['layout']['admin theme'])) {
425
      $theme = $this->plugins['layout']['admin theme'];
426
    }
427
    else {
428
      $theme = $this->plugins['layout']['theme'];
429
    }
430

    
431
    // Determine whether to render layout.
432
    $show = TRUE;
433
    if (!$this->show_empty_layout && !$this->admin) {
434
      $show = FALSE;
435
      // Render layout if any region is not empty.
436
      foreach ($this->rendered['regions'] as $region) {
437
        if (is_string($region) && $region !== '') {
438
          $show = TRUE;
439
          break;
440
        }
441
      }
442
    }
443
    if (!$show) {
444
      return;
445
    }
446

    
447
    $this->rendered['layout'] = theme($theme, array('css_id' => check_plain($this->display->css_id), 'content' => $this->rendered['regions'], 'settings' => $this->display->layout_settings, 'display' => $this->display, 'layout' => $this->plugins['layout'], 'renderer' => $this));
448
    return $this->prefix . $this->rendered['layout'] . $this->suffix;
449
  }
450

    
451
  /**
452
   * Attach out-of-band page metadata (e.g., CSS and JS).
453
   *
454
   * This must be done before render, because panels-within-panels must have
455
   * their CSS added in the right order: inner content before outer content.
456
   */
457
  function add_meta() {
458
    global $theme;
459

    
460
    if (!empty($this->plugins['layout']['css'])) {
461
      // Do not use the path_to_theme() function, because it returns the
462
      // $GLOBALS['theme_path'] value, which may be overriden in the theme()
463
      // function when the theme hook defines the key 'theme path'.
464
      $theme_path = isset($theme) ? drupal_get_path('theme', $theme) : '';
465

    
466
      $css = $this->plugins['layout']['css'];
467
      if (!is_array($css)) {
468
        $css = array($css);
469
      }
470

    
471
      // Load each of the CSS files defined in this layout.
472
      foreach ($css as $file) {
473
        if (!empty($theme_path) && file_exists($theme_path . '/' . $file)) {
474
          $this->add_css($theme_path . '/' . $file);
475
        }
476
        else {
477
          $this->add_css($this->plugins['layout']['path'] . '/' . $file);
478
        }
479
      }
480
    }
481

    
482
    if ($this->admin && isset($this->plugins['layout']['admin css'])) {
483
      $admin_css = $this->plugins['layout']['admin css'];
484
      if (!is_array($admin_css)) {
485
        $admin_css = array($admin_css);
486
      }
487
      foreach ($admin_css as $file) {
488
        $this->add_css($this->plugins['layout']['path'] . '/' . $file);
489
      }
490
    }
491
  }
492

    
493
  /**
494
   * Add CSS information to the renderer.
495
   *
496
   * To facilitate previews over Views, CSS can now be added in a manner
497
   * that does not necessarily mean just using drupal_add_css. Therefore,
498
   * during the panel rendering process, this method can be used to add
499
   * css and make certain that ti gets to the proper location.
500
   *
501
   * The arguments should exactly match drupal_add_css().
502
   *
503
   * @see drupal_add_css
504
   */
505
  function add_css($filename) {
506
    switch ($this->meta_location) {
507
      case 'standard':
508
        drupal_add_css($filename);
509
        break;
510

    
511
      case 'inline':
512
        $url = base_path() . $filename;
513
        $this->prefix .= '<link type="text/css" rel="stylesheet" href="' . file_create_url($url) . '" />' . "\n";
514
        break;
515
    }
516
  }
517

    
518
  /**
519
   * Render all prepared panes, first by dispatching to their plugin's render
520
   * callback, then handing that output off to the pane's style plugin.
521
   *
522
   * @return array
523
   *   The array of rendered panes, keyed on pane pid.
524
   */
525
  function render_panes() {
526
    drupal_alter('panels_prerender_panes', $this);
527
    ctools_include('content');
528

    
529
    // First, render all the panes into little boxes.
530
    $this->rendered['panes'] = array();
531
    foreach ($this->prepared['panes'] as $pid => $pane) {
532
      $content = $this->render_pane($pane);
533
      if ($content) {
534
        $this->rendered['panes'][$pid] = $content;
535
      }
536
    }
537
    return $this->rendered['panes'];
538
  }
539

    
540
  /**
541
   * Render a pane using its designated style.
542
   *
543
   * This method also manages 'title pane' functionality, where the title from
544
   * an individual pane can be bubbled up to take over the title for the entire
545
   * display.
546
   *
547
   * @param object $pane
548
   *   A Panels pane object, as loaded from the database.
549
   */
550
  function render_pane(&$pane) {
551
    module_invoke_all('panels_pane_prerender', $pane);
552

    
553
    $content = $this->render_pane_content($pane);
554
    if ($this->display->hide_title == PANELS_TITLE_PANE && !empty($this->display->title_pane) && $this->display->title_pane == $pane->pid) {
555

    
556
      // If the user selected to override the title with nothing, and selected
557
      // this as the title pane, assume the user actually wanted the original
558
      // title to bubble up to the top but not actually be used on the pane.
559
      if (empty($content->title) && !empty($content->original_title)) {
560
        $this->display->stored_pane_title = $content->original_title;
561
      }
562
      else {
563
        $this->display->stored_pane_title = !empty($content->title) ? $content->title : '';
564
      }
565
    }
566

    
567
    if (!empty($content->content)) {
568
      if (!empty($pane->style['style'])) {
569
        $style = panels_get_style($pane->style['style']);
570

    
571
        if (isset($style) && isset($style['render pane'])) {
572
          $output = theme($style['render pane'], array('content' => $content, 'pane' => $pane, 'display' => $this->display, 'style' => $style, 'settings' => $pane->style['settings']));
573

    
574
          // This could be null if no theme function existed.
575
          if (isset($output)) {
576
            return $output;
577
          }
578
        }
579
      }
580

    
581
      // Fallback.
582
      return theme('panels_pane', array('content' => $content, 'pane' => $pane, 'display' => $this->display));
583
    }
584
  }
585

    
586
  /**
587
   * Render the interior contents of a single pane.
588
   *
589
   * This method retrieves pane content and produces a ready-to-render content
590
   * object. It also manages pane-specific caching.
591
   *
592
   * @param object $pane
593
   *   A Panels pane object, as loaded from the database.
594
   *
595
   * @return stdClass $content
596
   *   A renderable object, containing a subject, content, etc. Based on the
597
   *   renderable objects used by the block system.
598
   */
599
  function render_pane_content(&$pane) {
600
    ctools_include('context');
601
    // TODO finally safe to remove this check?
602
    if (!is_array($this->display->context)) {
603
      watchdog('panels', 'renderer::render_pane_content() hit with a non-array for the context', $this->display, WATCHDOG_DEBUG);
604
      $this->display->context = array();
605
    }
606

    
607
    $caching = !empty($pane->cache['method']) && empty($this->display->skip_cache);
608
    if ($caching && ($cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context, $pane))) {
609
      $content = $cache->content;
610
    }
611
    else {
612
      if ($caching) {
613
        // This is created before rendering so that calls to drupal_add_js
614
        // and drupal_add_css will be captured.
615
        $cache = new panels_cache_object();
616
      }
617

    
618
      $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, array(), $this->display->args, $this->display->context);
619

    
620
      foreach (module_implements('panels_pane_content_alter') as $module) {
621
        $function = $module . '_panels_pane_content_alter';
622
        $function($content, $pane, $this->display->args, $this->display->context, $this, $this->display);
623
      }
624
      if ($caching && isset($cache)) {
625
        $cache->set_content($content);
626
        panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $pane);
627
        $content = $cache->content;
628
      }
629
    }
630

    
631
    // If there's content, check if we've css configuration to add.
632
    if (!empty($content)) {
633
      // Pass long the css_id that is usually available.
634
      if (!empty($pane->css['css_id'])) {
635
        $id = ctools_context_keyword_substitute($pane->css['css_id'], array(), $this->display->context);
636
        $content->css_id = check_plain($id);
637
      }
638

    
639
      // Pass long the css_class that is usually available.
640
      if (!empty($pane->css['css_class'])) {
641
        $class = ctools_context_keyword_substitute($pane->css['css_class'], array(), $this->display->context, array('css safe' => TRUE));
642
        $content->css_class = check_plain($class);
643
      }
644
    }
645

    
646
    return $content;
647
  }
648

    
649
  /**
650
   * Render all prepared regions, placing already-rendered panes into their
651
   * appropriate positions therein.
652
   *
653
   * @return array
654
   *   An array of rendered panel regions, keyed on the region name.
655
   */
656
  function render_regions() {
657
    drupal_alter('panels_prerender_regions', $this);
658
    $this->rendered['regions'] = array();
659

    
660
    // Loop through all panel regions, put all panes that belong to the current
661
    // region in an array, then render the region. Primarily this ensures that
662
    // the panes are arranged in the proper order.
663
    $content = array();
664
    foreach ($this->prepared['regions'] as $region_id => $conf) {
665
      $region_panes = array();
666
      foreach ($conf['pids'] as $pid) {
667
        // Only include panes for region rendering if they had some output.
668
        if (!empty($this->rendered['panes'][$pid])) {
669
          $region_panes[$pid] = $this->rendered['panes'][$pid];
670
        }
671
      }
672
      $this->rendered['regions'][$region_id] = $this->render_region($region_id, $region_panes);
673
    }
674

    
675
    return $this->rendered['regions'];
676
  }
677

    
678
  /**
679
   * Render a single panel region.
680
   *
681
   * Primarily just a passthrough to the panel region rendering callback
682
   * specified by the style plugin that is attached to the current panel region.
683
   *
684
   * @param $region_id
685
   *   The ID of the panel region being rendered
686
   * @param $panes
687
   *   An array of panes that are assigned to the panel that's being rendered.
688
   *
689
   * @return string
690
   *   The rendered, HTML string output of the passed-in panel region.
691
   */
692
  function render_region($region_id, $panes) {
693
    $style = $this->prepared['regions'][$region_id]['style'];
694
    $style_settings = $this->prepared['regions'][$region_id]['style settings'];
695

    
696
    // Retrieve the pid (can be a panel page id, a mini panel id, etc.), this
697
    // might be used (or even necessary) for some panel display styles.
698
    // TODO: Got to fix this to use panel page name instead of pid, since pid is
699
    // no longer guaranteed. This needs an API to be able to set the final id.
700
    $owner_id = 0;
701
    if (isset($this->display->owner) && is_object($this->display->owner) && isset($this->display->owner->id)) {
702
      $owner_id = $this->display->owner->id;
703
    }
704

    
705
    $output = theme($style['render region'], array('display' => $this->display, 'owner_id' => $owner_id, 'panes' => $panes, 'settings' => $style_settings, 'region_id' => $region_id, 'style' => $style));
706
    return $output;
707
  }
708

    
709
}