diff --git D6UPDATE.txt D6UPDATE.txt
index 8bda859..5fad7f5 100644
--- D6UPDATE.txt
+++ D6UPDATE.txt
@@ -18,7 +18,7 @@ administrative view of a layout can now have its own theme function.
CONTENT TYPES
=============
-panels_node_legacy module renamed to panels_node_content.module
+panels_node_legacy module renamed to panels_node_content.module
-- NEED UPDATE TO RENAME IN SYSTEM TABLE.
'title callback' now has $subtype as the first argument.
@@ -49,4 +49,4 @@ Moved to CTOOLS
When argument plugins fail to load a context at runtime, they must now return
error codes instead of FALSE or NULL (previously the practice). The error codes,
-and their respective documentation, can be found at the top of panels.module.
\ No newline at end of file
+and their respective documentation, can be found at the top of panels.module.
\ No newline at end of file
diff --git includes/display-render.inc includes/display-render.inc
index 7f327f9..9a969ba 100644
--- includes/display-render.inc
+++ includes/display-render.inc
@@ -8,77 +8,6 @@
*/
/**
- * Render a display by loading the content into an appropriate
- * array and then passing through to panels_render_layout.
- *
- * if $incoming_content is NULL, default content will be applied. Use
- * an empty string to indicate no content.
- * @render
- * @ingroup hook_invocations
- */
-function _panels_render_display(&$display) {
- $layout = panels_get_layout($display->layout);
- if (!$layout) {
- return NULL;
- }
-
- $output = '';
-
- // Let modules act just prior to render.
- foreach (module_implements('panels_pre_render') as $module) {
- $function = $module . '_panels_pre_render';
- $output .= $function($display);
- }
-
- $output .= panels_render_layout($layout, $display, $display->css_id, $display->layout_settings);
-
- // Let modules act just after render.
- foreach (module_implements('panels_post_render') as $module) {
- $function = $module . '_panels_post_render';
- $output .= $function($display);
- }
- return $output;
-}
-
-/**
- * Given a full layout structure and a content array, render a panel display.
- * @render
- */
-function panels_render_layout($layout, $content, $css_id = NULL, $settings = array(), $display = NULL) {
- if (!empty($layout['css'])) {
- if (file_exists(path_to_theme() . '/' . $layout['css'])) {
- drupal_add_css(path_to_theme() . '/' . $layout['css']);
- }
- else {
- drupal_add_css($layout['path'] . '/' . $layout['css']);
- }
- }
- // This now comes after the CSS is added, because panels-within-panels must
- // have their CSS added in the right order; inner content before outer content.
-
- // If $content is an object, it's a $display and we have to render its panes.
- if (is_object($content)) {
- $display = $content;
- if (empty($display->cache['method'])) {
- $content = panels_render_panes($display);
- }
- else {
- $cache = panels_get_cached_content($display, $display->args, $display->context);
- if ($cache === FALSE) {
- $cache = new panels_cache_object();
- $cache->set_content(panels_render_panes($display));
- panels_set_cached_content($cache, $display, $display->args, $display->context);
- }
- $content = $cache->content;
- }
- }
-
- $output = theme($layout['theme'], check_plain($css_id), $content, $settings, $display);
-
- return $output;
-}
-
-/**
* Render the administrative layout of a display.
*
* This is used for the edit version, so that layouts can have different
@@ -104,91 +33,12 @@ function panels_render_layout_admin($layout, $content, $display) {
}
/**
- * Render all the panes in a display into a $content array to be used by
- * the display theme function.
- */
-function panels_render_panes(&$display) {
- ctools_include('content');
-
- $keywords = array();
-
- // First, render all the panes into little boxes. We do this here because
- // some panes request to be rendered after other panes (primarily so they
- // can do the leftovers of forms).
- $panes = array();
- $later = array();
-
- foreach ((array) $display->content as $pid => $pane) {
- $pane->shown = !empty($pane->shown); // guarantee this field exists.
- // If the user can't see this pane, do not render it.
- if (!$pane->shown || !panels_pane_access($pane, $display)) {
- continue;
- }
-
- // If this pane wants to render last, add it to the $later array.
- $content_type = ctools_get_content_type($pane->type);
-
- if (!empty($content_type['render last'])) {
- $later[$pid] = $pane;
- continue;
- }
-
- $panes[$pid] = panels_render_pane_content($display, $pane, $keywords);
- }
-
- foreach ($later as $pid => $pane) {
- $panes[$pid] = panels_render_pane_content($display, $pane, $keywords);
- }
-
- // Loop through all panels, put all panes that belong to the current panel
- // in an array, then render the panel. Primarily this ensures that the
- // panes are in the proper order.
- $content = array();
- foreach ($display->panels as $panel_name => $pids) {
- $panel = array();
- foreach ($pids as $pid) {
- if (!empty($panes[$pid])) {
- $panel[$pid] = $panes[$pid];
- }
- }
- $content[$panel_name] = panels_render_panel($display, $panel_name, $panel);
- }
-
- // Prevent notices by making sure that all panels at least have an entry:
- $layout = panels_get_layout($display->layout);
- $panels = panels_get_panels($layout, $display);
- foreach ($panels as $id => $panel) {
- if (!isset($content[$id])) {
- $content[$id] = NULL;
- }
- }
-
- return $content;
-}
-
-/**
- * Render a single pane, identifying its context, and put it into
- * the $panes array.
- */
-function panels_render_pane_content(&$display, &$pane, $keywords) {
- $content = panels_get_pane_content($display, $pane, $keywords, $display->args, $display->context, $display->incoming_content);
-
- // Pass long the css_id that is usually available.
- if (!empty($pane->css['css_id'])) {
- $content->css_id = $pane->css['css_id'];
- }
-
- // Pass long the css_class that is usually available.
- if (!empty($pane->css['css_class'])) {
- $content->css_class = $pane->css['css_class'];
- }
-
- return $content;
-}
-
-/**
* Render a pane using the appropriate style.
*
+ * Legacy function; this behavior has been moved onto the display renderer
+ * object. The function name here is included for backwards compatibility. New
+ * style plugins should NEVER call it.
+ *
* $content
* The already rendered content via panels_render_pane_content()
* $pane
@@ -223,7 +73,7 @@ function panels_render_pane($content, $pane, &$display) {
}
}
- if (!empty($content->content)) {
+ if (!empty($content)) {
// fallback
return theme('panels_pane', $content, $pane, $display);
}
@@ -253,64 +103,3 @@ function panels_get_panel_style_and_settings($panel_settings, $panel) {
return array($style, $style_settings);
}
-
-/**
- * Render a panel, by storing the content of each pane in an appropriate array
- * and then passing through to the theme function that will render the panel
- * in the configured panel style.
- *
- * @param $display
- * A display object.
- * @param $panel
- * The ID of the panel being rendered
- * @param $panes
- * An array of panes that are assigned to the panel that's being rendered.
- *
- * @return
- * The rendered HTML for a panel.
- * @render
- */
-function panels_render_panel($display, $panel, $panes) {
- list($style, $style_settings) = panels_get_panel_style_and_settings($display->panel_settings, $panel);
-
- // Retrieve the pid (can be a panel page id, a mini panel id, etc.), this
- // might be used (or even necessary) for some panel display styles.
- // TODO: Got to fix this to use panel page name instead of pid, since pid is
- // no longer guaranteed. This needs an API to be able to set the final id.
- $owner_id = 0;
- if (isset($display->owner) && is_object($display->owner) && isset($display->owner->id)) {
- $owner_id = $display->owner->id;
- }
-
- return theme($style['render panel'], $display, $owner_id, $panes, $style_settings, $panel);
-}
-
-/**
- * Get the title from a display.
- *
- * The display must have already been rendered, or the setting to set the display's title
- * from a pane's title will not have worked.
- *
- * @param $display
- * The display to get the title from.
- *
- * @return
- * The title to use. If NULL, this means to let any default title that may be in use
- * pass through. i.e, do not actually set the title.
- */
-function panels_display_get_title($display) {
- switch ($display->hide_title) {
- case PANELS_TITLE_NONE:
- return '';
-
- case PANELS_TITLE_PANE:
- return isset($display->stored_pane_title) ? $display->stored_pane_title : '';
-
- case PANELS_TITLE_FIXED:
- case FALSE; // For old exported panels that are not in the database.
- if (!empty($display->title)) {
- return filter_xss_admin(ctools_context_keyword_substitute($display->title, array(), $display->context));
- }
- return NULL;
- }
-}
diff --git includes/legacy.inc includes/legacy.inc
new file mode 100644
index 0000000..5ba90b5
--- /dev/null
+++ includes/legacy.inc
@@ -0,0 +1,71 @@
+legacy)) {
+ $this->determineStatus();
+ }
+ return $this->legacy;
+ }
+
+ /**
+ * Run all compatibility checks.
+ */
+ function determineStatus() {
+ $this->legacy = array();
+ foreach(get_class_methods($this) as $method) {
+ if (strtolower(substr($method, 0, 5)) == 'check') {
+ $this->legacy[$method] = $this->$method();
+ }
+ }
+ $this->legacy = array_filter($this->legacy);
+ }
+
+ /**
+ * Compatibility checker that ensures modules that implement Panels styles
+ * list their api as being at least 2.0; this corresponds to the change with
+ * the initial IPE commit that made region styles take a fully rendered pane
+ * HTML string instead of a pane object that still needed rendering.
+ */
+ function checkStylesIPE1() {
+ $legacy_info = array(
+ 'explanation' => $this->t('Panels 3.6 made changes to the rendering order in a way that affects certain style plugins. The above modules implement style plugins, but have not indicated their compatibility with this new system. See !link for information on how to update style plugins to the new system.', array('!link' => url('http://drupal.org/node/###FIXME', array('external' => TRUE)))),
+ 'modules' => array(),
+ );
+
+
+ $naughties = &$legacy_info['modules'];
+ $legacy = FALSE;
+
+ ctools_include('plugins', 'panels');
+ // TODO given that the plugin cache is also clearing at this time, should
+ // check this to ensure this isn't causing some kind of weird race condition
+ $styles = panels_get_styles();
+
+ foreach ($styles as $style) {
+ if (version_compare($style['version'], 2.0, '<') && empty($naughties[$style['module']])) {
+ $legacy = TRUE;
+ $naughties[$style['module']] = $this->t('Style plugins');
+ }
+ }
+ variable_set('panels_legacy_rendering_mode', $legacy);
+ return $legacy ? $legacy_info : array();
+ }
+}
diff --git includes/plugins.inc includes/plugins.inc
index 63936e8..5ed1622 100644
--- includes/plugins.inc
+++ includes/plugins.inc
@@ -34,50 +34,6 @@ function panels_get_panels($layout, $display) {
}
/**
- * Get the content from a given pane.
- *
- * @param $pane
- * The pane to retrieve content from.
- * @param $args
- * The arguments sent to the display.
- * @param $context
- * The panels context.
- * @param $incoming_content
- * Any incoming content if this display is a wrapper.
- */
-function panels_get_pane_content($display, $pane, $keywords, $args = array(), $context = array(), $incoming_content = '') {
- ctools_include('context');
- if (!is_array($context)) {
- $context = array();
- }
-
- if (!$incoming_content === '') {
- $incoming_content = t('Incoming content will be displayed here.');
- }
-
- $content = FALSE;
- $caching = !empty($pane->cache['method']) ? TRUE : FALSE;
- if ($caching && ($cache = panels_get_cached_content($display, $args, $context, $pane))) {
- $content = $cache->content;
- }
- else {
- $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, $keywords, $args, $context, $incoming_content);
- foreach (module_implements('panels_pane_content_alter') as $module) {
- $function = $module . '_panels_pane_content_alter';
- $function($content, $pane, $args, $context);
- }
- if ($caching) {
- $cache = new panels_cache_object();
- $cache->set_content($content);
- panels_set_cached_content($cache, $display, $args, $context, $pane);
- $content = $cache->content;
- }
- }
-
- return $content;
-}
-
-/**
* Get cached content for a given display and possibly pane.
*
* @return
@@ -389,6 +345,30 @@ function panels_get_caches() {
}
/**
+ * Fetch metadata on a specific display renderer plugin.
+ *
+ * @return
+ * An array of arrays with information about the requested panels display
+ * renderer.
+ */
+function panels_get_display_renderer($dr) {
+ ctools_include('plugins');
+ return ctools_get_plugins('panels', 'display_renderers', $dr);
+}
+
+/**
+ * Fetch metadata for all display renderer plugins.
+ *
+ * @return
+ * An array of arrays with information about all available panels display
+ * renderer.
+ */
+function panels_get_display_renderers() {
+ ctools_include('plugins');
+ return ctools_get_plugins('panels', 'display_renderers');
+}
+
+/**
* Get a function from a plugin, if it exists.
*
* @param $plugin
diff --git panels.install panels.install
index aaef6c7..5b8e203 100644
--- panels.install
+++ panels.install
@@ -5,11 +5,19 @@
* Test requirements for installation and running.
*/
function panels_requirements($phase) {
+ $function = "panels_requirements_$phase";
+ return function_exists($function) ? $function() : array();
+}
+
+/**
+ * Check install-time requirements.
+ */
+function panels_requirements_install() {
$requirements = array();
$t = get_t();
// Assume that if the user is running an installation profile that both
// Panels and CTools are the same release.
- if ($phase == 'install' && !(defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install')) {
+ if (!(defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install')) {
// apparently the install process doesn't include .module files,
// so we need to force the issue in order for our versioning
// check to work.
@@ -35,6 +43,33 @@ function panels_requirements($phase) {
}
/**
+ * Check runtime requirements (status report).
+ */
+function panels_requirements_runtime() {
+ $requirements = array();
+ $legacy = panels_get_legacy_state();
+ $t = get_t();
+ $state = $legacy->getStatus();
+ if (empty($state)) {
+ $requirements['panels_legacy'] = array(
+ 'title' => $t('Panels operating normally'),
+ 'severity' => REQUIREMENT_OK,
+ 'description' => $t('Panels is operating normally - no out-of-date plugins or modules are forcing it into legacy mode'),
+ );
+ }
+ else {
+ $description = $t("Panels is operating in Legacy mode due to the following issues:\n");
+
+ $requirements['panels_legacy'] = array(
+ 'title' => $t('Panels operating in Legacy mode'),
+ 'severity' => REQUIREMENT_WARNING,
+ 'description' => $description,
+ );
+ }
+ return $requirements;
+}
+
+/**
* Implementation of hook_schema().
*/
function panels_schema() {
@@ -1289,3 +1324,11 @@ function panels_update_6306() {
return $ret;
}
+
+/**
+ * See if Panels needs to operate in legacy rendering mode, and produce a
+ * warning if it does.
+ */
+function panels_update_6307() {
+
+}
\ No newline at end of file
diff --git panels.module panels.module
index 13f6fa1..bba2ced 100644
--- panels.module
+++ panels.module
@@ -19,7 +19,7 @@ define('PANELS_TITLE_PANE', 2); // And this is the new behavior, where the title
* @return An array with the major and minor versions
*/
function panels_api_version() {
- return array(3, 0);
+ return array(3, 1);
}
/**
@@ -473,6 +473,7 @@ class panels_display {
var $css_id = NULL;
var $context = array();
var $did = 'new';
+ var $renderer = 'panels_renderer_default';
function add_pane($pane, $location = FALSE) {
$pane->pid = $this->next_new_pid();
@@ -514,6 +515,74 @@ class panels_display {
$next_id = max($id);
return ++$next_id;
}
+
+ /**
+ * Get the title from a display.
+ *
+ * The display must have already been rendered, or the setting to set the
+ * display's title from a pane's title will not have worked.
+ *
+ * @return
+ * The title to use. If NULL, this means to let any default title that may be in use
+ * pass through. i.e, do not actually set the title.
+ */
+ function get_title() {
+ switch ($this->hide_title) {
+ case PANELS_TITLE_NONE:
+ return '';
+
+ case PANELS_TITLE_PANE:
+ return isset($this->stored_pane_title) ? $this->stored_pane_title : '';
+
+ case PANELS_TITLE_FIXED:
+ case FALSE; // For old exported panels that are not in the database.
+ if (!empty($this->title)) {
+ return filter_xss_admin(ctools_context_keyword_substitute($this->title, array(), $this->context));
+ }
+ return NULL;
+ }
+ }
+
+ /**
+ * Render this panels display.
+ *
+ * After checking to ensure the designated layout plugin is valid, a
+ * display renderer object is spawned and runs its rendering logic.
+ *
+ * @param mixed $renderer
+ * An instanciated display renderer object, or the name of a display
+ * renderer plugin+class to be fetched. Defaults to NULL. When NULL, the
+ * predesignated display renderer will be used.
+ */
+ function render($renderer = NULL) {
+ $layout = panels_get_layout($this->layout);
+ if (!$layout) {
+ return NULL;
+ }
+
+ if (!is_object($renderer)) {
+ $renderer = is_string($renderer) ? $renderer : $this->renderer;
+ $renderer_class = ctools_plugin_load_class('panels', 'display_renderers', $renderer, 'handler');
+ $renderer = new $renderer_class();
+ $renderer->build($this);
+ }
+
+ $output = '';
+ // Let modules act just prior to render.
+ foreach (module_implements('panels_pre_render') as $module) {
+ $function = $module . '_panels_pre_render';
+ $output .= $function($display, $renderer);
+ }
+
+ $output .= $renderer->render();
+
+ // Let modules act just after render.
+ foreach (module_implements('panels_post_render') as $module) {
+ $function = $module . '_panels_post_render';
+ $output .= $function($display, $renderer);
+ }
+ return $output;
+ }
}
/**
@@ -809,10 +878,9 @@ function panels_export_display($display, $prefix = '') {
*
* if $incoming_content is NULL, default content will be applied. Use
* an empty string to indicate no content.
- * @render
* @ingroup hook_invocations
*/
-function panels_render_display(&$display) {
+function panels_render_display(&$display, $renderer = NULL) {
ctools_include('display-render', 'panels');
ctools_include('plugins', 'panels');
ctools_include('context');
@@ -825,7 +893,7 @@ function panels_render_display(&$display) {
return drupal_render_form($form_context->form_id, $form_context->form);
}
}
- return _panels_render_display($display);
+ return $display->render($renderer);
}
@@ -861,7 +929,7 @@ function panels_print_layout($id, $content) {
* then operate as a theme function of the form.
*/
function theme_panels_render_display_form($form) {
- $form['#children'] = _panels_render_display($form['#display']);
+ $form['#children'] = $form['#display']->render();
drupal_render($form);
return theme('form', $form);
}
@@ -947,10 +1015,76 @@ function panels_ctools_plugin_styles() {
return array(
'load themes' => TRUE,
'use hooks' => TRUE,
+ 'process' => 'panels_plugin_styles_process',
);
}
/**
+ * Implementation of hook_ctools_plugin_api().
+ *
+ * Inform CTools about version information for various plugins implemented by
+ * Panels.
+ *
+ * @param string $owner
+ * The system name of the module owning the API about which information is
+ * being requested.
+ * @param string $api
+ * The name of the API about which information is being requested.
+ */
+function panels_ctools_plugin_api($owner, $api) {
+ if ($owner == 'panels' && $api = 'styles') {
+ // As of 6.x-3.6, Panels has a slightly new system for style plugins.
+ return array('version' => 2.0);
+ }
+}
+
+/**
+ * Perform additional processing on a style plugin.
+ *
+ * Currently this is only being used to apply versioning information to style
+ * plugins in order to ensure the legacy renderer passes the right type of
+ * parameters to a style plugin in a hybrid environment of both new and old
+ * plugins.
+ *
+ * @see _ctools_process_data()
+ *
+ * @param array $plugin
+ * The style plugin that is being processed.
+ * @param array $info
+ * The style plugin type info array.
+ */
+function panels_plugin_styles_process(&$plugin, $info) {
+ $compliant_modules = ctools_plugin_api_info('panels', 'styles', 2.0, 2.0);
+ $plugin['version'] = empty($compliant_modules[$plugin['module']]) ? 1.0 : $compliant_modules[$plugin['module']]['version'];
+}
+
+/**
+ * Gateway to the PanelsLegacyState class/object, which does all legacy state
+ * checks and provides information about the cause of legacy states as needed.
+ *
+ * @return PanelsLegacyState $legacy
+ */
+function panels_get_legacy_state() {
+ static $legacy = NULL;
+ if (!isset($legacy)) {
+ ctools_include('legacy', 'panels');
+ $legacy = new PanelsLegacyState();
+ }
+ return $legacy;
+}
+
+/**
+ * Implementation of hook_flush_caches().
+ *
+ * We implement this so that we can be sure our legacy rendering state setting
+ * in $conf is updated whenever caches are cleared.
+ */
+function panels_flush_caches() {
+ $legacy = panels_get_legacy_state();
+ $legacy->determineStatus();
+}
+
+/**
* Get the display that is currently being rendered as a page.
*
* Unlike in previous versions of this, this only returns the display,
diff --git panels_ipe/images/dragger.png panels_ipe/images/dragger.png
new file mode 100644
index 0000000..bb3b57b
Binary files /dev/null and panels_ipe/images/dragger.png differ
diff --git panels_ipe/panels_ipe.css panels_ipe/panels_ipe.css
new file mode 100644
index 0000000..424cf55
--- /dev/null
+++ panels_ipe/panels_ipe.css
@@ -0,0 +1,141 @@
+div.panels-ipe-handlebar-wrapper {
+ margin-top: 1em;
+ border-bottom: #999 solid 1px;
+}
+
+div.panels-ipe-handlebar-wrapper ul {
+ float: left;
+ margin: 0;
+ padding: 0;
+ text-align: right;
+}
+
+div.panels-ipe-handlebar-wrapper li,
+div.panels-ipe-draghandle {
+ background: none repeat scroll 0 0 transparent;
+ list-style: none outside none;
+ margin: 0;
+ padding: 0;
+ float: left;
+}
+
+div.panels-ipe-handlebar-wrapper li {
+ border-top: 1px solid #CCC;
+ border-right: 1px solid #CCC;
+}
+
+div.panels-ipe-handlebar-wrapper li.first {
+ border-left: 1px solid #CCC;
+}
+
+div.panels-ipe-draghandle {
+ float: right;
+ border-right: none;
+ border-left: 1px solid #AAA;
+}
+
+/* Hide editor-state-on elements initially */
+.panels-ipe-on {
+ display: none;
+}
+
+.panels-ipe-editing .panels-ipe-on {
+ display: block;
+}
+
+/* Show editor-state-off elements initially */
+.panels-ipe-off {
+ display: block;
+}
+
+div.panels-ipe-handlebar-wrapper li a,
+div.panels-ipe-draghandle span,
+div.panels-ipe-newblock a {
+ background-color: #f6f6f6;
+ display: block;
+ padding: 0.2em 0.5em;
+}
+
+div.panels-ipe-newblock a {
+ display: inline;
+ border: 1px solid #CCC;
+}
+
+.panels-ipe-editing .panels-ipe-portlet-content {
+ margin-bottom: 10px;
+ border: transparent dotted 1px;
+ overflow: hidden;
+}
+
+.panels-ipe-editing .panels-ipe-portlet-content:hover {
+ border: #FF000A dotted 1px;
+}
+
+.panels-ipe-editing .panels-ipe-region {
+ border: transparent dotted 1px;
+ float: left;
+ width: 100%;
+ margin-bottom: 5px;
+}
+
+div.panels-ipe-draghandle {
+ border: none;
+}
+
+div.panels-ipe-draghandle span {
+ border: none;
+ background: #989896 url(images/dragger.png);
+ text-indent: -9999px;
+ width: 26px;
+ height: 17px;
+ padding-left: 0;
+ padding-right: 0;
+ padding-bottom: 0.25em;
+}
+
+.ui-sortable-placeholder { margin: 1em; border: 1px dotted black; visibility: visible !important; height: 50px !important; }
+.ui-sortable-placeholder * { visibility: hidden; }
+
+/* counteract panels_dnd.css - temporary */
+div.panels-ipe-display-container .panel-pane .pane-title {
+ padding: 0;
+}
+
+/** ============================================================================
+ * Controller form markup
+ */
+
+div#panels-ipe-control-container {
+ z-index: 100;
+ position: fixed;
+ margin: auto;
+ bottom: 0;
+ left: 50%;
+ display: block;
+ background-color: #000;
+ padding: 0.5em 1em;
+ -moz-border-radius-topleft:5px;
+ -moz-border-radius-topright:5px;
+ -moz-box-shadow: #333 0px 1px 0px;
+ -webkit-border-radius-topleft:5px;
+ -webkit-border-radius-topright:5px;
+ -webkit-box-shadow: #333 0px 1px 0px;
+}
+
+div.panels-ipe-pseudobutton {
+ cursor: pointer;
+ background-color: #333;
+ font:normal 11px/15px "Lucida Grande",Tahoma,Verdana,sans-serif;
+ color: #FFF;
+ -moz-border-radius:5px;
+ -moz-box-shadow: #333 0px 1px 0px;
+ -webkit-border-radius:5px;
+ -webkit-box-shadow: #333 0px 1px 0px;
+ padding: 0.3em 0.8em;
+ float: left;
+}
+
+div.panels-ipe-control .form-submit {
+ float: left;
+ margin: 0.3em 0.5em;
+}
diff --git panels_ipe/panels_ipe.info panels_ipe/panels_ipe.info
new file mode 100644
index 0000000..c27a2a9
--- /dev/null
+++ panels_ipe/panels_ipe.info
@@ -0,0 +1,7 @@
+; $Id$
+name = Panels In-Place Editor
+description = Provide a UI for managing some Panels directly on the frontend, instead of having to use the backend.
+package = "Panels"
+dependencies[] = panels
+dependencies[] = jquery_ui
+core = 6.x
diff --git panels_ipe/panels_ipe.js panels_ipe/panels_ipe.js
new file mode 100644
index 0000000..d85686e
--- /dev/null
+++ panels_ipe/panels_ipe.js
@@ -0,0 +1,174 @@
+// $Id$
+
+// Ensure the $ alias is owned by jQuery
+(function($) {
+
+Drupal.PanelsIPE = {
+ editors: {},
+ bindClickDelete: function(context) {
+ $('a.pane-delete:not(.pane-delete-processed)', context)
+ .addClass('pane-delete-processed')
+ .click(function() {
+ if (confirm('Remove this pane?')) {
+ $(this).parents('div.panels-ipe-portlet-wrapper').fadeOut(1000, function() {
+ $(this).empty().remove();
+ });
+ }
+ return false;
+ });
+ },
+}
+
+// A ready function should be sufficient for this, at least for now
+$(function() {
+ $.each(Drupal.settings.PanelsIPECacheKeys, function() {
+ Drupal.PanelsIPE.editors[this] = new DrupalPanelsIPE(this, Drupal.settings.PanelsIPESettings[this]);
+ });
+});
+
+Drupal.behaviors.PanelsIPE = function(context) {
+ Drupal.PanelsIPE.bindClickDelete(context);
+};
+
+/**
+ * Base object (class) definition for the Panels In-Place Editor.
+ *
+ * A new instance of this object is instanciated whenever an IPE is
+ * initiated, and destroyed when editing is concluded (successfully or not).
+ *
+ * @param {string} cache_key
+ */
+function DrupalPanelsIPE(cache_key, cfg) {
+ var ipe = this;
+ this.key = cache_key;
+ this.state = {};
+ this.topParent = $('div#panels-ipe-display-'+cache_key);
+ this.control = $('div#panels-ipe-control-'+ cache_key);
+ this.initButton = $('div.panels-ipe-startedit', this.control);
+ this.cfg = cfg;
+
+ this.initEditing = function(formdata) {
+ // See http://jqueryui.com/demos/sortable/ for details on the configuration
+ // parameters used here.
+ var sortable_options = { // TODO allow the IPE plugin to control these
+ revert: true,
+ dropOnEmpty: true, // default
+ opacity: 0.75, // opacity of sortable while sorting
+ // placeholder: 'draggable-placeholder',
+ // forcePlaceholderSize: true,
+ items: 'div.panels-ipe-portlet-wrapper',
+ handle: 'div.panels-ipe-draghandle',
+ tolerance: 'pointer',
+ // containment: ipe.topParent,
+ };
+ $('div.panels-ipe-sort-container', ipe.topParent).sortable(sortable_options);
+ // Since the connectWith option only does a one-way hookup, iterate over
+ // all sortable regions to connect them with one another.
+ $('div.panels-ipe-sort-container', ipe.topParent)
+ .sortable('option', 'connectWith', ['div.panels-ipe-sort-container']);
+
+ $('.panels-ipe-form-container', ipe.control).append(formdata);
+ // bind ajax submit to the form
+ $('form', ipe.control).submit(function(event) {
+ url = $(this).attr('action');
+ try {
+ var ajaxOptions = {
+ type: 'POST',
+ url: url,
+ data: { 'js': 1 },
+ global: true,
+ success: ipe.formRespond,
+ error: function(xhr) {
+ Drupal.CTools.AJAX.handleErrors(xhr, url);
+ },
+ dataType: 'json'
+ };
+ $(this).ajaxSubmit(ajaxOptions);
+ }
+ catch (err) {
+ alert("An error occurred while attempting to process " + url);
+ return false;
+ }
+ return false;
+ });
+
+ $('input:submit', ipe.control).each(function() {
+ if ($(this).val() == 'Save') {
+ $(this).click(ipe.saveEditing);
+ };
+ if ($(this).val() == 'Cancel') {
+ $(this).click(ipe.cancelEditing);
+ };
+ });
+
+ // Perform visual effects in a particular sequence.
+ ipe.control.fadeOut('normal', function() {
+ ipe.initButton.hide();
+ ipe.control.fadeIn('normal', function() {
+ // Show all the hidden IPE elements
+ $('.panels-ipe-on').show('slow', function() {
+ ipe.topParent.addClass('panels-ipe-editing');
+ });
+ })
+ });
+ }
+
+ this.formRespond = function(data) {
+ $('.panels-ipe-form-container', ipe.control).empty();
+ ipe.endEditing();
+ }
+
+ this.showEditor = function() {
+
+ }
+
+ this.endEditing = function() {
+ // Re-hide all the IPE meta-elements
+ $('div.panels-ipe-on').hide('normal');
+ ipe.topParent.removeClass('panels-ipe-editing');
+ // Re-show all the IPE non-editing meta-elements
+ $('div.panels-ipe-off').show('normal');
+ };
+
+ this.saveEditing = function() {
+ $('div.panels-ipe-region', ipe.topParent).each(function() {
+ var val = '';
+ var region = $(this).attr('id').split('panels-ipe-regionid-')[1];
+ $(this).children('div.panels-ipe-portlet-wrapper').each(function() {
+ if (val) {
+ val += ',';
+ }
+ val += $(this).attr('id').split('panels-ipe-paneid-')[1];
+ });
+ $('input#edit-panel-pane-' + region, ipe.control).val(val);
+ });
+ };
+
+ this.cancelEditing = function() {
+ $('div.panels-ipe-region', ipe.topParent).sortable('destroy');
+ };
+
+ var ajaxOptions = {
+ type: "POST",
+ url: ipe.cfg.formPath,
+ data: { 'js': 1 },
+ global: true,
+ success: ipe.initEditing,
+ error: function(xhr) {
+ Drupal.CTools.AJAX.handleErrors(xhr, ipe.cfg.formPath);
+ },
+ dataType: 'json'
+ };
+
+ $('div.panels-ipe-region', this.topParent).each(function() {
+ $('div.panels-ipe-portlet-wrapper', this).parent()
+ .wrapInner('
');
+ });
+
+ $('div.panels-ipe-startedit', this.control).click(function() {
+ var $this = $(this);
+ $.ajax(ajaxOptions);
+ });
+};
+
+})(jQuery);
diff --git panels_ipe/panels_ipe.module panels_ipe/panels_ipe.module
new file mode 100644
index 0000000..da15d46
--- /dev/null
+++ panels_ipe/panels_ipe.module
@@ -0,0 +1,501 @@
+ array('administer page manager'), // TODO this clearly needs changing
+ 'type' => MENU_CALLBACK,
+ 'page arguments' => array(3),
+ );
+
+ $items['panels_ipe/ajax/render-pane/%/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ipe_ajax_render_pane',
+ 'page arguments' => array(3, 4),
+ ) + $base;
+ $items['panels_ipe/ajax/add-pane/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ipe_ajax_add_pane_choose',
+ ) + $base;
+ $items['panels_ipe/ajax/add-pane-config/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ipe_ajax_add_pane_config',
+ ) + $base;
+ $items['panels_ipe/ajax/configure/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ipe_ajax_configure_pane',
+ ) + $base;
+ $items['panels_ipe/ajax/save/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ipe_ajax_save_display',
+ ) + $base;
+ $items['panels_ipe/ajax/edit/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ipe_ajax_edit',
+ ) + $base;
+
+ return $items;
+}
+
+/**
+ * Implementation of hook_ctools_plugin_directory().
+ */
+function panels_ipe_ctools_plugin_directory($module, $plugin) {
+ if ($module == 'panels' && $plugin == 'display_renderers') {
+ return 'plugins/' . $plugin;
+ }
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function panels_ipe_theme() {
+ return array(
+ 'panels_ipe_pane_wrapper' => array(
+ 'arguments' => array('output' => NULL, 'pane' => NULL, 'display' => NULL),
+ ),
+ 'panels_ipe_region_wrapper' => array(
+ 'arguments' => array('output' => NULL, 'region_id' => NULL, 'display' => NULL),
+ ),
+ 'panels_ipe_add_pane_button' => array(
+ 'arguments' => array('region_id' => NULL, 'display' => NULL),
+ ),
+ 'panels_ipe_placeholder_pane' => array(
+ 'arguments' => array('region_id' => NULL, 'region_title' => NULL),
+ ),
+ 'panels_ipe_dnd_form_container' => array(
+ 'arguments' => array('link' => NULL, 'cache_key' => NULL, 'display' => NULL),
+ ),
+ );
+}
+
+/**
+ * Theme the 'placeholder' pane, which is shown on an active IPE when no panes
+ * live in that region.
+ *
+ * @param string $region_id
+ * @param string $region_title
+ */
+function theme_panels_ipe_placeholder_pane($region_id, $region_title) {
+ $output = '';
+ $output .= "
$region_title
";
+ $output .= '';
+ return $output;
+}
+
+function theme_panels_ipe_pane_wrapper($output, $pane, $display) {
+ $links = array(
+ 'edit' => array(
+ 'title' => t('edit'),
+ 'href' => "panels_ipe/ajax/configure/$display->cache_key/$pane->pid",
+ 'attributes' => array(
+ 'class' => 'ctools-use-modal',
+ // 'id' => "pane-edit-panel-pane-$pane->pid",
+ ),
+ ),
+ // Deleting is managed entirely in the js; this is just an attachment point
+ // for it
+ 'delete' => array(
+ 'title' => t('delete'),
+ 'href' => '#',
+ 'attributes' => array(
+ 'class' => 'pane-delete',
+ 'id' => "pane-delete-panel-pane-$pane->pid",
+ ),
+ ),
+ );
+ $attr = array(
+ 'class' => 'panels-ipe-linkbar',
+ );
+
+ $links = theme('links', $links, $attr);
+ $links .= 'DRAGGER
';
+ $handlebar = '' . $links . '
';
+ return $handlebar . $output;
+}
+
+function theme_panels_ipe_region_wrapper($output, $region_id, $display) {
+ return $output;
+}
+
+function theme_panels_ipe_add_pane_button($region_id, $display) {
+ $attr = array('class' => 'ctools-use-modal');
+ $link = l(t('add new pane'), "panels_ipe/ajax/add-pane/$display->cache_key/$region_id", array('attributes' => $attr));
+ return '' . $link . '
';
+}
+
+function panels_ipe_ajax_render_pane($pid, $cache) {
+ $class = ctools_plugin_load_class('panels', 'display_renderers', 'panels_renderer_single_pane', 'handler');
+ $renderer = new $class();
+ $renderer->build($cache->display, $pid);
+ $output = array();
+ $output[] = ctools_ajax_command_replace("#panels-ipe-paneid-$pid > .panels-ipe-proper-pane", $renderer->render());
+
+ ctools_ajax_render($output);
+}
+
+function panels_ipe_get_cache_key($key = NULL) {
+ static $cache;
+ if (isset($key)) {
+ $cache = $key;
+ }
+ return $cache;
+}
+
+/**
+ * AJAX entry point to create the controller form for an IPE.
+ *
+ * TODO this can be moved into the IPE plugin probably
+ */
+function panels_ipe_ajax_edit($cache) {
+ $display = $cache->display;
+ ctools_include('form');
+
+ $form_state = array(
+ 'display' => &$display,
+ 'content_types' => $cache->content_types,
+ 'rerender' => FALSE,
+ 'no_redirect' => TRUE,
+ );
+
+ $output = ctools_build_form('panels_ipe_edit_control_form', $form_state);
+ // no output == submit
+ if (!$output) {
+ if (!empty($form_state['clicked_button']['#save-display'])) {
+ panels_save_display($display);
+ }
+ // panels_cache_clear('display', $display->did);
+ drupal_json(1);
+ }
+ else {
+ drupal_json($output);
+ }
+ exit;
+}
+
+function panels_ipe_edit_control_form(&$form_state) {
+ $display = &$form_state['display'];
+ $display->cache_key = isset($display->cache_key) ? $display->cache_key : $display->did;
+
+ // Annoyingly, theme doesn't have access to form_state so we have to do this.
+ $form['#display'] = $display;
+
+ $layout = panels_get_layout($display->layout);
+ $layout_panels = panels_get_panels($layout, $display);
+
+ $form['panel'] = array('#tree' => TRUE);
+ $form['panel']['pane'] = array('#tree' => TRUE);
+
+ foreach ($layout_panels as $panel_id => $title) {
+ // Make sure we at least have an empty array for all possible locations.
+ if (!isset($display->panels[$panel_id])) {
+ $display->panels[$panel_id] = array();
+ }
+
+ $form['panel']['pane'][$panel_id] = array(
+ // Use 'hidden' instead of 'value' so the js can access it.
+ '#type' => 'hidden',
+ '#default_value' => implode(',', (array) $display->panels[$panel_id]),
+ );
+ }
+
+ $form['buttons']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#id' => 'panels-ipe-save',
+ '#submit' => array('panels_edit_display_form_submit'),
+ '#save-display' => TRUE,
+ );
+ $form['buttons']['cancel'] = array(
+ '#type' => 'submit',
+ '#value' => t('Cancel'),
+ );
+ return $form;
+}
+
+/**
+ * Experimenting with this as a means for outputting the display editor form.
+ * @param unknown_type $main
+ */
+function panels_ipe_footer($main = 0) {
+ $key = panels_ipe_get_cache_key();
+ if (!isset($key)) {
+ return;
+ }
+ // TODO should be moved into the IPE plugin - WAAAY too hardcoded right now
+ $output = "";
+ $output .= "
";
+ $output .= "
";
+ $output .= "" . t('Customize this page') . "";
+ $output .= "
";
+ $output .= "
";
+ $output .= "
";
+ return $output;
+}
+
+/**
+ * COPIED STRAIGHT OUT OF DISPLAY-EDIT.INC.
+ *
+ * This duplication will go away...once I figure out how to refactor out the
+ * hard-codedness of much of display-edit.inc.
+ */
+
+
+/**
+ * Entry point for AJAX: 'Add Content' modal form, from which the user selects the
+ * type of pane to add.
+ *
+ * @param int $cache
+ * The display id of the $display object currently being edited.
+ * @param string $panel_id
+ * A string with the name of the panel region to which the selected
+ * pane type will be added.
+ */
+function panels_ipe_ajax_add_pane_choose($cache, $panel_id = NULL, $category = NULL) {
+ $display = $cache->display;
+ $layout = panels_get_layout($display->layout);
+ $layout_panels = panels_get_panels($layout, $display);
+
+ if ($layout && array_key_exists($panel_id, $layout_panels)) {
+ ctools_modal_render(
+ t('Add content to !s', array('!s' => $layout_panels[$panel_id])),
+ panels_ipe_add_content($cache, $panel_id, $category)
+ );
+ }
+
+ ctools_modal_render(t('Error'), t('Invalid input'));
+}
+
+/**
+ * Display a list of content available.
+ */
+function panels_ipe_add_content($cache, $panel_id, $key) {
+ ctools_include('content');
+ $cache_key = $cache->display->cache_key;
+
+ $category_names = array();
+ $categories = array();
+ $titles = array();
+
+ foreach ($cache->content_types as $type_name => $subtypes) {
+ if (is_array($subtypes)) {
+ $content_type = ctools_get_content_type($type_name);
+ foreach ($subtypes as $subtype_name => $subtype_info) {
+ $title = filter_xss_admin($subtype_info['title']);
+ $description = isset($subtype_info['description']) ? $subtype_info['description'] : $title;
+
+ $icon = ctools_content_admin_icon($subtype_info);
+
+ if (isset($subtype_info['top level'])) {
+ $category = 'root';
+ }
+ else if (isset($subtype_info['category'])) {
+ if (is_array($subtype_info['category'])) {
+ list($category, $weight) = $subtype_info['category'];
+ }
+ else {
+ $category = $subtype_info['category'];
+ }
+ }
+ else {
+ $category = t('Uncategorized');
+ }
+
+ $category_key = preg_replace('/[^a-z0-9]/', '-', strtolower($category));
+
+ $output = '
';
+ $url = "panels_ipe/ajax/add-pane-config/$cache_key/$panel_id/$type_name/$subtype_name";
+ $output .= ctools_ajax_image_button($icon, $url, $description, 'panels-modal-add-config');
+ $output .= '
' . ctools_ajax_text_button($title, $url, $description, 'panels-modal-add-config') . '
';
+ $output .= '
';
+ if (!isset($categories[$category_key])) {
+ $category_names[$category_key] = $category;
+ $categories[$category_key] = array();
+ $titles[$category_key] = array();
+ }
+
+ $categories[$category_key][] = $output;
+ $titles[$category_key][] = $title;
+ }
+ }
+ }
+ if (!$categories) {
+ $output = t('There are no content types you may add to this display.');
+ }
+ else {
+ if (!empty($category_names['root'])) {
+ unset($category_names['root']);
+ }
+
+ $output = '
';
+ natcasesort($category_names);
+ $selector = '
';
+
+ // Render our list of categories in column 0.
+ foreach ($category_names as $category => $name) {
+ $class = 'panels-modal-add-category';
+ if ($category == $key) {
+ $class .= ' active';
+ }
+
+ $url = "panels_ipe/ajax/add-pane/$cache_key/$panel_id/$category";
+ $selector .= ctools_ajax_text_button($name, $url, '', $class);
+ }
+
+ $selector .= '
';
+ if (!empty($categories['root'])) {
+ foreach ($titles['root'] as $id => $title) {
+ $selector .= $categories['root'][$id];
+ }
+ }
+
+ if (empty($key) || empty($category_names[$key]) || $key == 'root') {
+// $key = current(array_keys($category_names));
+ $center = '
';
+ $center .= t('Content options are divided by category. Please select a category from the left to proceed.');
+ $center .= '
';
+ }
+ else {
+ natcasesort($titles[$key]);
+
+ // Fill out the info for our current category.
+ $columns = 2;
+ $col[1] = '';
+ $col[2] = '';
+
+ $col_size = count($titles[$key]) / $columns;
+ $count = 0;
+ foreach ($titles[$key] as $id => $title) {
+ $which = floor($count++ / $col_size) + 1; // we leave 0 for the categories.
+ $col[$which] .= $categories[$key][$id];
+ }
+
+ $center = '';
+ foreach ($col as $id => $column) {
+ $center .= '
';
+ }
+ $center .= '
'; // columns
+ }
+ $output .= '
';
+ $output .= '
';
+ $output .= $center;
+ $output .= '
'; // categories box
+ }
+ return $output;
+}
+
+/**
+ * AJAX entry point for to configure a pane that has just been added.
+ */
+function panels_ipe_ajax_add_pane_config($cache, $panel_id = NULL, $type_name = NULL, $subtype_name = NULL, $step = NULL) {
+ ctools_include('content');
+ $cache_key = $cache->display->cache_key;
+ $content_type = ctools_get_content_type($type_name);
+ $subtype = ctools_content_get_subtype($content_type, $subtype_name);
+
+ if (!isset($step) || !isset($cache->new_pane)) {
+ $pane = panels_new_pane($type_name, $subtype_name);
+ $pane->configuration = ctools_content_get_defaults($content_type, $subtype);
+ $pane->panel = $panel_id;
+ $cache->new_pane = &$pane;
+ }
+ else {
+ $pane = $cache->new_pane;
+ }
+
+ $form_state = array(
+ 'display' => &$cache->display,
+ 'contexts' => $cache->display->context,
+ 'pane' => &$pane,
+ 'cache_key' => $cache_key,
+ 'cache' => &$cache,
+ 'ajax' => TRUE,
+ 'modal' => TRUE,
+ 'commands' => array(),
+ );
+
+ $form_info = array(
+ 'path' => "panels_ipe/ajax/add-pane-config/$cache_key/$panel_id/$type_name/$subtype_name/%step",
+ 'next callback' => 'panels_ipe_ajax_edit_pane_config_next',
+ 'finish callback' => 'panels_ipe_ajax_add_pane_config_finish',
+ );
+
+ $rc = ctools_content_form('add', $form_info, $form_state, $content_type, $pane->subtype, $subtype, $pane->configuration, $step);
+ if ($rc === FALSE) {
+ panels_ipe_ajax_add_pane_config_finish($form_state);
+ $form_state['commands'][] = ctools_modal_command_dismiss();
+ ctools_ajax_render($form_state['commands']);
+ }
+}
+
+/**
+ * AJAX entry point for to configure a pane that has just been added.
+ */
+function panels_ipe_ajax_configure_pane($cache, $pid = NULL, $step = NULL) {
+ ctools_include('content');
+ $cache_key = $cache->display->cache_key;
+ if (empty($cache->display->content[$pid])) {
+ ctools_modal_render(t('Error'), t('Invalid pane id.'));
+ }
+
+ $pane = &$cache->display->content[$pid];
+
+ $content_type = ctools_get_content_type($pane->type);
+ $subtype = ctools_content_get_subtype($content_type, $pane->subtype);
+
+ $form_state = array(
+ 'display' => &$cache->display,
+ 'contexts' => $cache->display->context,
+ 'pane' => &$pane,
+ 'cache' => &$cache,
+ 'ajax' => TRUE,
+ 'modal' => TRUE,
+ 'commands' => array(),
+ );
+
+ $form_info = array(
+ 'path' => "panels_ipe/ajax/configure/$cache_key/$pid/%step",
+ 'next callback' => 'panels_ipe_ajax_edit_pane_config_next',
+ 'finish callback' => 'panels_ipe_ajax_edit_pane_config_finish',
+ );
+
+ ctools_content_form('edit', $form_info, $form_state, $content_type, $pane->subtype, $subtype, $pane->configuration, $step);
+}
+
+function panels_ipe_ajax_edit_pane_config_next(&$form_state) {
+ $form_state['cache']->new_pane = $form_state['pane'];
+ panels_edit_cache_set($form_state['cache']);
+}
+
+function panels_ipe_ajax_add_pane_config_finish(&$form_state) {
+ $cache = &$form_state['cache'];
+ $pane = $cache->new_pane;
+ unset($cache->new_pane);
+
+ // Get a real pid for this pane.
+ $pane->pid = "new-" . $cache->display->next_new_pid();
+ // Put the pane into the display where it belongs
+
+ $cache->display->content[$pane->pid] = $pane;
+ $cache->display->panels[$pane->panel][] = $pane->pid;
+
+ $class = ctools_plugin_load_class('panels', 'display_renderers', 'panels_renderer_ipe', 'handler');
+ $renderer = new $class();
+ $renderer->build($cache->display);
+ panels_edit_cache_set($cache);
+
+ $form_state['commands'][] = ctools_ajax_command_append("#panels-ipe-regionid-$pane->panel", $renderer->render_pane($pane));
+}
+
+function panels_ipe_ajax_edit_pane_config_finish(&$form_state) {
+ $cache = &$form_state['cache'];
+ $pane = &$form_state['pane'];
+
+ $class = ctools_plugin_load_class('panels', 'display_renderers', 'panels_renderer_single_pane', 'handler');
+ $renderer = new $class();
+ $renderer->build($cache->display);
+ $renderer->prepare($pane->pid);
+ panels_edit_cache_set($cache);
+
+ $form_state['commands'][] = ctools_ajax_command_replace("#panels-ipe-paneid-$pane->pid > .panels-ipe-portlet-content *", $renderer->render());
+}
diff --git panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php
new file mode 100644
index 0000000..d601b55
--- /dev/null
+++ panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php
@@ -0,0 +1,84 @@
+display->cache_key}' class='panels-ipe-display-container'>$output
";
+ }
+
+ function add_meta() {
+ $this->display->cache_key = $this->display->did;
+ panels_ipe_get_cache_key($this->display->cache_key);
+ panels_load_include('display-edit');
+ $cache = new stdClass();
+ $cache->display = $this->display;
+ ctools_include('content');
+ // NO good reason for this to need to be set way out here!?
+ $cache->content_types = ctools_content_get_available_types();
+ panels_edit_cache_set($cache);
+ ctools_include('ajax');
+ ctools_include('modal');
+ ctools_modal_add_js();
+ drupal_add_css(panels_get_path('css/panels_admin.css'));
+ drupal_add_css(panels_get_path('css/panels_dnd.css'));
+ drupal_add_css(drupal_get_path('module', 'panels_ipe') . '/panels_ipe.css');
+ drupal_add_js(drupal_get_path('module', 'panels_ipe') . '/panels_ipe.js');
+ $settings = array(
+ 'formPath' => '/panels_ipe/ajax/edit/' . $this->display->cache_key,
+ );
+ drupal_add_js(array('PanelsIPECacheKeys' => array($this->display->cache_key)), 'setting');
+ drupal_add_js(array('PanelsIPESettings' => array($this->display->cache_key => $settings)), 'setting');
+ jquery_ui_add(array('ui.draggable', 'ui.droppable', 'ui.sortable'));
+ parent::add_meta();
+ }
+
+ /**
+ * Override & call the parent, then pass output through to the dnd wrapper
+ * theme function.
+ *
+ * @param $pane
+ */
+ function render_pane($pane) {
+ $output = parent::render_pane($pane);
+ if (empty($output)) {
+ return;
+ }
+ // Add an inner layer wrapper to the pane content before placing it into
+ // draggable portlet
+ $output = "$output
";
+ // Hand it off to the plugin/theme for placing draggers/buttons
+ $output = theme('panels_ipe_pane_wrapper', $output, $pane, $this->display);
+ return "" . $output . "
";
+ }
+
+ /**
+ * Add an 'empty' pane placeholder above all the normal panes, and
+ * @param $region_id
+ * @param $panes
+ */
+ function render_region($region_id, $panes) {
+ // Generate this region's 'empty' placeholder pane from the IPE plugin.
+ $empty_ph = theme('panels_ipe_placeholder_pane', $region_id, $this->plugins['layout']['panels'][$region_id]);
+ // Wrap the placeholder in some guaranteed markup.
+ $panes['empty_placeholder'] = '' . $empty_ph . "
";
+ // Generate this region's add new pane button. FIXME waaaaay too hardcoded
+ $panes['add_button'] = theme('panels_ipe_add_pane_button', $region_id, $this->display);
+
+ $output = parent::render_region($region_id, $panes);
+ $output = theme('panels_ipe_region_wrapper', $output, $region_id, $this->display);
+ $classes = 'panels-ipe-region';
+ // Use the canonical list of empty regions to determine whether or not this
+ // region should initially be marked empty. It is important that we use the
+ // list instead of introspecting on render data so that this behavior is
+ // easily controlled externally.
+ if (array_search($region_id, $this->prepared['empty regions'])) {
+ $classes .= ' panels-ipe-region-empty';
+ }
+ return "" . $output . "
";
+ }
+}
diff --git panels_ipe/plugins/display_renderers/panels_renderer_ipe.inc panels_ipe/plugins/display_renderers/panels_renderer_ipe.inc
new file mode 100644
index 0000000..9b8b693
--- /dev/null
+++ panels_ipe/plugins/display_renderers/panels_renderer_ipe.inc
@@ -0,0 +1,8 @@
+ array(
+ 'class' => 'panels_renderer_ipe',
+ 'parent' => 'panels_renderer_default',
+ ),
+);
\ No newline at end of file
diff --git panels_mini/panels_mini.module panels_mini/panels_mini.module
index d96ef26..81b93b1 100644
--- panels_mini/panels_mini.module
+++ panels_mini/panels_mini.module
@@ -77,7 +77,7 @@ function panels_mini_block($op = 'list', $delta = 0, $edit = array()) {
$block = array();
$block['content'] = panels_render_display($panel_mini->display);
- $block['subject'] = panels_display_get_title($panel_mini->display);
+ $block['subject'] = $panel_mini->display->get_title();
// If NULL, fall back to this title:
if (!isset($block['subject'])) {
$block['subject'] = $panel_mini->title;
diff --git panels_stylizer/panels_stylizer.module panels_stylizer/panels_stylizer.module
index b3d92f6..f89f7be 100644
--- panels_stylizer/panels_stylizer.module
+++ panels_stylizer/panels_stylizer.module
@@ -135,6 +135,23 @@ function panels_stylizer_perm() {
}
/**
+ * Implementation of hook_ctools_plugin_api().
+ *
+ * Inform CTools that panels_stylizer's style plugins are up-to-date.
+ *
+ * @param string $owner
+ * The system name of the module owning the API about which information is
+ * being requested.
+ * @param string $api
+ * The name of the API about which information is being requested.
+ */
+function panels_stylizer_ctools_plugin_api($owner, $api) {
+ if ($owner == 'panels' && $api = 'styles') {
+ return array('version' => 2.0);
+ }
+}
+
+/**
* Implementation of hook_theme()
*/
function panels_stylizer_theme() {
diff --git panels_stylizer/plugins/styles/stylizer.inc panels_stylizer/plugins/styles/stylizer.inc
index 6029f4c..786d593 100644
--- panels_stylizer/plugins/styles/stylizer.inc
+++ panels_stylizer/plugins/styles/stylizer.inc
@@ -14,7 +14,7 @@ $plugin = array(
'render pane' => 'panels_stylizer_stylizer_style_render_pane',
'pane settings form' => 'panels_stylizer_stylizer_style_pane_settings_form',
- 'render panel' => 'panels_stylizer_stylizer_style_render_region',
+ 'render region' => 'panels_stylizer_stylizer_style_render_region',
'settings form' => 'panels_stylizer_stylizer_style_region_settings_form',
// 'settings validate' => 'panels_stylizer_stylizer_style_settings_validate',
@@ -26,8 +26,8 @@ $plugin = array(
function theme_panels_stylizer_stylizer_style_render_region($display, $panel_id, $panes, $style_settings) {
$output = '';
- foreach ($panes as $pane_id => $content) {
- $output .= panels_render_pane($content, $display->content[$pane_id], $display);
+ foreach ($panes as $pane_id => $pane_output) {
+ $output .= $pane_output;
}
if ($style_settings['style'] == '$') {
diff --git plugins/display_renderers/panels_renderer_default.class.php plugins/display_renderers/panels_renderer_default.class.php
new file mode 100644
index 0000000..c67b661
--- /dev/null
+++ plugins/display_renderers/panels_renderer_default.class.php
@@ -0,0 +1,462 @@
+build($display);
+ * $html_output = $renderer->render();
+ * @endcode
+ *
+ * Internally, the render pipeline is divided into two phases, prepare and
+ * render:
+ * - The prepare phase transforms the skeletal data on the provided
+ * display object into a structure that is expected by the render phase.
+ * It is divided into a series of discrete sub-methods and operates
+ * primarily by passing parameters, all with the intention of making
+ * subclassing easier.
+ * - The render phase relies primarily on data stored in the renderer object's
+ * properties, presumably set in the prepare phase. It iterates through the
+ * rendering of each pane, pane styling, placement in panel regions, region
+ * styling, and finally the arrangement of rendered regions in the layout.
+ * Caching, if in use, is triggered per pane, or on the entire display.
+ *
+ * In short: prepare builds conf, render renders conf. Subclasses should respect
+ * this separation of responsibilities by adhering to these loose guidelines,
+ * given a loaded display object:
+ * - If your renderer needs to modify the datastructure representing what is
+ * to be rendered (panes and their conf, styles, caching, etc.), it should
+ * use the prepare phase.
+ * - If your renderer needs to modify the manner in which that renderable
+ * datastructure data is rendered, it should use the render phase.
+ *
+ * In the vast majority of use cases, this default renderer will be sufficient
+ * and need not be switched out/subclassed; style plugins and/or layout plugins
+ * accomplish everything needed. If you think you might need a custom
+ * renderer, consider the following criteria/examples:
+ * - Some additional markup needs to be added to EVERY SINGLE panel.
+ * - Given a full display object, just render one pane.
+ * - Show a Panels admin interface.
+ *
+ * The system is almost functionally identical to the old procedural approach,
+ * with some exceptions (@see panels_renderer_legacy for details). The approach
+ * here differs primarily in its friendliness to tweaking via inheritance.
+ */
+class panels_renderer_default {
+ /**
+ * The Panels display object that is to be rendered.
+ *
+ * @var panels_display
+ */
+ var $display;
+
+ /**
+ * An associative array of loaded plugins. Used primarily as a central
+ * location for storing plugins that require additional loading beyond
+ * reading the plugin definition, which is already statically cached by
+ * ctools_get_plugins(). An example is layout plugins, which can optionally
+ * have a callback that determines the set of panel regions available at
+ * runtime.
+ *
+ * @var array
+ */
+ var $plugins = array();
+
+ /**
+ * A multilevel array of rendered data. The first level of the array
+ * indicates the type of rendered data, typically with up to three keys:
+ * 'layout', 'regions', and 'panes'. The relevant rendered data is stored as
+ * the value for each of these keys as it is generated:
+ * - 'panes' are an associative array of rendered output, keyed on pane id.
+ * - 'regions' are an associative array of rendered output, keyed on region
+ * name.
+ * - 'layout' is the whole of the rendered output.
+ *
+ * @var array
+ */
+ var $rendered = array();
+
+ /**
+ * A multilevel array of data prepared for rendering. The first level of the
+ * array indicates the type of prepared data. The standard renderer populates
+ * and uses two top-level keys, 'panes' and 'regions':
+ * - 'panes' are an associative array of pane objects to be rendered, keyed
+ * on pane id and sorted into proper rendering order.
+ * - 'regions' are an associative array of regions, keyed on region name,
+ * each of which is itself an indexed array of pane ids in the order in
+ * which those panes appear in that region.
+ *
+ * @var array
+ */
+ var $prepared = array();
+
+ /**
+ * Boolean state variable, indicating whether or not the prepare() method has
+ * been run.
+ *
+ * This state is checked in panels_renderer_default::render_layout() to
+ * determine whether the prepare method should be automatically triggered.
+ * @var bool
+ */
+ var $prep_run = FALSE;
+
+ /**
+ * Receive and store the display object to be rendered.
+ *
+ * This is a psuedo-constructor that should typically be called immediately
+ * after object construction.
+ *
+ * @param panels_display $display
+ * The panels display object to be rendered.
+ */
+ function build(&$display) {
+ $layout = panels_get_layout($display->layout);
+ $this->display = &$display;
+ $this->plugins['layout'] = $layout;
+ if (!isset($layout['panels'])) {
+ $this->plugins['layout']['panels'] = panels_get_panels($layout, $display);
+ }
+ }
+
+ /**
+ * Prepare the attached display for rendering.
+ *
+ * This is the outermost prepare method. It calls several sub-methods as part
+ * of the overall preparation process. This compartmentalization is intended
+ * to ease the task of modifying renderer behavior in child classes.
+ *
+ * If you override this method, it is important that you either call this
+ * method via parent::prepare(), or manually set $this->prep_run = TRUE.
+ *
+ * @param mixed $external_settings
+ * An optional parameter allowing external code to pass in additional
+ * settings for use in the preparation process. Not used in the default
+ * renderer, but included for interface consistency.
+ */
+ function prepare($external_settings = NULL) {
+ $this->prepare_panes($this->display->content);
+ $this->prepare_regions($this->display->panels, $this->display->panel_settings);
+ $this->prepared['empty regions'] = array_diff(array_keys($this->plugins['layout']['panels']), array_keys($this->prepared['regions']));
+ $this->prep_run = TRUE;
+ }
+
+ /**
+ * Prepare the list of panes to be rendered, accounting for visibility/access
+ * settings and rendering order.
+ *
+ * This method represents the standard approach for determining the list of
+ * panes to be rendered that is compatible with all parts of the Panels
+ * architecture. It first applies visibility & access checks, then sorts panes
+ * into their proper rendering order, and returns the result as an array.
+ *
+ * Inheriting classes should override this method if that renderer needs to
+ * regularly make additions to the set of panes that will be rendered.
+ *
+ * @param array $panes
+ * An associative array of panes, keyed on pane id.
+ * @return array
+ * An associative array of panes to be rendered, keyed on pane id and sorted
+ * into proper rendering order.
+ */
+ function prepare_panes($panes) {
+ ctools_include('content');
+ // Use local variables as writing to them is very slightly faster
+ $normal = $last = array();
+
+ // Prepare the list of panes to be rendered
+ foreach ($panes as $pid => $pane) {
+ // TODO remove in 7.x and ensure the upgrade path weeds out any stragglers; it's been long enough
+ $pane->shown = !empty($pane->shown); // guarantee this field exists.
+ // If this pane is not visible to the user, skip out and do the next one
+ if (!$pane->shown || !panels_pane_access($pane, $this->display)) {
+ continue;
+ }
+
+ $ct_plugin_def = ctools_get_content_type($pane->type);
+
+ // If this pane wants to render last, add it to the $last array. We allow
+ // this because some panes need to be rendered after other panes,
+ // primarily so they can do things like the leftovers of forms.
+ if (!empty($ct_plugin_def['render last'])) {
+ $last[$pid] = $pane;
+ }
+ // Otherwise, render it in the normal order.
+ else {
+ $normal[$pid] = $pane;
+ }
+ }
+ $this->prepared['panes'] = $normal + $last;
+ return $this->prepared['panes'];
+ }
+
+ /**
+ * @param array $regions
+ * @param array $settings
+ */
+ function prepare_regions($region_list, $settings) {
+ // Initialize defaults to be used for regions without their own explicit
+ // settings. Use display settings if they exist, else hardcoded defaults
+ $default = array(
+ 'style' => !empty($settings['style']) ? $settings['style'] : panels_get_style('default'),
+ 'style settings' => isset($settings['style_settings']['default']) ? $settings['style_settings']['default'] : array(),
+ );
+
+ $regions = array();
+ if (empty($settings)) {
+ // No display/panel region settings exist, init all with the defaults.
+ foreach ($region_list as $region_id => $panes) {
+ $regions[$region_id] = $default;
+ $regions[$region_id]['pids'] = $panes;
+ }
+ }
+ else {
+ // Some settings exist; iterate through each region and set individually
+ foreach ($region_list as $region_id => $panes) {
+ if (empty($settings[$region_id]['style']) || $settings[$region_id]['style'] == -1) {
+ $regions[$region_id] = $default;
+ }
+ else {
+ $regions[$region_id]['style'] = panels_get_style($settings[$region_id]['style']);
+ $regions[$region_id]['style settings'] = isset($settings['style_settings'][$region_id]) ? $settings['style_settings'][$region_id] : array();
+ }
+ $regions[$region_id]['pids'] = $panes;
+ }
+ }
+ $this->prepared['regions'] = $regions;
+ return $this->prepared['regions'];
+ }
+
+ /**
+ * Build inner content, then hand off to layout-specified theme function for
+ * final render step.
+ *
+ * This is the outermost method in the Panels render pipeline. It calls the
+ * inner methods, which return a content array, which is in turn passed to the
+ * theme function specified in the layout plugin.
+ *
+ * @return string
+ * Themed & rendered HTML output.
+ */
+ function render() {
+ // Attach out-of-band data first.
+ $this->add_meta();
+
+ if (empty($this->display->cache['method'])) {
+ return $this->render_layout();
+ }
+ else {
+ $cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context);
+ if ($cache === FALSE) {
+ $cache = new panels_cache_object();
+ $cache->set_content($this->render_layout());
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context);
+ }
+ return $cache->content;
+ }
+ }
+
+ /**
+ * Perform display/layout-level render operations.
+ *
+ * This method triggers all the inner pane/region rendering processes, passes
+ * that to the layout plugin's theme callback, and returns the rendered HTML.
+ *
+ * If display-level caching is enabled and that cache is warm, this method
+ * will not be called.
+ */
+ function render_layout() {
+ if (empty($this->prep_run)) {
+ $this->prepare();
+ }
+ $this->render_panes();
+ $this->render_regions();
+ $this->rendered['layout'] = theme($this->plugins['layout']['theme'], check_plain($this->display->css_id), $this->rendered['regions'], $this->display->layout_settings, $this->display);
+ return $this->rendered['layout'];
+ }
+
+ /**
+ * Attach out-of-band page metadata (e.g., CSS and JS).
+ *
+ * This must be done before render, because panels-within-panels must have
+ * their CSS added in the right order: inner content before outer content.
+ */
+ function add_meta() {
+ if (!empty($this->plugins['layout']['css'])) {
+ if (file_exists(path_to_theme() . '/' . $this->plugins['layout']['css'])) {
+ drupal_add_css(path_to_theme() . '/' . $this->plugins['layout']['css']);
+ }
+ else {
+ drupal_add_css($this->plugins['layout']['path'] . '/' . $this->plugins['layout']['css']);
+ }
+ }
+ }
+
+ function render_panes() {
+ ctools_include('content');
+
+ // First, render all the panes into little boxes.
+ $this->rendered['panes'] = array();
+ foreach ($this->prepared['panes'] as $pid => $pane) {
+ $this->rendered['panes'][$pid] = $this->render_pane($pane);
+ }
+ }
+
+ /**
+ * Render a pane using the appropriate style.
+ *
+ * @param object $pane
+ * The $pane information from the display
+ */
+ function render_pane($pane) {
+ $content = $this->render_pane_content($pane);
+ if ($this->display->hide_title == PANELS_TITLE_PANE && !empty($this->display->title_pane) && $this->display->title_pane == $pane->pid) {
+
+ // If the user selected to override the title with nothing, and selected
+ // this as the title pane, assume the user actually wanted the original
+ // title to bubble up to the top but not actually be used on the pane.
+ if (empty($content->title) && !empty($content->original_title)) {
+ $this->display->stored_pane_title = $content->original_title;
+ }
+ else {
+ $this->display->stored_pane_title = !empty($content->title) ? $content->title : '';
+ }
+ }
+
+ if (!empty($pane->style['style'])) {
+ $style = panels_get_style($pane->style['style']);
+
+ if (isset($style) && isset($style['render pane'])) {
+ $output = theme($style['render pane'], $content, $pane, $this->display);
+
+ // This could be null if no theme function existed.
+ if (isset($output)) {
+ return $output;
+ }
+ }
+ }
+
+ if (!empty($content->content)) {
+ // fallback
+ return theme('panels_pane', $content, $pane, $this->display);
+ }
+ }
+
+ /**
+ * Render the contents of a single pane.
+ *
+ * This method retrieves pane content and produces a ready-to-render content
+ * object. It also manages pane-specific caching.
+ *
+ * @param stdClass $pane
+ * A Panels pane object, as loaded from the database.
+ */
+ function render_pane_content($pane) { // TODO remove this method by collapsing it into $this->render_panes()
+ ctools_include('context');
+ // TODO finally safe to remove this check?
+ if (!is_array($this->display->context)) {
+ watchdog('panels', 'renderer:render_pane_content() hit with a non-array for the context', $this->display, WATCHDOG_DEBUG);
+ $this->display->context = array();
+ }
+
+ $content = FALSE;
+ $caching = !empty($pane->cache['method']) ? TRUE : FALSE;
+ if ($caching && ($cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context, $pane))) {
+ $content = $cache->content;
+ }
+ else {
+ $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, array(), $this->display->args, $this->display->context);
+ foreach (module_implements('panels_pane_content_alter') as $module) {
+ $function = $module . '_panels_pane_content_alter';
+ $function($content, $pane, $this->display->args, $this->display->context);
+ }
+ if ($caching) {
+ $cache = new panels_cache_object();
+ $cache->set_content($content);
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $pane);
+ $content = $cache->content;
+ }
+ }
+
+ // Pass long the css_id that is usually available.
+ if (!empty($pane->css['css_id'])) {
+ $content->css_id = $pane->css['css_id'];
+ }
+
+ // Pass long the css_class that is usually available.
+ if (!empty($pane->css['css_class'])) {
+ $content->css_class = $pane->css['css_class'];
+ }
+
+ return $content;
+ }
+
+ /**
+ * Render all panes in the attached display into their panel regions, then
+ * render those regions.
+ *
+ * @return array
+ * An array of rendered panel regions, keyed on the region name.
+ */
+ function render_regions() {
+ // Initialize the regions array with keys for each region and NULL values
+ // for each; this prevents warnings on empty regions later
+ $this->rendered['regions'] = array();
+ foreach (array_keys($this->plugins['layout']['panels']) as $region_id) {
+ $this->rendered['regions'][$region_id] = NULL;
+ }
+
+ // Loop through all panel regions, put all panes that belong to the current
+ // region in an array, then render the region. Primarily this ensures that
+ // the panes are arranged in the proper order.
+ $content = array();
+ foreach ($this->prepared['regions'] as $region_id => $conf) {
+ $region_panes = array();
+ foreach ($conf['pids'] as $pid) {
+ // Only include panes for region rendering if they had some output.
+ if (!empty($this->rendered['panes'][$pid])) {
+ $region_panes[$pid] = $this->rendered['panes'][$pid];
+ }
+ }
+ $this->rendered['regions'][$region_id] = $this->render_region($region_id, $region_panes);
+ }
+
+ return $this->rendered['regions'];
+ }
+
+ /**
+ * Render a single panel region.
+ *
+ * Primarily just a passthrough to the panel region rendering callback
+ * specified by the style plugin that is attached to the current panel region.
+ *
+ * @param $region_id
+ * The ID of the panel region being rendered
+ * @param $panes
+ * An array of panes that are assigned to the panel that's being rendered.
+ *
+ * @return string
+ * The rendered, HTML string output of the passed-in panel region.
+ */
+ function render_region($region_id, $panes) {
+ $style = $this->prepared['regions'][$region_id]['style'];
+ $style_settings = $this->prepared['regions'][$region_id]['style settings'];
+
+ // Retrieve the pid (can be a panel page id, a mini panel id, etc.), this
+ // might be used (or even necessary) for some panel display styles.
+ // TODO: Got to fix this to use panel page name instead of pid, since pid is
+ // no longer guaranteed. This needs an API to be able to set the final id.
+ $owner_id = 0;
+ if (isset($this->display->owner) && is_object($this->display->owner) && isset($this->display->owner->id)) {
+ $owner_id = $this->display->owner->id;
+ }
+
+ return theme($style['render region'], $this->display, $owner_id, $panes, $style_settings, $region_id);
+ }
+}
diff --git plugins/display_renderers/panels_renderer_default.inc plugins/display_renderers/panels_renderer_default.inc
new file mode 100644
index 0000000..707c532
--- /dev/null
+++ plugins/display_renderers/panels_renderer_default.inc
@@ -0,0 +1,7 @@
+ array(
+ 'class' => 'panels_renderer_default',
+ ),
+);
\ No newline at end of file
diff --git plugins/display_renderers/panels_renderer_legacy.class.php plugins/display_renderers/panels_renderer_legacy.class.php
new file mode 100644
index 0000000..67e135a
--- /dev/null
+++ plugins/display_renderers/panels_renderer_legacy.class.php
@@ -0,0 +1,222 @@
+layout);
+ $this->display = &$display;
+ $this->plugins['layout'] = $layout;
+ }
+
+ /**
+ * Builds inner content, then hands off to layout-specified theme function for
+ * final render step.
+ *
+ * This is the outermost method in the Panels render pipeline. It calls the
+ * inner methods, which return a content array, which is in turn passed to the
+ * theme function specified in the layout plugin.
+ *
+ * @return string
+ * Themed & rendered HTML output.
+ */
+ function render() {
+ if (!empty($this->plugins['layout']['css'])) {
+ if (file_exists(path_to_theme() . '/' . $this->plugins['layout']['css'])) {
+ drupal_add_css(path_to_theme() . '/' . $this->plugins['layout']['css']);
+ }
+ else {
+ drupal_add_css($this->plugins['layout']['path'] . '/' . $this->plugins['layout']['css']);
+ }
+ }
+ // This now comes after the CSS is added, because panels-within-panels must
+ // have their CSS added in the right order; inner content before outer content.
+
+ if (empty($this->display->cache['method'])) {
+ $content = $this->render_regions();
+ }
+ else {
+ $cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context);
+ if ($cache === FALSE) {
+ $cache = new panels_cache_object();
+ $cache->set_content($this->render_regions());
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context);
+ }
+ $content = $cache->content;
+ }
+
+ $output = theme($this->plugins['layout']['theme'], check_plain($this->display->css_id), $content, $this->display->layout_settings, $this->display);
+
+ return $output;
+ }
+
+ /**
+ * Render all panes in the attached display into their panel regions, then
+ * render those regions.
+ *
+ * @return array $content
+ * An array of rendered panel regions, keyed on the region name.
+ */
+ function render_regions() {
+ ctools_include('content');
+
+ // First, render all the panes into little boxes. We do this here because
+ // some panes request to be rendered after other panes (primarily so they
+ // can do the leftovers of forms).
+ $panes = array();
+ $later = array();
+
+ foreach ($this->display->content as $pid => $pane) {
+ $pane->shown = !empty($pane->shown); // guarantee this field exists.
+ // If the user can't see this pane, do not render it.
+ if (!$pane->shown || !panels_pane_access($pane, $this->display)) {
+ continue;
+ }
+
+ // If this pane wants to render last, add it to the $later array.
+ $content_type = ctools_get_content_type($pane->type);
+
+ if (!empty($content_type['render last'])) {
+ $later[$pid] = $pane;
+ continue;
+ }
+
+ $panes[$pid] = $this->render_pane($pane);
+ }
+
+ foreach ($later as $pid => $pane) {
+ $panes[$pid] = $this->render_pane($pane);
+ }
+
+ // Loop through all panels, put all panes that belong to the current panel
+ // in an array, then render the panel. Primarily this ensures that the
+ // panes are in the proper order.
+ $content = array();
+ foreach ($this->display->panels as $panel_name => $pids) {
+ $panel_panes = array();
+ foreach ($pids as $pid) {
+ if (!empty($panes[$pid])) {
+ $panel_panes[$pid] = $panes[$pid];
+ }
+ }
+ $content[$panel_name] = $this->render_region($panel_name, $panel_panes);
+ }
+
+ // Prevent notices by making sure that all panels at least have an entry:
+ $panels = panels_get_panels($this->plugins['layout'], $this->display);
+ foreach ($panels as $id => $panel) {
+ if (!isset($content[$id])) {
+ $content[$id] = NULL;
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Render the contents of a single pane.
+ *
+ * This method retrieves pane content and produces a ready-to-render content
+ * object. It also manages pane-specific caching.
+ *
+ * @param stdClass $pane
+ * A Panels pane object, as loaded from the database.
+ */
+ function render_pane($pane) {
+ ctools_include('context');
+ if (!is_array($this->display->context)) {
+ $this->display->context = array();
+ }
+
+ $content = FALSE;
+ $caching = !empty($pane->cache['method']) ? TRUE : FALSE;
+ if ($caching && ($cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context, $pane))) {
+ $content = $cache->content;
+ }
+ else {
+ $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, array(), $this->display->args, $this->display->context);
+ foreach (module_implements('panels_pane_content_alter') as $module) {
+ $function = $module . '_panels_pane_content_alter';
+ $function($content, $pane, $this->display->args, $this->display->context);
+ }
+ if ($caching) {
+ $cache = new panels_cache_object();
+ $cache->set_content($content);
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $pane);
+ $content = $cache->content;
+ }
+ }
+
+ // Pass long the css_id that is usually available.
+ if (!empty($pane->css['css_id'])) {
+ $content->css_id = $pane->css['css_id'];
+ }
+
+ // Pass long the css_class that is usually available.
+ if (!empty($pane->css['css_class'])) {
+ $content->css_class = $pane->css['css_class'];
+ }
+
+ return $content;
+ }
+
+ /**
+ * Render a single panel region.
+ *
+ * Primarily just a passthrough to the panel region rendering callback
+ * specified by the style plugin that is attached to the current panel region.
+ *
+ * @param $region_name
+ * The ID of the panel region being rendered
+ * @param $panes
+ * An array of panes that are assigned to the panel that's being rendered.
+ *
+ * @return
+ * The rendered HTML for the passed-in panel region.
+ */
+ function render_region($region_name, $panes) {
+ list($style, $style_settings) = panels_get_panel_style_and_settings($this->display->panel_settings, $region_name);
+ $callback = 'render panel';
+
+ // Retrieve the pid (can be a panel page id, a mini panel id, etc.), this
+ // might be used (or even necessary) for some panel display styles.
+ $owner_id = 0;
+ if (isset($this->display->owner) && is_object($this->display->owner) && isset($this->display->owner->id)) {
+ $owner_id = $this->display->owner->id;
+ }
+
+ // Check to see if we're actually running a current style plugin even though
+ // we're in the legacy renderer
+ if (version_compare($style['version'], 2.0, '>=')) {
+ // We are, so pre-render the content as the current version expects
+ foreach($panes as $pane_id => $pane) {
+ $panes[$pane_id] = panels_render_pane($pane, $this->display->content[$pane_id], $this->display);
+ }
+ // And set the callback to the new key
+ $callback = 'render region';
+
+ }
+ dsm($panes);
+
+ return theme($style[$callback], $this->display, $owner_id, $panes, $style_settings, $region_name);
+ }
+}
diff --git plugins/display_renderers/panels_renderer_legacy.inc plugins/display_renderers/panels_renderer_legacy.inc
new file mode 100644
index 0000000..2082105
--- /dev/null
+++ plugins/display_renderers/panels_renderer_legacy.inc
@@ -0,0 +1,7 @@
+ array(
+ 'class' => 'panels_renderer_legacy',
+ ),
+);
\ No newline at end of file
diff --git plugins/display_renderers/panels_renderer_single_pane.class.php plugins/display_renderers/panels_renderer_single_pane.class.php
new file mode 100644
index 0000000..4d022c8
--- /dev/null
+++ plugins/display_renderers/panels_renderer_single_pane.class.php
@@ -0,0 +1,35 @@
+display = &$display;
+ }
+
+ function prepare($external_settings = NULL) {
+ $this->render_pid = $external_settings;
+ }
+
+ function render() {
+ // If no requested pid, or requested pid does not exist,
+ if (empty($this->render_pid) || empty($this->display->content[$this->render_pid])) {
+ return NULL;
+ }
+ return $this->render_pane($this->display->content[$this->render_pid]);
+ }
+
+ function render_single($pid) {
+ return $this->render_pane($this->display->content[$pid]);
+ }
+}
\ No newline at end of file
diff --git plugins/display_renderers/panels_renderer_single_pane.inc plugins/display_renderers/panels_renderer_single_pane.inc
new file mode 100644
index 0000000..e0576ff
--- /dev/null
+++ plugins/display_renderers/panels_renderer_single_pane.inc
@@ -0,0 +1,8 @@
+ array(
+ 'class' => 'panels_renderer_single_pane',
+ 'parent' => 'panels_renderer_default',
+ ),
+);
\ No newline at end of file
diff --git plugins/styles/corners/rounded_corners.inc plugins/styles/corners/rounded_corners.inc
index 0dd7db7..dd99bd4 100644
--- plugins/styles/corners/rounded_corners.inc
+++ plugins/styles/corners/rounded_corners.inc
@@ -9,8 +9,8 @@
// Plugin definition
$plugin = array(
'title' => t('Rounded corners'),
- 'description' => t('Presents the panes or panels with a rounded corner box around them'),
- 'render panel' => 'panels_rounded_corners_style_render_panel',
+ 'description' => t('Presents the panes or panel regions with a rounded corner box around them'),
+ 'render region' => 'panels_rounded_corners_style_render_region',
'render pane' => 'panels_rounded_corners_style_render_pane',
'settings form' => 'panels_rounded_corners_style_settings_form',
'hook theme' => array(
@@ -27,7 +27,7 @@ $plugin = array(
*
* @ingroup themeable
*/
-function theme_panels_rounded_corners_style_render_panel($display, $panel_id, $panes, $settings) {
+function theme_panels_rounded_corners_style_render_region($display, $region_id, $panes, $settings) {
$output = '';
// Determine where to put the box. If empty or 'pane' around each pane. If
@@ -35,19 +35,18 @@ function theme_panels_rounded_corners_style_render_panel($display, $panel_id, $p
$where = empty($settings['corner_location']) ? 'pane' : $settings['corner_location'];
$print_separator = FALSE;
- foreach ($panes as $pane_id => $pane) {
- $text = panels_render_pane($pane, $display->content[$pane_id], $display);
- if ($text) {
+ foreach ($panes as $pane_id => $pane_output) {
+ if ($pane_output) {
// Add the separator if we've already displayed a pane.
if ($print_separator) {
- $output .= '
';
+ $output .= '
';
}
if ($where == 'pane') {
- $output .= theme('panels_rounded_corners_box', $text);
+ $output .= theme('panels_rounded_corners_box', $pane_output);
}
else {
- $output .= $text;
+ $output .= $pane_output;
$print_separator = TRUE;
}
}
@@ -184,7 +183,7 @@ $idstr div.admin-links {
margin-left: -12px;
}
-$idstr .panel-separator {
+$idstr .region-separator {
background: url($url/shadow-b.png) repeat-x 0 center;
font-size: 1px;
height: 30px;
diff --git plugins/styles/default.inc plugins/styles/default.inc
index 2020676..90d3319 100644
--- plugins/styles/default.inc
+++ plugins/styles/default.inc
@@ -10,7 +10,7 @@
$plugin = array(
'title' => t('No style'),
'description' => t('The default panel rendering style; displays each pane with a separator.'),
- 'render panel' => 'panels_default_style_render_panel',
+ 'render region' => 'panels_default_style_render_region',
);
/**
@@ -18,22 +18,21 @@ $plugin = array(
*
* @ingroup themeable
*/
-function theme_panels_default_style_render_panel($display, $panel_id, $panes, $settings) {
+function theme_panels_default_style_render_region($display, $region_id, $panes, $settings) {
$output = '';
$print_separator = FALSE;
- foreach ($panes as $pane_id => $content) {
+ foreach ($panes as $pane_id => $pane_output) {
// Add the separator if we've already displayed a pane.
if ($print_separator) {
$output .= '';
}
- $output .= $text = panels_render_pane($content, $display->content[$pane_id], $display);
+ $output .= $pane_output;
// If we displayed a pane, this will become true; if not, it will become
// false.
- $print_separator = (bool) $text;
+ $print_separator = (bool) $pane_output;
}
return $output;
}
-
diff --git plugins/styles/list.inc plugins/styles/list.inc
index c3405fe..12aa7bc 100644
--- plugins/styles/list.inc
+++ plugins/styles/list.inc
@@ -11,7 +11,7 @@
$plugin = array(
'title' => t('List'),
'description' => t('Presents the panes in the form of an HTML list.'),
- 'render panel' => 'panels_list_style_render_panel',
+ 'render region' => 'panels_list_style_render_region',
'settings form' => 'panels_list_style_settings_form',
);
@@ -20,11 +20,10 @@ $plugin = array(
*
* @ingroup themeable
*/
-function theme_panels_list_style_render_panel($display, $panel_id, $panes, $settings) {
+function theme_panels_list_style_render_region($display, $region_id, $panes, $settings) {
$items = array();
- foreach ($panes as $pane_id => $content) {
- $item = panels_render_pane($content, $display->content[$pane_id], $display);
+ foreach ($panes as $pane_id => $item) {
if (isset($item)) {
$items[] = $item;
}
diff --git plugins/task_handlers/panel_context.inc plugins/task_handlers/panel_context.inc
index 7e215fb..946c8c5 100644
--- plugins/task_handlers/panel_context.inc
+++ plugins/task_handlers/panel_context.inc
@@ -185,6 +185,7 @@ $plugin = array(
'default conf' => array(
'title' => t('Panel'),
'no_blocks' => FALSE,
+ 'use_ipe' => FALSE,
'css_id' => '',
'css' => '',
'contexts' => array(),
@@ -267,12 +268,20 @@ function panels_panel_context_render($handler, $base_contexts, $args, $test = TR
// With an argument, this actually sets the display.
panels_get_current_page_display($display);
+ $renderer = NULL; // NULL means the default renderer will be used
+
+ // TODO this is a temporary, hacky approach to ACLs for the IPE, and will be
+ // modified in a later release
+ if (module_exists('panels_ipe') && !empty($handler->conf['use_ipe']) && user_access('administer page manager')) {
+ $renderer = 'panels_renderer_ipe';
+ }
+
$info = array(
- 'content' => panels_render_display($display),
+ 'content' => panels_render_display($display, $renderer),
'no_blocks' => !empty($handler->conf['no_blocks']),
);
- $info['title'] = panels_display_get_title($display);
+ $info['title'] = $display->get_title();
return $info;
}
@@ -694,6 +703,17 @@ function panels_panel_context_edit_settings(&$form, &$form_state) {
'#description' => t('Check this to have the page disable all regions displayed in the theme. Note that some themes support this setting better than others. If in doubt, try with stock themes to see.'),
);
+ if (module_exists('panels_ipe')) { // Temporary approach till we get something better
+ // Initialize the value if needed...hacky hacky
+ $conf['use_ipe'] = isset($conf['use_ipe']) ? $conf['use_ipe'] : FALSE;
+ $form['conf']['use_ipe'] = array(
+ '#type' => 'checkbox',
+ '#default_value' => $conf['use_ipe'],
+ '#title' => t('Use In-Place Editor'),
+ '#description' => t('Use the new Panels In-Place Editor on this panel for users with global panel-editing permissions (Warning: EXPERIMENTAL!!)'),
+ );
+ }
+
$form['conf']['css_id'] = array(
'#type' => 'textfield',
'#size' => 35,
@@ -715,6 +735,7 @@ function panels_panel_context_edit_settings(&$form, &$form_state) {
*/
function panels_panel_context_edit_settings_submit(&$form, &$form_state) {
$form_state['handler']->conf['no_blocks'] = $form_state['values']['no_blocks'];
+ $form_state['handler']->conf['use_ipe'] = $form_state['values']['use_ipe'];
$form_state['handler']->conf['css_id'] = $form_state['values']['css_id'];
$form_state['handler']->conf['css'] = $form_state['values']['css'];
$form_state['handler']->conf['title'] = $form_state['values']['title'];