panels_renderer_standard.class.php 27 KB

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