Merging DRUPAL-6--3 into HEAD for 7.x work. Some manually-resolved merge
authorSam Boyer
Tue, 7 Sep 2010 09:42:13 +0000 (09:42 +0000)
committerSam Boyer
Tue, 7 Sep 2010 09:42:13 +0000 (09:42 +0000)
conflicts.

Conflicts:
contexts/terms.inc
docs/sample_plugin_ct.inc
help/plugin-style.html
help/plugins-style.html
plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.inc
plugins/layouts/twocol_bricks/panels-twocol-bricks.tpl.php
plugins/styles/corners/rounded_corners.inc
plugins/styles/default.inc
plugins/styles/list.inc

103 files changed:
API.txt [deleted file]
CHANGELOG.txt
D6UPDATE.txt
TODO.txt [deleted file]
css/panels-dashboard.css
css/panels_admin.css
css/panels_dnd.css
docs/docs.inc [deleted file]
docs/doxygen.css [deleted file]
docs/sample_plugin_ct.inc [deleted file]
help/api.html
help/panels.help.ini
help/plugins-layout.html [new file with mode: 0644]
help/plugins-style.html [moved from help/plugin-style.html with 100% similarity]
includes/callbacks.inc
includes/common.inc
includes/display-edit.inc
includes/display-layout.inc
includes/display-render.inc
includes/legacy.inc [new file with mode: 0644]
includes/page-wizard.inc [new file with mode: 0644]
includes/panels.pipelines.inc [new file with mode: 0644]
includes/plugins.inc
js/display_editor.js
panels.install
panels.module
panels2.doxy [deleted file]
panels_ipe/css/panels_ipe.css [new file with mode: 0644]
panels_ipe/images/dragger.png [new file with mode: 0644]
panels_ipe/includes/panels_ipe.pipelines.inc [new file with mode: 0644]
panels_ipe/js/panels_ipe.js [new file with mode: 0644]
panels_ipe/panels_ipe.info [new file with mode: 0644]
panels_ipe/panels_ipe.module [new file with mode: 0644]
panels_ipe/plugins/display_renderers/ipe.inc [new file with mode: 0644]
panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php [new file with mode: 0644]
panels_mini/panels_mini.admin.inc
panels_mini/panels_mini.install
panels_mini/panels_mini.module
panels_mini/plugins/content_types/panels_mini.inc
panels_mini/plugins/export_ui/panels_mini.inc [new file with mode: 0644]
panels_mini/plugins/export_ui/panels_mini_ui.class.php [new file with mode: 0644]
panels_node/panels_node.install
panels_node/panels_node.module
plugins/cache/simple.inc
plugins/display_renderers/editor.inc [new file with mode: 0644]
plugins/display_renderers/legacy.inc [new file with mode: 0644]
plugins/display_renderers/panels_renderer_editor.class.php [new file with mode: 0644]
plugins/display_renderers/panels_renderer_legacy.class.php [new file with mode: 0644]
plugins/display_renderers/panels_renderer_simple.class.php [new file with mode: 0644]
plugins/display_renderers/panels_renderer_single_pane.class.php [new file with mode: 0644]
plugins/display_renderers/panels_renderer_standard.class.php [new file with mode: 0644]
plugins/display_renderers/simple.inc [new file with mode: 0644]
plugins/display_renderers/single_pane.inc [new file with mode: 0644]
plugins/display_renderers/standard.inc [new file with mode: 0644]
plugins/export_ui/panels_layouts.inc [new file with mode: 0644]
plugins/export_ui/panels_layouts_ui.class.php [new file with mode: 0644]
plugins/layouts/flexible/flexible-admin.css
plugins/layouts/flexible/flexible-admin.js
plugins/layouts/flexible/flexible.css
plugins/layouts/flexible/flexible.inc
plugins/layouts/onecol/onecol.inc
plugins/layouts/threecol_25_50_25/threecol_25_50_25.inc
plugins/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.tpl.php
plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.inc
plugins/layouts/threecol_33_34_33/threecol_33_34_33.inc
plugins/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.tpl.php
plugins/layouts/twocol/twocol.inc
plugins/layouts/twocol_bricks/twocol_bricks.inc
plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php
plugins/layouts/twocol_stacked/twocol_stacked.inc
plugins/page_wizards/landing_page.inc [new file with mode: 0644]
plugins/page_wizards/node_override.inc [new file with mode: 0644]
plugins/style_bases/pane/pane_plain_box/icon.png [new file with mode: 0644]
plugins/style_bases/pane/pane_plain_box/pane-plain-box.css [new file with mode: 0644]
plugins/style_bases/pane/pane_plain_box/pane-plain-box.tpl.php [new file with mode: 0644]
plugins/style_bases/pane/pane_plain_box/pane_plain_box.inc [new file with mode: 0644]
plugins/style_bases/pane/pane_rounded_shadow/box-color.png [new file with mode: 0644]
plugins/style_bases/pane/pane_rounded_shadow/box-shadow.png [new file with mode: 0644]
plugins/style_bases/pane/pane_rounded_shadow/icon.png [new file with mode: 0644]
plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.css [new file with mode: 0644]
plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.tpl.php [new file with mode: 0644]
plugins/style_bases/pane/pane_rounded_shadow/pane_rounded_shadow.inc [new file with mode: 0644]
plugins/style_bases/region/region_plain_box/icon.png [new file with mode: 0644]
plugins/style_bases/region/region_plain_box/region-plain-box.css [new file with mode: 0644]
plugins/style_bases/region/region_plain_box/region-plain-box.tpl.php [new file with mode: 0644]
plugins/style_bases/region/region_plain_box/region_plain_box.inc [new file with mode: 0644]
plugins/style_bases/region/region_rounded_shadow/box-color.png [new file with mode: 0644]
plugins/style_bases/region/region_rounded_shadow/box-shadow.png [new file with mode: 0644]
plugins/style_bases/region/region_rounded_shadow/icon.png [new file with mode: 0644]
plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.css [new file with mode: 0644]
plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.tpl.php [new file with mode: 0644]
plugins/style_bases/region/region_rounded_shadow/region_rounded_shadow.inc [new file with mode: 0644]
plugins/styles/block.inc
plugins/styles/naked.inc
plugins/styles/stylizer.inc [new file with mode: 0644]
plugins/task_handlers/panel_context.inc
plugins/views/panels.views.inc [new file with mode: 0644]
plugins/views/panels_views_plugin_row_fields.inc [new file with mode: 0644]
templates/panels-dashboard-block.tpl.php [new file with mode: 0644]
templates/panels-dashboard-link.tpl.php [new file with mode: 0644]
templates/panels-dashboard.tpl.php
translations/panels.fr.po
translations/panels.hu.po [new file with mode: 0644]

diff --git a/API.txt b/API.txt
deleted file mode 100644 (file)
index ac5488f..0000000
--- a/API.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-# $Id$
-
-The most current (usually -dev) version of the Panels 2 API Documentation can be
-found online at http://doxy.samboyer.org/panels2/. 
index dfee568..6722594 100644 (file)
@@ -1,5 +1,94 @@
-Panels 6.x-3.x-dev
-==================
+Panels 6.x-dev
+==============
+hook_panels_pre_render() and hook_panels_post_render() getting empty displays.
+#865704: Editing mini panels styles failed badly due to missing piece of cache.
+Custom style could crash if trying to enable style reuse without first enabling stylizer.module.
+In Flexible, the "class" would not stick when adding a new region/row/column but would when editing.
+#861426: Editing a mini panel while that same mini panel is displayed on the page resulted in a crash.
+#871730: Panel nodes with IPE enabled let anyone use IPE.
+#877810: Multiple "Add flexible layout" tabs appearing based on number of flexible layouts stored.
+#873224 by thsutton: hook_requirements() could cause notice error.
+#867340 by cedarm: Restore removed but deprecated panels_render_layout() function.
+#871942 by dwatson: Invalid CSS made dragger image not appear during IPE.
+#867864: Fix a admin title and edit link problems with custom content types.
+#570194: Add support for the "checkall" module in the content permissions page.
+#696846: Mini panels conflated displayed title with admin title.
+#889824: Custom class could would cause stylizer class to become malformed.
+Add a simple "Panels" row style to Views that lets you put fields in a panel layout.
+Add two new Page Wizards to more easily create some of the most commonly used Panel pages.
+#869766 by rupl: flexible layout builder could fail with multiple columns in only one row. (Needs clear cache to see changes in generated CSS.)
+#897214: Mini panel uninstall tried to order mini panels by a non-existing field.
+#893456 by omerida: Change FAPI definition of panel node "body" field to be more consistent with other node body fields.
+#812658 by naxoc: Caching should take language into account.
+#890086: Possible crash in flexible layout by not having CTools export.inc loaded.
+
+Panels 6.x-3.7 (2010-Jul-26)
+==============
+Experimentally introduced the In-Place Editor (IPE).
+Updated Panels Stylizer to use CTools' new Export UI.
+Deprecated panels_get_panels() function. This will be eventually disappear. Use panels_get_regions() instead.
+#834084: Reusable flexible layout introduced. Admin UI at admin/build/panels/layouts.
+Clean up flexible. Attempt to reduce the use of unnecessary divs. 
+Add more ability to control flexible classes.
+Add ability to fix flexible canvas to a set width.
+Allow disabling of scaling for IE6 in flexible.
+Fixes to IPE:
+  Add pane would add the new pane outside region styling
+  Add pane would not appear in empty regions
+  Panes could not be dragged to empty regions
+  Save would lose content if they were inside stylizer styled regions
+#822234 by Amitaibu: Missing url() caused IPE to not work on sites not using top level URLs.
+#844062: Turned the normal drag and drop editor into a renderer plugin, made the IPE part of it. Also some fairly serious code reorganization.
+Improve the renderer selection framework. This framework has no UI, but it utilizes exportables and new choices can be hand crafted.
+Improve the IPE to use the proper cache if it is set, and to respect locking.
+#847784: IPE displayed "Edit" link when no such link should have existed.
+#843758: Legacy renderer caused "missing argument" warning when rendering.
+Made it possible to override jQuery UI sortable options via settings in the renderer plugin.
+Moved Panels stylizer to CTools. You must update CTools at the same time as Panels.
+#737602: Generic NOT checkbox for all access tests.
+#369628: Sort results by export key when bulk exporting.
+#690902: Improvement to export.module integration of panel nodes.
+#690814 by neclimdul: Panels mini edit form could overwrite page title.
+#675220 by dereine: Provide a watchdog message if a layout can't be loaded when rendering a panel.
+#713526 by psynaptic: Omit top and bottom region divs in stacked layouts if regions are empty.
+#823216 by jonathan1055: Put quotes around url() in rounded corners CSS.
+#603150 by hefox: Protect against the panels node add form being used in unusual places.
+#841824 by levacjeep: Extend panels_edit and panels_edit_layout to allow more options with the $destination.
+#842976 by drewish: Make sure the theme knows path and file information for plugin supplied themes.
+#743850 by foripepe: Add delete panel node permissions.
+#532900: Flexible sometimes did not update links when region settings changed.
+#723036: Node pane previews did not include node content as they should.
+#831216: It should not be possible to remove the Canvas.
+#216076: Make sure that empty panes are not rendered. Note: Legacy mode region styles can still render empty panes anyway.
+#609626: Static caching loading mini panels.
+#848712 by josh_k: Use panel nodes with IPE.
+#860306 by jcmarco: "Substitutions" fieldset would not open due to missing js.
+#856964: "Custom style" selection was not working.
+#863302: Allow IPE to cope with empty panes.
+#865344: by jrockowitz: Panel node access hook should use $account.
+
+Panels 6.x-3.5 (2010-May-28)
+==============
+Replaced panels_load_include() with ctools_include().
+Restored (and deprecated) panels_print_layout(), as other modules were relying on it.
+
+Panels 6.x-3.4 (2010-May-19)
+==============
+Introduced Panels Stylizer.
+Removed superfluous function, panels_sanitize_display().
+#655268: Fix a problem retrieving cached content created by #634746.
+Removed the old and crufty panels_print_layout().
+#745808 by Robbert: use number_format() in flexible width calculations to ensure locale settings cannot bork generated CSS.
+Stopped wasting cycles trying to render a mini panel in a block when we know the mini panel doesn't exist.
+#765978 by joshk: Added watchdoggery and a hook to panels_display_save().
+#746568 by mgriego: Disable block caching for mini panels.
+#798954 by dixon: Pass $form_state to style plugins' validate and submit callbacks.
+#779012 by c960657: implement the callback for a single content_type subtype in panels_mini to improve performance with  large numbers of mini panels.
+#763584 by neclimdul: Add a link to the block configuration page for mini-panels that takes the user to edit that mini- panel.
+Security updates: protect the importer with the 'use PHP for block visibility' permission.
+
+Panels 6.x-3.3 (2010-Feb-01)
+==============
 
 #612850: Fix crash bug with panes referencing deleted/missing views.
 #614178: Transition to panels-pane.tpl.php caused empty panes to display again.
@@ -12,15 +101,16 @@ Panels 6.x-3.x-dev
 #683162: Convert all plugins to use new $plugin = array() format. This is less brittle than the specially named hook.
 #543898: Fix notice error when a layout has no regions.
 #647706: Mini panel names can only be 32 characters, but the form failed to restrict the limit.
+Introduce the Panels stylizer module which builds on the CTools stylizer base to create user definable, recolorable styles.
 
-Panels 6.x-3.2
+Panels 6.x-3.2 (2009-Oct-21)
 ==============
 
 #606980 by Deciphered and sethfreach: Typo caused panel-pane class to disappear if another class was assigned.
 #607242: Using the wrong variable in hook_block can cause title to not be overridable.
 #608062: Visibility rules did not appear in the list when added to a pane (though they still applied).
 
-Panels 6.x-3.1
+Panels 6.x-3.1 (2009-Oct-15)
 ==============
 
 #552846 by eMPee584: Fix notice with tablesorts on mini panels page if no mini panels exist.
@@ -33,7 +123,7 @@ Add a new field to allow setting a panel title from one of its panes.
 #583172 by jacine: Turn panel pane into a template, as it always should have been.
 #604404: Make sure caching does not trigger for form POSTs.
 
-Panels 6.x-3.0
+Panels 6.x-3.0 (2009-Aug-19)
 ==============
 
 #529258 by stella: Attempting to modify CSS properties on mini panel causes ajax failure.
@@ -126,4 +216,4 @@ Add 'content type' property to content types.
 Add 'defaults' property to content types.
 Panels views cleanup, including making exposed form blocks available.
 #391788 by guix: Simple cache should not unserialize data that is now already unserialized.
-#367635 by lee20: View title built too early and so lost arguments.
\ No newline at end of file
+#367635 by lee20: View title built too early and so lost arguments.
index 8bda859..d340127 100644 (file)
@@ -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.
diff --git a/TODO.txt b/TODO.txt
deleted file mode 100644 (file)
index dcfc27d..0000000
--- a/TODO.txt
+++ /dev/null
@@ -1,132 +0,0 @@
-# $Id$
-
-PLEASE NOTE THAT THIS FILE CONTAINS VERY OUTDATED INFORMATION, KEPT ONLY FOR HISTORICAL PURPOSES.
-
-For the current plans related to Panels2, you should consult the groups.drupal.org Panels group:
-
-http://groups.drupal.org/panels
-
-Here's a rough sketch of the work plan shortly prior to the 5.x-2.0-beta4 release:
-
-5.x-beta3 ==> beta4 ==> beta5 ==> RC1 ==> RCs... ==> 5.x RELEASE!! ==> 5.x-2.1
-                                   |
-                               6.x port ==> 6.x RCs... ==> 6.x RELEASE!! ==> 6.x-2.1
-                                                             |
-                                                           6.x-3.x-ALPHA ==> (FUTURE OF PANELS)
-
-==========================================================
-
-TODO:
-
-X = DONE
-o = TODO
-- = Skipped/postponed
-
-ajax/js
-    o remove panels_hidden, use only javascript
-    o Remove 'panels_set' and use the broken up drupal_get_form functions
-      rather than drupal_get_form for our multi-step stuff.
-    X Add permission checking to all the ajax functions
-    o Break up panels_ajax into separate entry areas [mostly done]
-    o ensure complete degradability
-    X Make sure the icons on the 'change layout' page are clickable
-    X Loading animation for panels-modal
-    X Make the titlebar outside of the overflow in the modal
-    o Change show/hide to use AJAX. 
-    X Write my own drag & drop
-
-modules
-    X panels node
-    X mini-panel
-    o panels-profile
-    o panels-dashboard-compat (upgrade all the old dashboard installs)
-
-panels common
-    o update settings to use categories like the 'add content' popup now does.
-    o Clean up and better document this code
-
-panels page
-    X panels page 'default' panels
-    X allow panel-page to use $arg/$node/$node-type/$user substitution like Views
-    X allow panel-page to get a context; allow modules to provide this context.
-    X allow panel-page to do menu item & tab like Views.
-    X provide 'edit' tab for panels pages
-    X allow setting to not display blocks
-    o Clone panels page
-
-mini panels
-    X Allow mini panels to require an argument (and thus be just panel content)
-
-content types
-    - Allow creating new custom blocks.
-    X Get all 'block' settings into blocks.
-    X Remove all calls to the 'admin' callback
-    X Make panels_get_content_type() like using for layout
-    X Translate views to new content format
-    X Translate node to new content format
-    X Translate custom to new content format
-    X Allow content types to be aware of panel context.
-    X Allow content types to include or exclude themselves based upon context.
-    X Figure out a way for blocks to easily have per-block icons
-    X sort blocks in content type list
-    X sort views in content type list
-    X add weight so custom and node will float up
-    X make forms fit a bit better
-    X Allow display to have 'arguments' that can be passed on to Views
-      more fluidly
-    X give views.inc setting to NOT use arguments.
-    X unify title overriding.
-    X node_content type 
-    X taxonomy term types
-    o user types
-    o views context sensitive conversions
-
-layouts
-    X Allow each layout to have settings
-    X Create a flexible layout where you can quickly create arbitrary layouts
-    X Fix the div div problem in all layouts
-
-icons
-    X Change configure icon
-    X Change remove pane icon
-    X Find add content icon
-    X Create icons for content types
-
-Flexible layout
-    o Allow fixed width sidebars
-    o Use a slider to control widths
-
-Styles:
-    o Convert DND to a style
-    X Rounded corners style
-
-Arguments:
-    X taxonomy argument
-    X user argument
-
-panels API
-    X Provide method to directly edit/configure an individual content block if viewer has permissions
-    X Provide method to control pane visibility. Make sure this is controllable from the admin so that not all users can get to it.
-    X Change add content to be per panel
-    X Make Cancel actually cancel
-    X Wrap calls to existing callbacks
-    X Make a function to simply get the IDs of all content-types
-    X Allow panels_edit_display to restrict available content to a preprepared list
-    X Use cache instead of putting everything in $_SESSION
-    X create panels_render_display() (or panels_display_render)
-    X Allow API to accept context
-    X Allow API to accept content
-    X Allow API to accept arguments
-
-General:
-    o CSS class/id name cleanup and unification.
-    o Make sure core blocks that require context are labelled as such.
-
-Documentation
-    o Creating new panel modules that use the API
-    o Creating new content types
-    o Creating new layouts
-    o Creating new styles
-    o Creating new arguments
-    o Themeing panels
-    o Doing interesting things with Panels
index 002c234..500bc9f 100644 (file)
@@ -1,15 +1,6 @@
 /* $Id$ */
 
-.dashboard-entry .dashboard-icon {
-  float: left;
-  width: 50px;
-  height: 50px;
-  text-align: center;
-  vertical-align: middle;
-  margin: 0 5px;
-}
-
-.dashboard-entry .dashboard-link a {
+.dashboard-entry .dashboard-link {
   font-size: 120%;
   font-weight: bold;
 }
   margin-bottom: .5em;
 }
 
-.dashboard-entry {
-  padding-left: 1em;
-}
-
-.dashboard-entry .dashboard-link form {
-  margin: 0 !important;
-}
-
 .dashboard-link form input {
   margin: 0;
 }
   text-align: center;
 }
 
-table.panels-manage {
-  margin: 0 1em 1em 1em;
+.dashboard-content table {
+  margin: 0;
   width: 100%;
 }
 
-.dashboard-pages .links {
+.dashboard-content {
+  padding: 0 1em;
+}
+
+.panels-dashboard .links {
   text-align: right;
 }
 
 .dashboard-pages .page-manager-page-operations {
   text-align: right;
-}
\ No newline at end of file
+}
+
+.dashboard-block {
+  padding-bottom: 1em;
+  border-bottom: 1px dotted #ddd;
+  margin-bottom: 1em;
+}
index de3ea22..c969ebd 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id$ */
 
 .layout-link {
-  float: left; 
+  float: left;
   padding: 1em;
   width: 125px;
   height: 160px;
 .panels-layouts-checkboxes .form-checkboxes .form-item .layout-icon,
 #panels-choose-layout .form-radios .form-item .layout-icon {
   float: none;
-  height: 15em;
+  height: 11em;
   width: 90px;
 }
 .panels-layouts-checkboxes .form-checkboxes .option,
 #panels-choose-layout .form-radios .form-item .option {
   width: 50px;
   display: block;
-  text-align: center; 
+  text-align: center;
 }
 
 .panels-layouts-checkboxes .form-submit,
   margin-right: 20px;
 }
 
+.panels-layouts-checkboxes .panels-layouts-category {
+  font-weight: bold;
+  width: 100%;
+  float: left;
+}
+
 .panels-layouts-checkboxes .description {
   clear: left;
 }
index b7b82e7..ba14a5f 100644 (file)
@@ -100,7 +100,7 @@ div.panels-set-title-hide .panel-pane-is-title {
   background: #f8f8f8;
 }
 
-.panel-portlet .content {
+.panel-portlet .pane-content {
   margin: .5em 0 .5em 0;
   padding: 0 .25em 0 .25em;
   display: none; /* initially hidden */
@@ -440,7 +440,7 @@ a.close img {
 }
 
 
-div.panel-pane div.ctools-dropdown-container-wrapper {
+#panels-dnd-main div.panel-pane div.ctools-dropdown-container-wrapper {
   margin-left: -158px;
   margin-top: -4px;
 }
@@ -459,22 +459,22 @@ html.js div.panels-display-links div.ctools-dropdown-container ul li a {
 }
 */
 
-.panel-pane .pane-title {
+#panels-dnd-main .panel-pane .pane-title {
   padding: 0.25em 0.5em;
 }
-.panel-pane .pane-title:after {
+#panels-dnd-main .panel-pane .pane-title:after {
   font-size: 0.8em;
   color: crimson;
   letter-spacing: normal;
   display: block;
 }
-.panel-pane.hidden-pane .pane-title:after {
+#panels-dnd-main .panel-pane.hidden-pane .pane-title:after {
   content: " status: hidden";
 }
-.panel-pane.changed .pane-title:after {
+#panels-dnd-main .panel-pane.changed .pane-title:after {
   content: " status: changes not saved";
 }
-.panel-pane.hidden-pane.changed .pane-title:after {
+#panels-dnd-main .panel-pane.hidden-pane.changed .pane-title:after {
   content: " status: hidden & changed";
 }
 
@@ -499,7 +499,9 @@ html.js div.panels-display-links div.ctools-dropdown-container {
   -moz-border-radius: 0.333em;
   -webkit-box-shadow: 0.333em 0.333em 0.333em rgba(0, 0, 0, 0.25);
   font-size: 0.9em;
+  font-weight: bold;
 }
+
 html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li,
 html.js div.panels-display-links div.ctools-dropdown-container ul li {
 
diff --git a/docs/docs.inc b/docs/docs.inc
deleted file mode 100644 (file)
index 6cde2f0..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-// $Id$
-
-/**
-@file
-This file contains no working PHP code; it exists to provide additional
-documentation for doxygen as well as to document hooks and API functions in
-the standard Drupal manner.
- */
-
-/**
-@mainpage Panels 2 Documentation
-
-The Panels 2 API Documentation is intended as a guide to the innerworkings of Panels, and will primarily be of interest to Drupal themers and developers. \n\n Panels is a complex and abstract system, which necessitates the use of a lot of specialized terms. If you are new to Panels, we strongly recommend you consult the @ref panels_glossary "Panels Glossary" to familiarize yourself with the jargon.\n\n 
-
-@section panels_api_top Developer API Reference
-\li @subpage panels_api_plugins "Plugins API"
-
-Please note that this documentation is generated directly from the Panels project source code. Consequently, if you find errors or have improvements, you can file a patch in the Panels issue queue at http://drupal.org/project/issues/panels like any other patch. Documentation that is not specific to a file, function, or class is contained in the .inc files in /panels/docs.
- */
-
-/**
-@page panels_glossary Panels Glossary
-
-\par 
-<em>"Hey - you know that box thing in Panels? It's that...ugh, y'know, it's that rectangular, light-colored box. And it's in the Panels editor. It's right there on the editing form, with its name at the top. No, no! Not the name I gave it on the other form, I CAN read - I mean the name of the boxy THING! What in tarnation do I call the thing itself!?!"<sup>*</sup></em>\n
-
-If you've ever tried to talk or write about - or, let's be honest, even <em>use</em> - Panels, then you probably know right where that sentence is coming from. Panels has all kinds of 'things' in it - things you click on, things you look at, things that contain other things, things that pop up, things that slide away, things that...well, the list goes on. It's enough of a challenge knowing what they are when we see them, but trying to communicate about Panels with real people, and not Panels itself? Fuhgettaboutit. So we're working on a little glossary that we're hoping might make that just a little better.
-
-<hr>
-
-@SECglossary{layout,Layout Terms}
-Panels' basic purpose is structure content for viewing, and it makes use of its own set of terms to describe this structure. There are three essential terms - @LGt{pane,Pane}, @LGt{panel,Panel}, and @LGt{display,Display}.
-@AG{pane,Pane} Panes are individual content items, and are the smallest/highest-level unit of content that the Panels API itself configures and renders. If you've used the Panels 2 content editor at all, then you're probably most familiar with panes via the 'Add Content' @LGt{modal} where you pick out the content you'd like to add. Each one of the items on that form is a @LGt{content_types,content type} (or subtype); once you select one and fill out the form, you'll see a graphical representation of the new pane appear.
-
-@AG{panel,Panel} Panels are the containers that hold @LGt{pane,Panes}. When you select a @LGt{layouts,layout}, what you're choosing is the number and arrangement of panels. The content editor represents panels quite well: panels are the blue (by default) boxes with their name at the top and the plus-shaped button that opens the 'Add content' @LGt{modal}. And yes the namespace ambiguity is awful - we're resolving it soon.
-
-@AG{display,Display} The Display unit is the most abstract of the three layout terms. Again, the Panels 2 content editor is a good way to think about it: there's no visual representation of a display in the editor because the display IS what you're editing: it's the object that contains the @LGt{panel,panels}, and it's settings in the display object that govern, for example, which content types are available on the 'Add Content' form. \n Displays are abstract and often the most difficult for newcomers to Panels to understand. They're also probably the most important thing that developers implementing the Panels API in a @LGt{clientmodule,client module} need to understand, as client modules work primarily with display objects. Displays are essentially the 'root' object of the API - this is functionally true in Panels 2, but it will be literally the case after Panels is rewritten in OO for Panels 3. \n In addition to @LGt{panel,panels}, displays hold almost all of Panels' plugins and are required in most of the @ref mainapi "main API functions"; this is why client modules interact with the Panels API primarily through display objects.
-
-@SECglossary{plugins,Panels Plugins}
-At its core, Panels is really just a structure for negotiating and handling its plugins. For a thorough introduction to plugins, see the introduction to the @LA{plugins,plugins API}.
-
-@SSECglossary{plugins,general,General Terms}
-The following terms are relevant to all plugin types:
-@AG{p-d-func,Plugin Declaration Function} All plugins begin with a declaration function. These declaration functions are what Panels searches for in compiling the list of available plugins, and as such are subject to strict naming conventions. The sole purpose of a plugin declaration function is to return a \pdarray populated with plugin \plugprops.
-
-@AG{p-d-array,Plugin Definition Array} Plugin definition arrays are returned from \pdfuncs. They are made up of two types of \plugprops: @LGt{p-p-setting,setting properties} and @LGt{p-p-callback,callback properties}. During each page request, Panels stores the information provided by these plugin definition arrays using the logic defined in panels_get_plugins(), allowing other parts of the Panels API to check settings and/or fire callbacks that have been defined in the definition array.
-
-@AG{plugprop,Plugin Properties} Every plugin definition array is an associative array, and we refer to each of the key/value pairs in that array as a 'property.' These properties govern all aspects of the plugin's behavior.
-
-@AG{p-p-setting,Setting Properties} Setting properties are values that Panels will use directly in various internal processes when deciding how to handle the plugin. Many setting properties are booleans that govern how Panels moves that plugin through various control statements; others are simple strings for administrative interface titles, or drupal 'weight' integers used to sort the plugin within lists.
-
-@AG{p-p-callback,Callback Properties} Callback properties are strings containing function names; Panels fires these callbacks at certain points in its internal processes.
-
-
-@SSECglossary{plugins,types,Plugin Types}
-Panels currently manages eight different types of plugins:
-@AG{arguments,Arguments}
-
-@AG{content_types,Content Types}
-
-@AG{context,Context}
-
-@AG{layouts,Layouts}
-
-@AG{styles,Styles}
-
-@AG{relationships,Relationships}
-
-@AG{cache,Cache}
-
-@AG{switchers,Switchers}
-
-@SECglossary{misc,Miscellaneous Terms}
-If we could find categories to put these in, we would. Just because we can't find a group to put them in, though, doesn't mean they're not important!
-@AG{modal,Javascript Modal Form}
-
-@AG{clientmodule,Client Module}
-
-\n\n
-<small><sup>*</sup> Yep, that's right - we were talking about a @LGt{panel,Panel}! Oh, the ambiguity...</small>
-
- */
-
-/**
-@page panels_api_plugins Plugins API Reference
-
-The Panels API uses a system of plugins to manage pretty much everything it
-does. Without the plugins, the API is an empty shell; plugins define the
-'what, where, and how' of Panels' behavior. Panels uses eight different
-types of plugins. Specific reference information for each plugin type is
-available on the corresponding page:
-    - @Psubplug{content_types,Content Types}
-
-These correspond to the similarly-named subdirectories filled with .inc
-files - each of those .inc files declares at least one plugin.
-
-@section panels_api_plugins_top_structure Structure of a Plugin
-
-All panels plugins have the same basic structure. Each plugin begins with a
-\pdfunc, which Panels identifies either by function name alone or through a
-combination of function name and .inc file location depending on the method
-being used. Regardless of the method or plugin type, Panels expects the same
-thing to be returned from the plugin declaration function: a \pdarray. The
-available \plugprops depend entirely on the type of plugin, so to learn more,
-you'll have to consult the documentation for each individual plugin type.
-
-
-There are eight types of panels plugins at present: context, relationships,
-arguments, content_types, styles, layouts, cache, and switchers; Panels
-provides an API that allows any module to implement these, but there's a
-starter set that comes with Panels itself. They're found in the various
-subdirectories that live under the Panels directory.
-
- */
diff --git a/docs/doxygen.css b/docs/doxygen.css
deleted file mode 100644 (file)
index a8fba01..0000000
+++ /dev/null
@@ -1,485 +0,0 @@
-BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV {
-       font-family: Geneva, Arial, Helvetica, sans-serif;
-}
-BODY,TD {
-       font-size: 90%;
-}
-H1 {
-       text-align: center;
-       font-size: 160%;
-}
-H2 {
-       font-size: 140%;
-  font-family: Verdana, sans-serif;
-}
-H3 {
-       font-size: 130%;
-  font-family: Verdana, sans-serif;
-}
-p {
-  text-indent: 1em;
-}
-CAPTION { 
-       font-weight: bold 
-}
-DIV.qindex {
-       width: 100%;
-       background-color: #e8eef2;
-       border: 1px solid #84b0c7;
-       text-align: center;
-       margin: 2px;
-       padding: 2px;
-       line-height: 140%;
-}
-DIV.navpath {
-       width: 100%;
-       background-color: #e8eef2;
-       border: 1px solid #84b0c7;
-       text-align: center;
-       margin: 2px;
-       padding: 2px;
-       line-height: 140%;
-}
-DIV.navtab {
-       background-color: #e8eef2;
-       border: 1px solid #84b0c7;
-       text-align: center;
-       margin: 2px;
-       margin-right: 15px;
-       padding: 2px;
-}
-div.contents {
-  margin: 0 auto;
-  max-width: 900px;
-}
-div#top {
-  max-width: 900px;
-  margin: 0 auto;
-}
-TD.navtab {
-       font-size: 70%;
-}
-A.qindex {
-       text-decoration: none;
-       font-weight: bold;
-       color: #1A419D;
-}
-A.qindex:visited {
-       text-decoration: none;
-       font-weight: bold;
-       color: #1A419D
-}
-A.qindex:hover {
-       text-decoration: none;
-       background-color: #ddddff;
-}
-A.qindexHL {
-       text-decoration: none;
-       font-weight: bold;
-       background-color: #6666cc;
-       color: #ffffff;
-       border: 1px double #9295C2;
-}
-A.qindexHL:hover {
-       text-decoration: none;
-       background-color: #6666cc;
-       color: #ffffff;
-}
-A.qindexHL:visited { 
-       text-decoration: none; 
-       background-color: #6666cc; 
-       color: #ffffff 
-}
-A.el { 
-       text-decoration: none; 
-       font-weight: bold 
-}
-A.elRef { 
-       font-weight: bold 
-}
-A.code:link { 
-       text-decoration: none; 
-       font-weight: normal; 
-       color: #0000FF
-}
-A.code:visited { 
-       text-decoration: none; 
-       font-weight: normal; 
-       color: #0000FF
-}
-A.codeRef:link { 
-       font-weight: normal; 
-       color: #0000FF
-}
-A.codeRef:visited { 
-       font-weight: normal; 
-       color: #0000FF
-}
-A:hover { 
-       text-decoration: none;  
-       background-color: #f2f2ff 
-}
-DL.el { 
-       margin-left: -1cm 
-}
-.fragment {
-       font-family: monospace, fixed;
-       font-size: 95%;
-}
-PRE.fragment {
-       border: 1px solid #CCCCCC;
-       background-color: #f5f5f5;
-       margin-top: 4px;
-       margin-bottom: 4px;
-       margin-left: 2px;
-       margin-right: 8px;
-       padding-left: 6px;
-       padding-right: 6px;
-       padding-top: 4px;
-       padding-bottom: 4px;
-}
-DIV.ah { 
-       background-color: black; 
-       font-weight: bold; 
-       color: #ffffff; 
-       margin-bottom: 3px; 
-       margin-top: 3px 
-}
-
-DIV.groupHeader {
-       margin-left: 16px;
-       margin-top: 12px;
-       margin-bottom: 6px;
-       font-weight: bold;
-}
-DIV.groupText { 
-       margin-left: 16px; 
-       font-style: italic; 
-       font-size: 90% 
-}
-BODY {
-       background: white;
-       color: black;
-       margin-right: 20px;
-       margin-left: 20px;
-}
-TD.indexkey {
-       background-color: #e8eef2;
-       font-weight: bold;
-       padding-right  : 10px;
-       padding-top    : 2px;
-       padding-left   : 10px;
-       padding-bottom : 2px;
-       margin-left    : 0px;
-       margin-right   : 0px;
-       margin-top     : 2px;
-       margin-bottom  : 2px;
-       border: 1px solid #CCCCCC;
-}
-TD.indexvalue {
-       background-color: #e8eef2;
-       font-style: italic;
-       padding-right  : 10px;
-       padding-top    : 2px;
-       padding-left   : 10px;
-       padding-bottom : 2px;
-       margin-left    : 0px;
-       margin-right   : 0px;
-       margin-top     : 2px;
-       margin-bottom  : 2px;
-       border: 1px solid #CCCCCC;
-}
-TR.memlist {
-       background-color: #f0f0f0; 
-}
-P.formulaDsp { 
-       text-align: center; 
-}
-IMG.formulaDsp {
-}
-IMG.formulaInl { 
-       vertical-align: middle; 
-}
-SPAN.keyword       { color: #008000 }
-SPAN.keywordtype   { color: #604020 }
-SPAN.keywordflow   { color: rgb(0, 119, 0); }
-SPAN.comment       { color: rgb(255, 128, 0); }
-SPAN.preprocessor  { color: #806020 }
-SPAN.stringliteral { color: rgb(221, 0, 0); }
-SPAN.charliteral   { color: #008080 }
-SPAN.vhdldigit     { color: #ff00ff }
-SPAN.vhdlchar      { color: #000000 }
-SPAN.vhdlkeyword   { color: #700070 }
-SPAN.vhdllogic     { color: #ff0000 }
-
-.mdescLeft {
-       padding: 0px 8px 4px 8px;
-       font-size: 80%;
-       font-style: italic;
-       background-color: #FAFAFA;
-       border-top: 1px none #E0E0E0;
-       border-right: 1px none #E0E0E0;
-       border-bottom: 1px none #E0E0E0;
-       border-left: 1px none #E0E0E0;
-       margin: 0px;
-}
-.mdescRight {
-        padding: 0px 8px 4px 8px;
-       font-size: 80%;
-       font-style: italic;
-       background-color: #FAFAFA;
-       border-top: 1px none #E0E0E0;
-       border-right: 1px none #E0E0E0;
-       border-bottom: 1px none #E0E0E0;
-       border-left: 1px none #E0E0E0;
-       margin: 0px;
-}
-.memItemLeft {
-       padding: 1px 0px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E0E0;
-       border-right-color: #E0E0E0;
-       border-bottom-color: #E0E0E0;
-       border-left-color: #E0E0E0;
-       border-top-style: solid;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       background-color: #FAFAFA;
-       font-size: 80%;
-}
-.memItemRight {
-       padding: 1px 8px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E0E0;
-       border-right-color: #E0E0E0;
-       border-bottom-color: #E0E0E0;
-       border-left-color: #E0E0E0;
-       border-top-style: solid;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       background-color: #FAFAFA;
-       font-size: 80%;
-}
-.memTemplItemLeft {
-       padding: 1px 0px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E0E0;
-       border-right-color: #E0E0E0;
-       border-bottom-color: #E0E0E0;
-       border-left-color: #E0E0E0;
-       border-top-style: none;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       background-color: #FAFAFA;
-       font-size: 80%;
-}
-.memTemplItemRight {
-       padding: 1px 8px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E0E0;
-       border-right-color: #E0E0E0;
-       border-bottom-color: #E0E0E0;
-       border-left-color: #E0E0E0;
-       border-top-style: none;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       background-color: #FAFAFA;
-       font-size: 80%;
-}
-.memTemplParams {
-       padding: 1px 0px 0px 8px;
-       margin: 4px;
-       border-top-width: 1px;
-       border-right-width: 1px;
-       border-bottom-width: 1px;
-       border-left-width: 1px;
-       border-top-color: #E0E0E0;
-       border-right-color: #E0E0E0;
-       border-bottom-color: #E0E0E0;
-       border-left-color: #E0E0E0;
-       border-top-style: solid;
-       border-right-style: none;
-       border-bottom-style: none;
-       border-left-style: none;
-       color: #606060;
-       background-color: #FAFAFA;
-       font-size: 80%;
-}
-.search { 
-       color: #003399;
-       font-weight: bold;
-}
-FORM.search {
-       margin-bottom: 0px;
-       margin-top: 0px;
-}
-INPUT.search { 
-       font-size: 75%;
-       color: #000080;
-       font-weight: normal;
-       background-color: #e8eef2;
-}
-TD.tiny { 
-       font-size: 75%;
-}
-a {
-       color: #1A41A8;
-}
-a:visited {
-       color: #2A3798;
-}
-.dirtab { 
-       padding: 4px;
-       border-collapse: collapse;
-       border: 1px solid #84b0c7;
-}
-TH.dirtab { 
-       background: #e8eef2;
-       font-weight: bold;
-}
-HR { 
-       height: 1px;
-       border: none;
-       border-top: 1px solid black;
-}
-
-/* Style for detailed member documentation */
-.memtemplate {
-       font-size: 80%;
-       color: #606060;
-       font-weight: normal;
-       margin-left: 3px;
-} 
-.memnav { 
-       background-color: #e8eef2;
-       border: 1px solid #84b0c7;
-       text-align: center;
-       margin: 2px;
-       margin-right: 15px;
-       padding: 2px;
-}
-.memitem {
-       padding: 4px;
-       background-color: #eef3f5;
-       border-width: 1px;
-       border-style: solid;
-       border-color: #dedeee;
-       -moz-border-radius: 8px 8px 8px 8px;
-}
-.memname {
-       white-space: nowrap;
-       font-weight: bold;
-}
-.memdoc{
-       padding-left: 10px;
-}
-.memproto {
-       background-color: #d5e1e8;
-       width: 100%;
-       border-width: 1px;
-       border-style: solid;
-       border-color: #84b0c7;
-       font-weight: bold;
-       -moz-border-radius: 8px 8px 8px 8px;
-}
-.paramkey {
-       text-align: right;
-}
-.paramtype {
-       white-space: nowrap;
-}
-.paramname {
-       color: #602020;
-       font-style: italic;
-       white-space: nowrap;
-}
-/* End Styling for detailed member documentation */
-
-/* for the tree view */
-.ftvtree {
-       font-family: sans-serif;
-       margin:0.5em;
-}
-/* these are for tree view when used as main index */
-.directory { 
-       font-size: 9pt; 
-       font-weight: bold; 
-}
-.directory h3 { 
-       margin: 0px; 
-       margin-top: 1em; 
-       font-size: 11pt; 
-}
-
-/* The following two styles can be used to replace the root node title */
-/* with an image of your choice.  Simply uncomment the next two styles, */
-/* specify the name of your image and be sure to set 'height' to the */
-/* proper pixel height of your image. */
-
-/* .directory h3.swap { */
-/*     height: 61px; */
-/*     background-repeat: no-repeat; */
-/*     background-image: url("yourimage.gif"); */
-/* } */
-/* .directory h3.swap span { */
-/*     display: none; */
-/* } */
-
-.directory > h3 { 
-       margin-top: 0; 
-}
-.directory p { 
-       margin: 0px; 
-       white-space: nowrap; 
-}
-.directory div { 
-       display: none; 
-       margin: 0px; 
-}
-.directory img { 
-       vertical-align: -30%; 
-}
-/* these are for tree view when not used as main index */
-.directory-alt { 
-       font-size: 100%; 
-       font-weight: bold; 
-}
-.directory-alt h3 { 
-       margin: 0px; 
-       margin-top: 1em; 
-       font-size: 11pt; 
-}
-.directory-alt > h3 { 
-       margin-top: 0; 
-}
-.directory-alt p { 
-       margin: 0px; 
-       white-space: nowrap; 
-}
-.directory-alt div { 
-       display: none; 
-       margin: 0px; 
-}
-.directory-alt img { 
-       vertical-align: -30%; 
-}
diff --git a/docs/sample_plugin_ct.inc b/docs/sample_plugin_ct.inc
deleted file mode 100644 (file)
index 5b5eb02..0000000
+++ /dev/null
@@ -1,902 +0,0 @@
-<?php
-// $Id$
-
-/**
-@file
-Plugin Reference: Content Types
-
-API documentation for Content Type (Soon to be: Pane Type) plugins.
-/
-
-/**
-@Pplug{content_types,Content Types}
-Content type plugins define the content you can put into panes for display; If you want to see a particular piece of content rendered by Panels, you need to write a content type. \n
-
-<p>Remember - although grouping the callbacks defined in these properties into the same .inc file can be handy, you're not bound to doing so. If you have a visibility checker that you want to share across a lot of content types, for example, consider defining the visibility checker in the main .module file while keeping the rest of the plugin in its own .inc file.</p>
-<p>Some unnecessary duplication and inelegancies persist in the logic behind content type plugin properties; this is particularly noteworthy in the case of the add/edit property distinctions. These issues are allowed to persist largely for legacy compatibility reasons.</p>
-<p>Plugin declaration functions must adhere to a particular naming convention; see panels_get_directories() for details. \n Note that the \pdarray provided by this sample does NOT define a coherent, working content type; displaying the full range of the plugin's flexibility makes it impossible to do so. For working examples, see the various plugins defined in the panels/content_types directory.</p>
-
-@SECplug{content_types,pdfunc,Content Type Plugin Declaration}
-<p>This sample \pdfunc returns a definition array containing ALL of the \plugprops that Panels currently allows. The string used for the array key is significant: it determines the 'type' that panes which are created by this plugin will be assigned.</p>
-<p>Keep in mind when choosing a type name that namespace collisions are both silent and destructive (the Panels engine will not emit any errors, it will simply overwrite data). Such collisions will always be decided in favor of the last plugin processed; processing order order is generally determined by the weight of the module (in the system table) defining the plugin.</p>
-<p>All the plugins that come packaged with Panels necessarily obey a strict naming convention: the array key used for $items is the same as the name of the file itself, as well as being the same as the string prefixed by the module name (in this case, 'panels_'), and affixed by a string that indicates the type of plugin (in this case, 'panels_content_types'). Your modules need not follow the same conventions, although it is recommended that you do if possible; again, refer to panels_get_directories().</p>
-@code
-function panels_SAMPLE_CT_panels_content_types() {
-  $items['SAMPLE_CT'] = array(
-    'title' => t('Sample Content Type'),
-    'weight' => -10,
-    'single' => TRUE,
-    'content_types' => 'panels_admin_content_types_SAMPLE_CT',
-    'render callback' => 'panels_content_SAMPLE_CT',
-    'add callback' => 'panels_admin_add_SAMPLE_CT',
-    'edit callback' => 'panels_admin_edit_SAMPLE_CT',
-    'add validate callback' => 'panels_admin_validate_SAMPLE_CT',
-    'edit validate callback' => 'panels_admin_validate_SAMPLE_CT',
-    'add submit callback' => 'panels_admin_submit_SAMPLE_CT',
-    'edit submit callback' => 'panels_admin_submit_SAMPLE_CT',
-    'title callback' => 'panels_admin_title_SAMPLE_CT',
-    'editor render callback' => 'panels_admin_pane_render_SAMPLE_CT',
-    'render last' => TRUE,
-    'visibility control' => 'panels_admin_visibility_control_SAMPLE_CT',
-    'visibility submit' => 'panels_admin_visibility_submit_SAMPLE_CT',
-    'visibility check' => 'panels_content_visibility_check_SAMPLE_CT',
-    'visibility serialize' => TRUE,
-    'role-based access' => FALSE,
-    'roles and visibility' => TRUE,
-    'form control' => 'panels_admin_form_control_SAMPLE_CT',
-  );
-  return $items;
-}
-@endcode \n\n
-
-@SSECplug{content_types,pdfunc,setprops,Overview of Setting Properties}
-<p>Only a few of the twenty properties defined for content type plugins are required, and some of them have default values that Panels will set if you don't assign anything. This first table covers setting properties. Keep in mind that although these tables indicate that there are requirements and interdependencies among the properties, there are no systematic checks to ensure that a plugin meets the requirements. Panels simply assumes that you did it right.</p>
-\n
-<table>
-  <tr>
-    <td>Property Name</td>
-    <td>Data Type</td>
-    <td>Required?</td>
-    <td>Default Value</td>
-    <td>Dependencies</td>
-    <td>Notes</td>
-  </tr>
-  <tr>
-    <td>@AAP{content_types,title,title}</td>
-    <td>string</td>
-    <td>Yes</td>
-    <td></td>
-    <td>None</td>
-    <td>The title that will be used for this pane on the 'Add Content' modal form, and in the display content editor in general. This is a purely internal setting; normal users will never see it.</td>
-  </tr>
-  <tr>
-    <td>@AAP{content_types,weight,weight}</td>
-    <td>Integer</td>
-    <td>No</td>
-    <td>\c 0</td>
-    <td>None</td>
-    <td>Standard drupal weighting concept at work here; all it determines is the position of this content type's icon relative to the other content type icons on the general Panels configuration forms. See panels_common_settings().</td>
-  </tr>
-  <tr>
-    <td>@AAP{content_types,single,single}</td>
-    <td>Boolean</td>
-    <td>No</td>
-    <td>\c FALSE</td>
-    <td>None</td>
-    <td>Indicates that this content type plugin provides only a single content type. Currently, this setting is ONLY used in figuring out how to group the content type on the general Panels configuration forms; see panels_common_settings(). Check out panels_admin_content_types_block() for an example of how one plugin can used define multiple content types (technically, multiple subtypes)</td>
-  </tr>
-  <tr>
-    <td>@AAP{content_types,render-last,render last}</td>
-    <td>Boolean</td>
-    <td>No</td>
-    <td><var>FALSE</var><sup>1</sup></td>
-    <td>none</td>
-    <td>If set to \c TRUE, this pane will be pushed to the back of the line during the render routine. See panels_render_panes().</td>
-  </tr>
-  <tr>
-    <td>@AAP{content_types,visibility-serialize,visibility serialize}</td>
-    <td>Boolean</td>
-    <td>Yes<sup>2,3</sup></td>
-    <td>FALSE</td>
-    <td>@LAP{content_types,t-visibility-control,visibility control}</td>
-    <td>If \c TRUE, then contents of $pane->visibility will be serialized before being saved to the database. This should be set as \c TRUE if, for example, your visibility form widget uses checkboxes (and therefore generates an array), as opposed to if your widget uses radios (and therefore generates an integer that can be stored directly). See panels_content_config_form_submit() and panels_save_display() to better understand how this works.</td>
-  </tr>
-  <tr>
-    <td>@AAP{content_types,role-based-access,role-based access}</td>
-    <td>Boolean</td>
-    <td>No</td>
-    <td><var>TRUE</var><sup>4</sup></td>
-    <td>none</td>
-    <td>Boolean setting to indicate whether you want the your content type to utilize the Panels API's built-in access system, which is based on drupal user roles. Set this to FALSE to disable role-based access.</td>
-  </tr>
-  <tr>
-    <td>@AAP{content_types,roles-and-visibility,roles and visibility}</td>
-    <td>Boolean</td>
-    <td>No</td>
-    <td><var>FALSE</var><sup>1</sup></td>
-    <td>@LAP{content_types,t-visibility-control,visibility control}</td>
-    <td>If you want your content type to use both your custom visibility logic and Panels' built-in roles-based access system, then set this to <var>TRUE</var>. Setting 'role-based access' to <var>TRUE</var> is not sufficient; see panels_ajax_ct_preconfigure() to understand how this works. If you use both systems, panels_pane_access() will \c AND the results together when determining pane visibility.</td>
-  </tr>
-</table>
-\par
-<small>
-  <sup>1</sup>  Technically, this property doesn't have a default value. However, because the setting is checked using a call to empty(), it effectively has a default value of <var>FALSE</var>.\n
-  <sup>2</sup>  Only required if properties it depends upon have been set.\n
-  <sup>3</sup>  Note that this will automatically be set to <var>FALSE</var> if the @LAP{content_types,t-visibility-control,visibility control} property is set defined.\n
-  <sup>4</sup>  'Required' is a little misleading in this case. it means that Panels needs this property to be set, not that it must be set by the plugin. So, if the default value works for your plugin, you don't have to define this property.\n
-</small>\n\n
-
-@SSECplug{content_types,pdfunc,callprops,Overview of Callback Properties}
-
-<p>This table provides a basic summary for all thirteen @LGt{p-p-callback,callback properties}. Most of the properties also have detailed documentation, which can be reached by clicking the property name.</p>
-<p>Parameters that are passed by reference from the function side (through call_user_func_array()) are marked with the by-reference operator.</p>
-\attention
-apis may break. here's how they're likely to. be warned.
-\attention
-form control is the most subject to change.
-\note
-'required' and 'dependencies' have specific meanings.
-
-<table>
-  <tr>
-    <td>Property Name</td>
-    <td>Return Value Type</td>
-    <td>Required?</td>
-    <td>Dependencies</td>
-    <td>Parameters</td>
-    <td>Notes</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_content_types,content_types}@AAP{content_types,t-content_types, }</td>
-    <td>Array</td>
-    <td>Yes</td>
-    <td>None</td>
-    <td></td>
-    <td>Panels calls this function to find out how many content types this plugin provides, as well as some basic 'gatekeeper' information about each of those content types. Most importantly, optional and required context(s) are defined in this function.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_render-callback,render callback}@AAP{content_types,t-render-callback, }</td>
-    <td>Object</td>
-    <td>Yes</td>
-    <td>None</td>
-    <td><var>$pane->configuration</var><sup>1</sup>, <var>$panel_args</var>, <var>$context</var></td>
-    <td>Panels calls this function while preparing a @LGt{display,display object} for viewing. The callback needs to construct and return an object, which is passed along to the @LAP{styles,Style} and @LAP{layouts,Layout} plugins for handling.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_add-callback,add callback}@AAP{content_types,t-add-callback, }</td>
-    <td>FAPI Array</td>
-    <td>No</td>
-    <td>None</td>
-    <td><var>$subtype</var>, <var>$parents</var>, <var>$pane->configuration</var><sup>1,2</sup></td>
-    <td>This function gets called when the user clicks an icon to add a new pane (from the 'Add Content' @LGt{modal,modal form}). note that it is often possible to use the same, or nearly the same, callback for this as for the @LAP{content_types,callbacks_edit-callback,edit callback}.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_edit-callback,edit callback}@AAP{content_types,t-edit-callback, }</td>
-    <td>FAPI Array</td>
-    <td>No</td>
-    <td>None</td>
-    <td><var>$subtype</var>, <var>$parents</var>, <var>$pane->configuration</var></td>
-    <td>This function gets called when the user clicks the 'Configure' button on an already-existing pane; it partially governs what appears on the resulting configuration modal.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_addedit-validate-callback,add/edit validate callback}@AAP{content_types,t-addedit-validate-callback, }</td>
-    <td></td>
-    <td>No</td>
-    <td>None<sup>3</sup></td>
-    <td><var>$form</var>, <var>$form_values</var></td>
-    <td>Defines a callback to be used as a FAPI validator, but only for the $form_values set by form items defined in the @LAP{content_types,callbacks_edit-callback,add/edit callback}.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_addedit-submit-callback,add/edit submit callback}@AAP{content_types,t-addedit-submit-callback, }</td>
-    <td>part of the <var>$pane->configuration</var> Array</td>
-    <td>No</td>
-    <td>None<sup>3</sup></td>
-    <td><var>$form_values</var></td>
-    <td>Defines a callback to be used as a FAPI submit handler, but only for the $form_values set by form items defined in the @LAP{content_types,callbacks_edit-callback,add/edit callback}.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_title-callback,title callback}@AAP{content_types,t-title-callback, }</td>
-    <td>String</td>
-    <td>Yes</td>
-    <td>None</td>
-    <td><var>$pane->configuration</var>, <var>$context</var></td>
-    <td>This function determines the title that the pane will use in the display content editor, and ONLY that title.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_editor-render-callback,editor render callback}@AAP{content_types,t-editor-render-callback, }</td>
-    <td>String</td>
-    <td>No</td>
-    <td>None</td>
-    <td><var>$display</var>, <var>$pane</var></td>
-    <td>This function determines the title that the pane will use in the display content editor, and ONLY that title.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_visibility-control,visibility control}@AAP{content_types,t-visibility-control, }</td>
-    <td>FAPI Array</td>
-    <td>No</td>
-    <td>None</td>
-    <td><var>$contexts</var>, <var>$subtype</var>, <var>$pane->configuration</var>, <var>$add</var></td>
-    <td>This callback is fired shortly after the add/edit callbacks. Use it to create a form widget form widget from which the user can select values that will make sense when passed to your @LAP{content_types,callbacks_visibility-check,visibility check callback}.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_visibility-submit,visibility submit}@AAP{content_types,t-visibility-submit, }</td>
-    <td>Mixed</td>
-    <td>No</td>
-    <td>@LAP{content_types,callbacks_visibility-control,visibility control}</td>
-    <td><var>$contexts</var>, <var>$subtype</var>, <var>$pane->configuration</var>, <var>$add</var></td>
-    <td>The custom submit handler for your content type's visibility settings. This function is passed the portion of the $form_values array that was generated from the widgets created by the @LAP{content_types,callbacks_visibility-control,visibility control} callback. Most plugins won't need to define this property, even if they define custom visibility control.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_visibility-check,visibility check}@AAP{content_types,t-visibility-check, }</td>
-    <td>Boolean</td>
-    <td>Yes<sup>4</sup></td>
-    <td>@LAP{content_types,callbacks_visibility-control,visibility control}</td>
-    <td><var>$contexts</var>, <var>$subtype</var>, <var>$pane->configuration</var>, <var>$add</var></td>
-    <td>Panels calls this function during the pane accessibility checking routine, which is handled by primarily by panels_pane_access(). Define the logic governing your content type's visibility here.</td>
-  </tr>
-  <tr>
-    <td>@LAP{content_types,callbacks_form-control,form control}@AAP{content_types,t-form-control, }</td>
-    <td>FAPI <var>$form</var> Array</td>
-    <td>No</td>
-    <td>None</td>
-    <td><var>&$form</var>, <var>&$pane</var>, <var>&$display</var></td>
-    <td>If the other callbacks governing the add/edit form (i.e., the @LAP{content_types,callbacks_edit-callback,add/edit callback} properties or the @LAP{content_types,callbacks_visibility-control,visibility control} property) aren't enough for your needs, then implement this callback. This function is passed virtually all of the Panels editing data by reference. Use with caution.</td>
-  </tr>
-</table>
-\par
-<small>
-  <sup>1</sup>  In the sample functions, the variable <var>$conf</var> is used. <var>$pane->configuration</var> is used here instead because that's the value being passed in, and it has meaning out of context, unlike <var>$conf</var>.\n
-  <sup>2</sup>  Because this is a new pane, its configuration is always going to be empty.\n
-  <sup>3</sup>  Even if you haven't defined the add/edit callback property, you can still define the validate/submit properties - Panels doesn't check. Of course, if you do so, your callback will be passed a great big <var>NULL</var>...\n
-  <sup>4</sup>  Only required if properties it depends upon have been set.\n
-</small>\n
-
-<hr>
-\n
-
-
-@SECplug{content_types,callbacks,Discussion and Samples of Callbacks}
-
-@SSECplug{content_types,callbacks,content_types,Callback Property: 'content_types'}
-Callback function set by the @LAP{content_types,t-content_types,content_types} property. Returns an array of data that Panels uses to determine:
-    - Whether or not the content type can be added to this display, based on
-      what context(s) are available.
-    - If context requirements are met, the remainder of the array's data
-      defines the icon, title, and description that the content type will
-      be rendered with on the the Add Content @LGt{modal,modal}.\n\n
-
-@code
-function panels_admin_content_types_SAMPLE_CT() {
-  return array(
-    // As with the plugin declaration, the value of this array key is significant:
-    // it will become the pane's subtype, stored in $pane->subtype.
-    'content' => array(
-      // The name used for this subtype on the Add Content modal - this is what
-      // appears right below the icon.
-      'title' => t('SAMPLE CONTENT TYPE'),
-      // The name of the icon file to be used for this subtype.
-      'icon' => 'icon_node.png',
-      // The server path to the directory where the above icon is located.
-      'path' => panels_get_path('plugins/content_types/node'),
-      // The 'description' appears as a tooltip when the user hovers their
-      // mouse pointer over the icon.
-      'description' => t('Descriptive text for the SAMPLE CONTENT TYPE, to be used in the tooltip.'),
-      // This property indicates which contexts are prerequisites for the content
-      // type to be used. If a display lacks a context required by this content
-      // type, then it simply will not be displayed. Multiple required contexts
-      // can be declared by placing each context into an indexed array.
-      'required context' => new ctools_context_required(t('Sample Required Context'), 'sample_context_required'),
-      // This property has the same syntax as 'required context', but if optional
-      // context requirements are not met, the content type will still be usable,
-      // simply in a reduced form. It's up to the plugin author to define just how
-      // different that functionality by writing varying the behavior of this plugin's
-      // other callbacks according to the presence/absence of the context.
-      'optional context' => new ctools_context_optional(t('Sample Optional Context'), 'sample_context_optional'),
-      // Category is the group this subtype's icon will be placed in. The first
-      // item in the array is the category name, and the second is the subtype's
-      // weight in that category (used for ordering the subtypes in the category
-      // relative to one another). Omitting a value for weight will cause it to
-      // default to 0; if you do omit the weight, you can simply return the
-      // t()-wrapped string title of the content type - no need to put it in an array.
-      'category' => array(t('Node context'), -9),
-    ),
-  );
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,render-callback,Callback Property: 'render callback'}
-Callback function set by the @LAP{content_types,t-render-callback,render callback} property. This callback constructs and returns an object for display.
-
-The sample function below is a direct copy of the node_content plugin's render
-callback; abstract example cases are of little use from here on out. Note that
-this case only implements three parameters, but there is also a fourth. Your
-content type can use as few/many of these parameters as you want, although
-you won't be able to much if you don't implement the first parameter, $conf.
-
-@param array $conf
- The contents of $pane->configuration. This will be an array with the following
- keys, by default:
-   - override_title (int/bool): 0 or 1, reflecting whether the user checked the
-     'override title' checkbox on the pane configuration form.
-   - override_title_text (string): a string containing the title override, as
-     written by the user on the pane configuration form.
-   - css_id (string): the special css id entered by the user on the pane config
-     form, if any.
-   - css_class (string): same idea as the css id.
-   - module (string): a string containing the name of the module implementing
-     this content type (or, in some cases, owning/generating the content).\n
-The above keys reflect the standard set of form items that the Panels API
-provides to every pane type by default. Any additional configuration items that
-you add (via the add/edit callbacks) will also appear in $conf by default.
-@param array $panel_args
- An indexed array of all arguments, if any, that have been passed to the display.
-@param mixed $context
- The contents of $context can vary widely. If only one context is being passed
- to the pane, $context will simply be that context object. If multiple contexts
- are passed, however, then $context will be an indexed array of those contexts.
- The sort of data contained in the context is completely dependent on the how
- that context has been defined.
-@param $incoming_content
-
-@return object $block
- An object, ready to be passed through the styling & theming layers. At minimum,
- the object should contain a 'content' element, as well as 'title' and/or
- 'subject' elements. If a 'title' element is not included, then the 'subject'
- is copied into $block->title later on in the render process. You are free to
- define as many elements as you want, but those elements will only be used
- if you write a panels style plugin specifically designed to take advantage of
- of them. Note that the '$block' variable name used here is arbitrary.
-
-@code
-function panels_content_SAMPLE_CT($conf, $panel_args, $context) {
-  // The node_content content_type plugin has a required context of 'node.'
-  // This simply double-checks to make sure that the necessary context is present;
-  // in particular, it excludes 'empty' contexts, which are used primarily during
-  // the edit process.
-  if (!empty($context) && empty($context->data)) {
-    return;
-  }
-
-  // The node context plugin stores an entire, fully-loaded $node object into
-  // its $context->data element; this pulls that node data out (via cloning, to
-  // ensure the original context data itself remains unchanged) and stores it in
-  // a correspondingly-named variable, $node.
-  $node = isset($context->data) ? drupal_clone($context->data) : NULL;
-  $block->module = 'node';
-  // Stores the nid from the context, to ensure it is acecssible later.
-  $block->delta = $node->nid;
-
-  // Just in case the context didn't load, but managed to get past the initial
-  // checks, this adds filler content to the $block.
-  if (empty($node)) {
-    $block->delta = 'placeholder';
-    $block->subject = t('Node title.');
-    $block->content = t('Node content goes here.');
-  }
-  else {
-    if (!empty($conf['identifier'])) {
-      $node->panel_identifier = $conf['identifier'];
-    }
-
-    $block->subject = $node->title;
-
-    unset($node->title);
-    // The pane's content is a complex enough operation that we delegate creating
-    // it to a helper function.
-    $block->content = panels_admin_SAMPLE_CT($node, $conf);
-  }
-
-  // If the user has the necessary permissions, an 'admin link' is generated.
-  // Admin links are the special links that appear above the pane's title when
-  // you mouse over the pane.
-  if (node_access('update', $node)) {
-    $block->admin_links['update'] = array(
-      'title' => t('Edit node'),
-      'alt' => t("Edit this node"),
-      'href' => "node/$node->nid/edit",
-      'query' => drupal_get_destination(),
-    );
-  }
-
-  if (!empty($conf['link']) && $node) {
-    $block->title_link = "node/$node->nid";
-  }
-
-  return $block;
-}
-@endcode
-
-Probably the most important thing to be noted about this helper function is
-just how similar it is to node.module's routine for node rendering.
-In fact, it's little more than a minor rewrite of
-<a href="http://api.drupal.org/api/function/node_view/5">node_view()</a>; the first lines are lifted directly from <a href="http://api.drupal.org/api/function/node_build_content/5">node_build_content()</a>,
-and the latter half from node_view().
-
-@code
-function panels_admin_SAMPLE_CT($node, $conf) {
-  // Remove the delimiter (if any) that separates the teaser from the body.
-  $node->body = str_replace('<!--break-->', '', $node->body);
-
-  // The 'view' hook can be implemented to overwrite the default function
-  // to display nodes.
-  if (node_hook($node, 'view')) {
-    $node = node_invoke($node, 'view', $conf['teaser'], $conf['page']);
-  }
-  else {
-    $node = node_prepare($node, $conf['teaser']);
-  }
-
-  if (empty($conf['no_extras'])) {
-  // Allow modules to make their own additions to the node.
-    node_invoke_nodeapi($node, 'view', $conf['teaser'], $conf['page']);
-  }
-
-  if ($conf['links']) {
-    $node->links = module_invoke_all('link', 'node', $node, $conf['teaser']);
-
-    foreach (module_implements('link_alter') AS $module) {
-      $function = $module .'_link_alter';
-      $function($node, $node->links);
-    }
-  }
-
-  // Set the proper node part, then unset unused $node part so that a bad
-  // theme can not open a security hole.
-  $content = drupal_render($node->content);
-  if ($conf['teaser']) {
-    $node->teaser = $content;
-    unset($node->body);
-  }
-  else {
-    $node->body = $content;
-    unset($node->teaser);
-  }
-
-  // Allow modules to modify the fully-built node.
-  node_invoke_nodeapi($node, 'alter', $conf['teaser'], $conf['page']);
-
-  return theme('node', $node, $conf['teaser'], $conf['page']);
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,add-callback,Callback Property: 'add callback'}
-Callback function set by the @LAP{content_types,t-add-callback,add callback} property. This callback constructs
-the pane configuration form for newly-added panes. This sample is lifted from
-the block content type plugin (block.inc); it is the only built-in Panels content
-type that implements an add callback that is different from the edit callback.
-
-Clearly there's relatively little need to differentiate between the add and edit
-callbacks; the only thing this one does is make sure that $conf has some of the
-right values before heading into the edit form. You still need to define both the
-'add callback' and 'edit callback' properties in the plugin declaration array, but you
-can just make them point to the same function.
-
-See the edit callback for more detailed discussion. \n
-
-@code
-function panels_admin_add_SAMPLE_CT($id, $parents, $conf = array()) {
-  list($conf['module'], $conf['delta']) = explode('-', $id, 2);
-  return panels_admin_edit_SAMPLE_CT($id, $parents, $conf);
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,edit-callback,Callback Property: 'edit callback'}
-Callback function set by the @LAP{content_types,t-edit-callback,edit callback} property in the plugin declaration array.
-This callback constructs the configuration form for panes that have already been added;
-the callback is fired when the 'Configure' button is clicked.
-
-This function essentially operates like a limited and targeted implementation of
-<a href="http://api.drupal.org/api/function/hook_form_alter/5">hook_form_alter()</a>; the Panels API wrangles FAPI as needed, so all you need to do
-is add the widgets you want for your content type/subtype.
-
-\note
-In future versions, the 'Block visibility' properties are likely to be moved into
-the appropriate visibility callbacks. They're here now because the block content type
-plugin was written long before the visibility system was introduced.
-
-Some of the techniques used in this edit callback are pretty advanced. For a more basic
-but quite thorough implementation of this callback, see panels_admin_edit_node_content().
-
-@param string $id
- The subtype of the pane being edited. The block panels content type plugin calls this
- variable '$id' for legacy reasons; we recommend you call this variable $subtype if you
- want your variable names to be optimally descriptive of their values.
-@param array $parents
- This parameter is largely deprecated, and is included for legacy API compatibility. Its
- intention was to provide information to form widgets about where they live on the $form.
- It is likely to disappear in Panels3. For all add/edit callbacks:
- @code $parents = array('configuration'); @endcode
- This corresponds to the fact that the $form returned from this callback will not be added
- to the root of the overall $form array, but to the $form['configuration'] sub-array.
- See panels_content_config_form().
-@param array $conf
- The contents of $pane->configuration, if any.
-@return array $form
- A standard FAPI form array.
-
-
-@code
-function panels_admin_edit_SAMPLE_CT($id, $parents, $conf) {
-  $form['module'] = array(
-    '#type' => 'value',
-    '#value' => $conf['module'],
-  );
-  $form['delta'] = array(
-    '#type' => 'value',
-    '#value' => $conf['delta'],
-  );
-
-  if (user_access('administer advanced pane settings')) {
-    $form['block_visibility'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Use block visibility settings (see block config)'),
-      '#default_value' => $conf['block_visibility'],
-      '#description' => t('If checked, the block visibility settings for this block will apply to this block.'),
-    );
-    // Module-specific block configurations.
-    if ($settings = module_invoke($conf['module'], 'block', 'configure', $conf['delta'])) {
-      // Specifically modify a couple of core block forms.
-      if ($conf['module'] == 'block') {
-        unset($settings['submit']);
-        $settings['info']['#type'] = 'value';
-        $settings['info']['#value'] = $settings['info']['#default_value'];
-      }
-      panels_admin_fix_block_tree($settings);
-      $form['block_settings'] = array(
-        '#type' => 'fieldset',
-        '#title' => t('Block settings'),
-        '#description' => t('Settings in this section are global and are for all blocks of this type, anywhere in the system.'),
-        '#tree' => FALSE,
-      );
-
-
-      $form['block_settings'] += $settings;
-    }
-  }
-
-  return $form;
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,addedit-submit-callback,Callback Property: 'add/edit submit callback'}
-Callback function set by the @LAP{content_types,t-addedit-submit-callback,edit callback}'[add|edit] submit callback' property in the plugin
-declaration array. In simpler plugins, you won't need to implement this callback;
-any values set by the widgets you added to the config form will automatically
-have their data added to the $pane->configuration array, which is serialized
-and stored in the panels_pane table.
-
-However, in cases where the settings being changed on this form need to be
-reflected in some other data structure, this callback can be used to ensure
-that the necessary changes are made. In this example (again from the block
-content type plugin), hook_block() is invoked with $op = 'save' for the module
-that owns the block, thereby allowing the normal block saving routine to
-do its thing.
-
-
-@code
-function panels_admin_submit_SAMPLE_CT(&$form_values) {
-  if (!empty($form_values['block_settings'])) {
-    module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values['block_settings']);
-  }
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,title-callback,Callback Property: 'title callback'}
-Callback function set by the @LAP{content_types,t-title-callback,title callback} property in the plugin definition array.
-
-Returns the title to be used in the display editor ONLY. When the pane is
-rendered for viewing, the value of $obj->title or $obj->subject, as returned
-from the callback defined in the 'render callback' property, will become the
-pane's title. The only way the value returned from here will show up as the
-pane's title upon viewing is if this callback is explicitly called from the
-render callback itself (i.e., @code $obj->title = panels_admin_title_SAMPLE_CT($conf, $context); @endcode)
-
-@param array $conf
- The contents of $pane->configuration, if any.
-@param mixed $context
- The contents of $context can vary; see other sample callback parameters for details.
-@return string $title
- The title to use for the pane in the display editor. Make sure to run it through t(), first.
-
-
-@code
-function panels_admin_title_SAMPLE_CT($conf, $context) {
-  return t('"@s" content', array('@s' => $context->identifier));
-}
-@endcode
-
-@SSECplug{content_types,callbacks,editor-render-callback,Callback Property: 'editor render callback'}
-Callback function set by the @LAP{content_types,t-editor-render-callback,editor render callback} property in the plugin definition array.
-
-Returns object used to populate the content area of the pane in the display
-editor ONLY.
-
-@param object $display
- The full display object being edited.
-@param object $pane
- The pane object being rendered for display editing.
-
-@return object
- The object containing the data to use when rendering the pane. The two fields in the object that are used for rendering are @code $obj->title @endcode, which is used as the title on the collapsible fieldset, and @code $obj->content @endcode, which is the contents of fieldset (if any).
-
-@code
-function panels_admin_pane_render_SAMPLE_CT($display, $pane) {
-  // Pretend your content type stores a node id in the $configuration array
-  // and that you want the title of that node as the fieldset title, and the
-  // teaser for that node as the content.
-  $node = node_load($pane->configuration['nid']);
-  $block = new stdClass();
-  $block->title = check_plain($node->title);
-  $block->content = node_view($node, TRUE);
-  return $block;
-}
-@endcode
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,visibility-control,Callback Property: 'visibility control'}
-Callback function set by the @LAP{content_types,t-visibility-control,visibility control} property in the plugin declaration array.
-
-If your plugin defines this property, you'll need to be cognizant of your definitions for several other properties: @LAP{content_types,callbacks_visibility-submit,visibility submit}, @LAP{content_types,callbacks_visibility-check,visibility check} and @LAP{content_types,visibility-serialize,visibility serialize}. @LAP{content_types,roles-and-visibility,roles and visibility} is also relevant, but won't be useful to the vast majority of content type plugins.
-
-Operates quite similarly to the add and edit callbacks, with a few exceptions:
-   - The FAPI parent is $form['visibility'] instead of $form['configuration']
-   - Instead of creating separate callbacks for add and edit, the $add parameter
-     indicates which operation is taking place.
-   - Whereas a submit handler is often unnecessary for the add/edit callbacks,
-     implementing this callback property means you need to be cognizant of several
-     other properties in the definition array.
-The visibility widget defined in this function is a reworked version of one originally
-created for og_panels (but was not/has not yet been committed); the goal is to control
-pane visibility according to the status of the current user relative to the group.
-
-Remember, this is NOT where you define the logic behind your visibility handling -
-all you're doing here is providing a form widget that to get some data. It's up to
-your visibility checker, defined in the @LAP{content_types,callbacks_visibility-check,visibility check} callback, to create the
-logic that can take the data from here and make the right decision about pane
-visibility.
-
-If you define a more complex system that uses multiple widgets, make sure to return
-them all stacked inside a single array, and that you set the FAPI <var>tree</var> property to <var>TRUE</var> as needed.
-
-@param mixed $contexts
- As in the render callback, this is either a context object, or an array of context
- objects. It's unnecessary and probably unwise to include this context
- data directly in the values that get saved in your form visibility function; that
- very same data will be available via the $display variable that's passed to the
- checker. Rather, $context is provided in the event that your visibility widget
- needs to vary depending on some information in $context.
-@param string $subtype
- The contents of $pane->subtype for the pane currently being edited.
-@param array $conf
- The contents of $pane->configuration, if any.
-@param bool $add
- If TRUE, then a new pane is being added. If FALSE, then an existing pane is being edited.
-@return array $visibility_widget
- A standard FAPI widget, to be added to the form.
-
-
-@code
-function panels_admin_visibility_control_SAMPLE_CT($contexts, $subtype, $conf, $add) {
-  return $visibility_widget = array(
-    '#type' => 'radios',
-    '#title' => t('Pane Visibility'),
-    '#description' => t('Who should this pane be visible to?'),
-    '#options' => array(
-      'all' => t('Everyone'),
-      'member' => t('Only group members'),
-      'nonmember' => t('Only group non-members'),
-      'admin' => t('Group administrators'),
-      ),
-    '#default_value' => isset($conf['visibility']) ? $conf['visibility'] : 0,
-  );
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,visibility-check,Callback Property: 'visibility check'}
-Callback function set by the @LAP{content_types,t-visibility-check,visibility check} property in the plugin definition array.
-
-This function takes advantage of cached static variables to increase performance. On any
-given page request, we know that only ONE group is going to be accessed, and only ONE user
-is going to be doing the accessing. Since the static keyword only lasts through a single page
-request, and nid and uid are the two variables that visibility depends upon in this case,
-we only have to query the database and build the $visibility array once, no matter how many
-panes fire this callback to determine visibility during this page request.
-
-@param object $pane
- The fully-loaded $pane object that we're running the visibility check against. The
- value set by the widget defined in the @LAP{content_types,callbacks_visibility-control,visibility control} callback is contained
- in $pane->visibility.
-@param object $display
- The fully-loaded display object that's currently being rendered. If you need $context
- to figure out what action to take, you'll find it/them in $display->context.
-@param object $user
- The current $user - the same as what you'd get from <var>global $user</var>.
- Passed for convenience, since visibility is often dependent on the $user.
-@return bool
- A boolean indicating if the pane should (TRUE) or should not (FALSE) be visible.
-
-
-@code
-function panels_content_visibility_check_SAMPLE_CT($pane, $display, $user) {
-  // use static variable to somewhat reduce queries for complex og_panels pages
-  static $visible;
-  if (!is_array($visible)) {
-    $visible = array();
-    $visible['all'] = TRUE;
-  }
-  if (!isset($visible[$pane->visibility])) {
-    $members = array();
-    $sql = "SELECT u.uid AS uid, ogu.is_admin AS admin FROM {og_uid} ogu
-            INNER JOIN {users} u ON ogu.uid = u.uid WHERE ogu.nid = %d
-            AND ogu.is_active = 1 AND u.status = 1 ORDER BY ogu.created DESC";
-    // $display holds the context; the $data element of $context holds a node object,
-    // and we want the nid of that node.
-    $result = db_query($sql, $display->context->data->nid);
-    while ($account = db_fetch_array($result)) {
-      $members[$account['uid']] = $account['admin'];
-    }
-    $visible['member'] = in_array($user->uid, array_keys($members));
-    $visible['nonmember'] = !$visible['member'];
-    $visible['admin'] = $visible['member'] ? $members[$user->uid] : FALSE;
-  }
-  return $visible[$pane->visibility];
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,visibility-submit,Callback Property: 'visibility submit'}
-Callback function set by the @LAP{content_types,t-visibility-submit,visibility submit} property in the plugin definition array.
-This sample submit handler below is valid for the sample @LAP{content_types,callbacks_visibility-control,visibility control} callback. However, the submit handler itself is
-also completely superfluous - the Panels API's built-in handler is entirely
-adequate in this case. It is included purely as an excuse to discuss the
-parameters of this function, and the different ways in can work.
-
-Implement this if you need to wrangle that data before the Panels API's data storage routines kick in, or if the API's built-in routines are inadequate and you need to build a custom storage mechanism. See panels_content_config_form() and panels_content_config_form_submit() to grok the logic behind if/when/how this callback is triggered.
-
-Most use cases for the Panels visibility system will not need to implement a
-submit handler, as the built-in handler in panels_content_config_form_submit()
-is adequate for taking care of the data. However, if you need to manipulate
-the data generated by your form widget before it gets saved, or if you need
-to inform a custom external storage mechanism, hook, whatever, about the
-visibility setting, then you should do so here.
-
-Whatever you return from this callback will be saved as the value of
-<var>$pane->visibility</var>. If it is a data type that must be serialized before being
-put in the database (arrays and objects), then make sure to set the 'visibility
-serialize' property to TRUE for this content type.
-
-Note that in this particular sample implementation, the 'visibility serialize'
-property should be set to FALSE, as the value produced by the widget in the
-sample @LAP{content_types,callbacks_visibility-control,visibility control} function returns a simple string. Because 'visibility
-serialize' is FALSE by default, you can also simply not set the property.
-
-Note also that the visibility field in the panels_pane table is the standard
-'text' field. The save routines in panels_save_display() will always
-convert the value to a string, so bear in mind: even if you send an integer
-in, you'll get a string of that number back out on the other end.
-
-@param string $form_value_visibility
-  $form_values['visibility'], is the sole argument passed to this callback.
-@param bool $add
-  As in the @LAP{content_types,callbacks_visibility-control,visibility control} callback parameters: if TRUE, then we're
-  in the submit phase for adding a new pane. If <var>FALSE</var>, then we're in the submit phase of an existing pane.
-@param object $pane
-  The fully-loaded $pane object that we're submitting. Provided primarily for
-  content types with complex data storage needs that may be dependent on data
-  contained $pane object.
-@param object $display
-  The fully-loaded $display object that's currently being edited. As with $pane,
-  provided primarily for complex implementations that may need some of the data.
-@return mixed $form_value_visibility
-  This value will be assigned to $pane->visibility, and eventually saved to the
-  panel_pane table into the corresponding 'visibility' field.
-
-
-@code
-function panels_admin_visibility_submit_SAMPLE_CT($form_value_visibility, $add, $pane, $display) {
-  return $form_value_visibility;
-}
-@endcode
-
-
-<hr>
-
-
-@SSECplug{content_types,callbacks,form-control,Callback Property: 'form control'}
-Callback function set by the @LAP{content_types,t-form-control,form control} property in the plugin declaration array.
-
-This callback allows you full control over not only the $form array, but also the
-current $pane and $display objects; all three are passed by reference. Note that
-they will be passed by reference whether or not you prefix the parameters with
-the reference operator; it is included in the below parameters purely for
-explanatory purposes. See panels_content_config_form() for the context.
-
-The callback is fired after all other form operations have been performed
-immediately, before the fully-built $form is sent back to FAPI for handling.
-Changes you make to the $pane and $display objects will also be preserved, as
-a call to panels_cache_set() is made. The TRULY intrepid can extract the $cache
-object from the value property in <var>$form['vars']</var> and play with that.
-
-This property is provided primarily as a means of allowing significant control
-over the innerworkings of the Panels content editing API, without necessitating
-direct hacks of the API itself. Probably the most common use case is hiding
-some/all of the form widgets that the Panels API adds to all content forms -
-the sample function contains a technique for doing just that.
-
-CAUTION: This is an advanced, dangerous feature. Improper use of it can severely
-break your content type, and even has the potential to wreak havoc with your
-permanent Panels data. Be sure to test implementations of it thoroughly before
-putting them in a production environment.
-
-@param array $form
-  The all-but-complete FAPI $form array that generates the add/edit content
-  form.
-@param object $pane
-  The fully-loaded $pane object that's currently being edited.
-@param object $display
-  The fully-loaded $display object that's currently being edited.
-@param bool $add
-  As in the @LAP{content_types,callbacks_visibility-control,visibility control} callback parameters: if TRUE, then we're
-  in the submit phase for adding a new pane. If <var>FALSE</var>, then we're in the submit phase of an existing pane.
-
-
-@code
-function panels_admin_form_control_SAMPLE_CT(&$form, &$pane, &$display) {
-  // A technique for stripping all the Panels-provided form widgets from the add/edit form.
-  // We'll just replace them all with 'value'-type elements and store the default values.
-  foreach ($form['configuration'] as $element => $settings) {
-    // Get rid of the formatting elements, if they exist.
-    if (in_array($element, array('aligner_start', 'aligner_stop', 'override_title_markup'))) {
-      unset($form['configuration'][$element]);
-      continue;
-    }
-    // Skip form elements that aren't generated by the Panels API.
-    if (!in_array($element, array('override_title', 'override_title_text', 'css_id', 'css_class'))) {
-      continue;
-    }
-    // We're down to the four we want to get rid of. Now, let's pull out the default values
-    $defaults[$element] = $settings['#default_value'];
-    unset($form['configuration'][$element]);
-  }
-  reset($defaults);
-  while (list($element, $value) = each($defaults)) {
-    $form['configuration'][$element] = array(
-      '#type' => 'value',
-      '#value' => $value,
-    );
-  }
-  // Now, the (default) values will still be present, and the submit function will work normally
-}
-@endcode
-
-
- */
\ No newline at end of file
index e69de29..e0d2d4a 100644 (file)
@@ -0,0 +1,44 @@
+<p>Panels is a pluggable rendering engine. Apart from its datastructure, almost nothing in Panels is hardcoded; every level of its rendering process is controlled by plugins (of the <a href="topic:ctools/plugins">CTools</a> variety), and therefore can be overriden. Some of these plugins' behaviors are obvious from the UI, whereas others are much more under-the-hood.</p>
+<p>Most site builders and module authors will find that plugins are the only way they need to interact with Panels to do what they want. However, if you are building whole new UIs/applications that are to be driven by the Panels engine, you'll also need the more traditional API that manages Panels editing and CRUD. Panels Node and Mini Panels are examples of 'applications' in this sense, as is <a href="http://drupal.org/project/og_panels">OG Panels</a>.</p>
+<p>This page is divided into two sections, the first dealing with Panels' rendering system & the plugins the drive it, and the second with the requirements for creating a Panels-driven application/UI. The former is a good starting place and should be of interest to anyone wanting to begin working with the Panels API.</p>
+
+<h2>The Panels Rendering System</h2>
+<p>Panels' rendering system takes a fully-loaded panels_display object and turns it into HTML. It doesn't care how that object was built or where it comes from - it's just a rendering engine. So long as the calling code provides a well-formed panels_display object, the engine will spit out HTML. Exactly how that process works is determined entirely by plugins.</p>
+<p>Panels utilizes eight different types of plugins within its rendering system, four owned by Panels and four by CTools. Let's run through that list.</p>
+<dl>
+<dt><a href="topic:panels/plugins-layout">Layout plugins</a></dt>
+<dd><em>Owned by Panels.</em></dd>
+<dd>Layout plugins provide the HTML output skeleton for any panel: they define a set of regions and (optionally) accompanying CSS/JS, very analogous to how themes define block regions. While layout plugins with complex logic are possible (the Flexible layout, for example), most layouts are nothing more than a region list, a tpl.php skeleton, and some CSS.</dd>
+<dt><a href="topic:panels/plugins-style">Style plugins</a></dt>
+<dd><em>Owned by Panels.</em></dd>
+<dd>Style plugins control the markup that wraps both individual panes and whole panel regions. A basic style plugin need not provide more than some basic CSS and a theme function or tpl.php file.</dd>
+<dt><a href="topic:ctools/plugins-style-bases">Style Bases</a></dt>
+<dd><em>Owned by CTools.</em></dd>
+<dd>Panels implements the 'style_bases' plugin type in order to interact with the Stylizer system, which technically lives in CTools, though true 'ownership' is a little blurred. Regardless, Panels uses this plugin to interact with Stylaizer in order to allow the creation of style plugins in the UI.</dd>
+<dd>This plugin is probably of interest only to module developers; site builders can safely skip it.</dd>
+<dt><a href="topic:ctools/context-context">Context plugins</a></dt>
+<dd><em>Owned by CTools.</em></dd>
+<dd>Context plugins are abstracted wrappers around bits of data - a node, user, or taxonomy term are easy examples. Panels uses context plugins so that it can work with all these different bits of data without caring about the details of the datastructure itself. Context can be a confusing concept, and you can learn more in the <a href="topic:ctools/context">CTools help</a>.</dd>
+<dd>Note that <a href="topic:ctools/context-arguments">argument</a> and <a href="topic:ctools/context-relationships">relationship</a> plugins are technically also used by Panels, but because they're really means for creating more context, they don't get separate entries in this list.</dd>
+<dt><a href="topic:ctools/plugins-content">Content Type plugins</a></dt>
+<dd><em>Owned by CTools: can utilize context.</em></dd>
+<dd>At the heart of the Panels rendering process, content type plugins define the rendering logic for every pane. Offering Panels a renderable chunk from your module means defining one of these. For those accustomed to the block system, they can be thought of as a more verbose block definition, contained neatly inside a single plugin.</dd>
+<dd>Content type plugins rely on context to provide them with source data, when necessary; this makes content plugins nice and portable.</dd>
+<dt><a href="topic:ctools/context-access">Access plugins</a></dt>
+<dd><em>Owned by CTools: can utilize context.</em></dd>
+<dd>Access plugins are essentially portable access checking logic, responsible for answering "Yes" or "No" given some contextual data. Panels uses these to define 'visibility rules' on individual panes. The other place these are commonly seen is in defining 'Selection rules' on Page Manager variants.</dd>
+<dd>If your module introduces new permissioning concepts or access logic (NOT a basic hook_perm() implementation), you may want to consider also encapsulating that logic in one or more of these plugins. For example, Organic Groups might implement an access plugin to allow/deny based on group membership, or the Date module might implement allow/deny logic based on request time.</dd>
+<dt><a href="topic:panels/plugins-cache">Cache plugins</a></dt>
+<dd><em>Owned by Panels; should utilize context.</em></dd>
+<dd>Panels cache plugins define caching strategies that can be applied to individual panes, or to whole displays. Caching plugins must deal with two basic issues: the cache backend to use for reading/writing, and TTL-managing strategies.</dd>
+<dd>TTL strategies can (and should) integrate heavily with the context, as that is where the real differential benefit of Panels-based caching is vs. standard core caching.</dd>
+<dt><a href="topic:panels/plugins-renderers">Display Renderers</a></dt>
+<dd><em>Owned by Panels: utilizes everything else.</em></dd>
+<dd>In the simplest terms, display renderers take a fully-loaded panels_display object and render it. They are very complex, powerful plugins - one could use them to cause Panels to bypass every single other plugin in this list. Fortunately, there is virtually no circumstance under which one would need to implement these plugins in the process of building a single site; only contrib developers working on highly complex use cases are likely to ever need them.</dd>
+<dd>In short, each display renderer plugin <strong>is</strong> a self-contained Panels rendering engine. If you want to grok the Panels rendering system, study these.</dd>
+</dl>
+
+
+<h2>Panels-based Applications</h2>
+<p>At its core, Panels is a rendering engine with no UI (though it does provide common elements for reuse in admin UIs). So if you're working with Panels via the web UI, it means you're working with a Panels application. The three that ship with this version of Panels are Mini Panels, Panel Nodes, and the integration with CTools' Page Manager.</p>
+<p>Panels also implements a task_handler plugin, which is owned by Page Manager in CTools. Task handlers aren't strictly a part of Panels' rendering system itself though (strictly speaking, they wrap it), so we won't cover them here.</p>
\ No newline at end of file
index 8ed4f6c..fb49c03 100644 (file)
@@ -2,45 +2,40 @@
 [advanced help settings]
 line break = TRUE
 
+[glossary]
+title = Panels glossary of terms
+weight = 100
+
 [about]
-title = What is Panels
+title = About Panels
 weight = -100
 
-[about-layouts]
-title = Selecting and modifying layouts
-parent = about
-
-[about-styles]
-title = Panel styles
-parent = about
-
-[about-cache]
-title = Using caching with Panels
-parent = about
-
-[about-context]
-title = Understanding contexts
-weight = 10
-parent = about
-
 [common-tasks]
 title = Accomplishing common tasks in Panels
 weight = -50
 
+[tutorials]
+title = Panels tutorials and how-tos
+weight = -30
+
 [tutorial-landing]
 title = Creating a simple landing page
+parent = tutorials
 
 [tutorial-node]
 title = Styling a node page
+parent = tutorials
 
 [tutorial-vocabulary]
 title = Styling a vocabulary
+parent = tutorials
 
 [apps]
-title = Panel applications
+title = Panels-based applications
+parent = api
 
 [panels-pages]
-title = Panel pages
+title = Panel pages, via Page Manager
 parent = apps
 
 [panels-mini]
@@ -51,21 +46,30 @@ parent = apps
 title = Panel nodes
 parent = apps
 
-[glossary]
-title = Panels glossary of terms
-
 [api]
-title = Panels API
-weight = 100
-
-[api-defaults]
-title = Creating default panel objects
-parent = api
+title = Working with the Panels API
+weight = 50
 
-[plugin-layout]
-title = Creating layout plugins
+[render-api]
+title = Panels plugins
 parent = api
 
-[plugin-style]
-title = Creating style plugins
-parent = api
\ No newline at end of file
+[plugins-layout]
+title = Layout plugins
+parent = render-api
+weight = -10
+
+[plugins-style]
+title = Style plugins
+parent = render-api
+weight = -7
+
+[plugins-cache]
+title = Cache plugins
+parent = render-api
+weight = -4
+
+[plugins-renderers]
+title = Display Renderer plugins
+parent = render-api
+weight = -1
diff --git a/help/plugins-layout.html b/help/plugins-layout.html
new file mode 100644 (file)
index 0000000..45051b2
--- /dev/null
@@ -0,0 +1,78 @@
+<h2>Getting Started:</h2>
+<p>Layout plugins are one of the simplest and most powerful sections of the panels api. There are two different ways that a layout can be implemented via panels.  Panels supports both module and theme implementations of panels.  The module implementation requires that hook_ctools_plugin_directory define the directory in which your layout plugins exist. (This same hook defines the directory for all panels plugins) Alternately, if you intend on implementing a layout in a theme this can be done primary through the theme's info file. The CTools help does a great job of actually explaining this portion of the API <a href="topic:ctools/plugins-implementing">ctools: plugins</a>.</p>
+
+<p>CTools explains even the layout hooks a little in its example, but we'll recap quickly and expand on this information. As ctools explains, the actual plugin file must be named with care as it will directly affect your naming scheme for the hook within it. This is really no different from any other hook within drupal except that we'll be using multiple replacements in this case. The function we're looking to implement is an instance of:
+<code>function YOURMODULE_PLUGINNAME_OWNERMODULE_PLUGINTYPE()</code>
+In our case we already know that the function will be:
+<code>function YOURMODULE_PLUGINNAME_panels_layouts()</code>
+This is because the plugin type we're working with is a layout, and the module that implements these layouts is the panels module. For the rest of the naming scheme "YOURMODULE" will be replaced with either the name of your module that implements this layout, or the name of the theme, and "PLUGINNAME" will be replaced with whatever the name of the plugin file is. For purposes of this example our module name us going to be "layout_sample" and our plugin will be "first_layout".</p>
+
+<h2>Directory Structure:</h2>
+<p>We're going to assume that you've laid your directory structure out very similarly to how panels does it. Something like this is rather likely:
+<pre>layout_sample
+  layout_sample.info
+  layout_sample.module
+  plugins
+    layouts
+      first_layout
+        first_layout.css
+        first_layout.inc
+        first_layout.png
+        layout-sample-first-layout.tpl.php</pre>
+The name of our .inc file is going to be the key to the entire layout plugin.</p>
+
+<h2>The .inc File:</h2>
+<p>We will start with the first_layout.inc file as it's the most important file we're dealing with here. First_layout.inc will look similar to the following:
+<pre>
+  $plugin  = array(
+    'title' => t('First Layout'),
+    'icon' => 'first_layout.png',
+    'theme' => 'layout_sample_first_layout',
+    'css' => 'first_layout.css',
+    'panels' => array(
+      'main' => t('Main region'),
+      'right' => t('Right region'),
+    ),
+  );
+</pre>
+The include file defines all the other files that our layout will utilize in order to be truly useful.  The array is fairly self explanitory but for the sake of specificity:
+<ol>
+<li><strong>Title:</strong><br />The title of our layout. (Utilized within the panels administration screens)</li>
+<li><strong>Icon:</strong><br />The graphical representation of our layout. (Utilized within the panels administration screens)</li>
+<li><strong>Theme:</strong><br />The template file of our layout. (Sharp eyed readers will note that the theme definition utilizes underscores instead of dashes, and does not have ".tpl.php" after it.  This is refering to the layout-sample-first-layout.tpl.php file all the same, it is simply how the naming convention works.  Utilize dashes in the tpl file name and underscores when refering to it in your include file.)</li>
+<li><strong>CSS:</strong><br />The css file to be utilized for our layout. (Utilized within the panels administration screens, AND when viewing the actual panel itself.)</li>
+<li><strong>Panels:</strong><br />Defines all the various regions within your panel.  This will be further utilized within our tpl.php file.</li>
+</ol>
+There are many additional properties that can be added to the include file. For purposes of this document we'll also make mention of the 'admin css' property. 'Admin css' is especially useful when utilizing a fixed width layout with fixed with panel regions.  This can break under most administrative circumstances, and panels provides you with the ability to give an additional css layout for the administrative section. It's a simple nicety and looks like this:
+<pre>
+  $plugin = array(
+    'title' => t('First Layout'),
+    'icon' => 'first_layout.png',
+    'theme' => 'layout_sample_first_layout',
+    'css' => 'first_layout.css',
+    'admin css' => 'first_layout_admin.css',
+    'panels' => array(
+      'main' => t('Main region'),
+      'right' => t('Right region'),
+    ),
+  );
+</pre>
+</p>
+
+<h2>The tpl.php File:</h2>
+<p>The tpl.php file is very similar to any other template file within drupal. The difference here is that we're being passed an array of regions through $content, and we also have a css id available to us for the entire panel in the form of $css_id.  The template is very straight forward and will look similar to the following:
+<pre>&lt;div class="panel-display panel-stacked-twothirds-onethird clear-block" &lt;?php if (!empty($css_id)) { print "id=\"$css_id\""; } ?&gt;&gt;  
+  &lt;div class="panel-panel panel-col-first panel-region-main"&gt;
+    &lt;div class="inside"&gt;&lt;?php print $content['main']; ?&gt;&lt;/div&gt;
+  &lt;/div&gt;
+
+  &lt;div class="panel-panel panel-col-last panel-region-right"&gt;
+    &lt;div class="inside"&gt;&lt;?php print $content['right']; ?&gt;&lt;/div&gt;
+  &lt;/div&gt;
+&lt;/div&gt;
+</pre>
+This is simply an example of what the html could look like. You can alter an update this html to fit your own needs.
+</p>
+
+<h2>The Other Files:</h2>
+<p>The css and png files are as simple as any other css or png file you've ever utilized. Panels provides some images for its graphical representations of its layouts. I would heavily encourage you to modify these to suit your needs.  The CSS files (admin and non) will be included at the appropriate times. Simply set them up to fit your purposes. If you're utilizing fixed width panel regions it's probably smart to provide an admin css file as well with your panel layout.</p>
index a42e339..318eadc 100644 (file)
@@ -12,98 +12,146 @@ function panels_admin_page() {
   return theme('panels_dashboard');
 }
 
-function template_preprocess_panels_dashboard(&$vars) {
-  drupal_add_css(panels_get_path('css/panels-dashboard.css'));
-  $vars['image_path'] = url(panels_get_path('images'));
-
-  $vars['panel_page_description'] = t('Panel pages can be used as landing pages. They have a URL path, accept arguments and can have menu entries.');
-  $vars['panel_override_description'] = t('Customize an already existing page within your site.');
-  if (module_exists('page_manager')) {
-    $vars['new_panel_page'] = l(t('Panel page'), 'admin/build/pages/add');
+function panels_dashboard_final_blocks(&$vars) {
+  // Add in links for missing modules that we still want to mention:
+  if (empty($vars['links']['page_manager'])) {
+    $vars['links']['page_manager'] = array(
+      'weight' => -100,
+      'title' => t('Panel page'),
+      'description' => '<em>' . t('You must activate the page manager module for this functionality.') . '</em>',
+    );
   }
-  else {
-    $vars['new_panel_page'] = '<b>' . t('Panel page') . '</b><br /><em>' . t('You must activate the page manager module for this functionality.') . '</em>';
-    $vars['new_panel_override'] = '<em>' . t('You must activate the page manager module for this functionality.') . '</em>';
+  if (empty($vars['links']['panels_mini'])) {
+    $vars['links']['panels_mini'] = array(
+      'title' => t('Mini panel'),
+      'description' => '<em>' . t('You must activate the Mini panels module for this functionality.') . '</em>',
+    );
   }
-
-  $vars['panel_node_description'] = t('Panel nodes are node content and appear in your searches, but are more limited than panel pages.');
-  if (module_exists('panels_node')) {
-    $vars['new_panel_node'] = l(t('Panel node'), 'node/add/panel');
+  if (empty($vars['links']['panels_node'])) {
+    $vars['links']['panels_mini'] = array(
+      'title' => t('Panel node'),
+      'description' => '<em>' . t('You must activate the panel node module for this functionality.') . '</em>',
+    );
   }
-  else {
-    $vars['new_panel_node'] = '<b>' . t('Panel node') . '</b><br /><em>' . t('You must activate the panel node module for this functionality.') . '</em>';
+}
+
+/**
+ * Implementation of hook_panels_dashboard_blocks().
+ *
+ * Adds page information to the Panels dashboard.
+ */
+function panels_panels_dashboard_blocks(&$vars) {
+  $vars['links']['panels_layout'] = array(
+    'title' => l(t('Custom layout'), 'admin/build/panels/layouts/add'),
+    'description' => t('Custom layouts can add more, site-specific layouts that you can use in your panels.'),
+  );
+
+   // Load all mini panels and their displays.
+  ctools_include('export');
+  $items = ctools_export_crud_load_all('panels_layout');
+  $count = 0;
+  $rows = array();
+
+  foreach ($items as $item) {
+    $rows[] = array(
+      check_plain($item->admin_title),
+      array(
+        'data' => l(t('Edit'), "admin/build/panels/layouts/list/$item->name/edit"),
+        'class' => 'links',
+      ),
+    );
+
+    // Only show 10.
+    if (++$count >= 10) {
+      break;
+    }
   }
 
-  $vars['panel_mini_description'] = t('Mini panels are small content areas exposed as blocks, for when you need to have complex block layouts or layouts within layouts.');
-  if (module_exists('panels_mini')) {
-    $vars['new_panel_mini'] = l(t('Mini panel'), 'admin/build/panel-mini/add');
+  if ($rows) {
+    $content = theme('table', array(), $rows, array('class' => 'panels-manage'));
   }
   else {
-    $vars['new_panel_mini'] = '<b>' . t('Mini panel') . '</b><br /><em>' . t('You must activate the Mini panels module for this functionality.') . '</em>';
+    $content = '<p>' . t('There are no custom layouts.') . '</p>';
+  }
+
+  $vars['blocks']['panels_layout'] = array(
+    'title' => t('Manage custom layouts'),
+    'link' => l(t('Go to list'), 'admin/build/panels/layouts'),
+    'content' => $content,
+    'class' => 'dashboard-layouts',
+    'section' => 'right',
+  );
+}
+
+function template_preprocess_panels_dashboard(&$vars) {
+  ctools_add_css('panels-dashboard', 'panels');
+  ctools_include('plugins');
+
+  $vars['image_path'] = ctools_image_path('', 'panels');
+
+  $vars['links'] = array();
+  $vars['blocks'] = array();
+
+  foreach (module_implements('panels_dashboard_blocks') as $module) {
+    $function = $module . '_panels_dashboard_blocks';
+    $function($vars);
   }
 
+  // Add in any default links for modules that are not active
+  panels_dashboard_final_blocks($vars);
+
+  // If page manager module is enabled, add a very low eight block to
+  // list the page wizards.
   if (module_exists('page_manager')) {
-    module_load_include('inc', 'page_manager', 'page_manager.admin');
-    $tasks = page_manager_get_tasks_by_type('page');
-    $pages = array('operations' => array());
-
-    page_manager_get_pages($tasks, $pages);
-    $count = 0;
-    $rows = array();
-    foreach ($pages['rows'] as $id => $info) {
-      $rows[] = array(
-        'data' => array(
-          $info['data']['title'],
-          $info['data']['operations'],
-        ),
-        'class' => $info['class'],
-      );
-
-      // Only show 10.
-      if (++$count >= 10) {
-        break;
+    $vars['blocks']['wizards'] = array(
+      'weight' => -101,
+      'section' => 'right',
+      'title' => t('Page wizards'),
+      'content' => '',
+      'class' => 'dashboard-wizards',
+    );
+
+    ctools_include('page-wizard');
+    $plugins = page_manager_get_page_wizards();
+    uasort($plugins, 'ctools_plugin_sort');
+
+    foreach ($plugins as $id => $plugin) {
+      if (isset($plugin['type']) && $plugin['type'] == 'panels') {
+        $link = array(
+          'title' => l($plugin['title'], 'admin/build/pages/wizard/' . $id),
+          'description' => $plugin['description'],
+        );
+
+        $vars['blocks']['wizards']['content'] .= theme('panels_dashboard_link', $link);
       }
     }
 
-    $vars['pages'] = theme('table', array(), $rows, array('class' => 'panels-manage'));
-    $vars['pagelink'] = l(t('Go to list'), 'admin/build/pages');
-  }
-  else {
-    $vars['pages'] = '<p>' . t('You must install and activate the Page Manager module to manage pages. This module is located within the Chaos Tools Suite.') . '</p>';
   }
 
-  if (module_exists('panels_mini')) {
-    // Load all mini panels and their displays.
-    $panel_minis = panels_mini_load_all();
-    $count = 0;
-    $rows = array();
-
-    foreach ($panel_minis as $panel_mini) {
-      $rows[] = array(
-        check_plain($panel_mini->title),
-        array(
-          'data' => l(t('Edit'), "admin/build/panel-mini/$panel_mini->name/edit-general"),
-          'class' => 'page-manager-page-operations',
-        ),
-      );
-
-      // Only show 10.
-      if (++$count >= 10) {
-        break;
-      }
-    }
+  uasort($vars['links'], 'ctools_plugin_sort');
 
-    if ($rows) {
-      $vars['minis'] = theme('table', array(), $rows, array('class' => 'panels-manage'));
-    }
-    else {
-      $vars['minis'] = '<p>' . t('There are no mini panels.') . '</p>';
-    }
-    $vars['minilink'] = l(t('Go to list'), 'admin/build/panel-mini');
+  $vars['blocks']['links'] = array(
+    'weight' => -100,
+    'section' => 'left',
+    'title' => t('Create new') . '...',
+    'content' => '',
+    'class' => 'dashboard-create',
+  );
+
+  // Turn the links into a block
+  foreach ($vars['links'] as $link) {
+    $vars['blocks']['links']['content'] .= theme('panels_dashboard_link', $link);
   }
-  else {
-    $vars['minis'] = '<p>' . t('You must install and activate the Mini Panels module to manage them.') . '</p>';
-    $vars['minilink'] = '';
+
+  uasort($vars['blocks'], 'ctools_plugin_sort');
+
+  $vars['left'] = '';
+  $vars['right'] = '';
+
+  // Render all the blocks
+  foreach ($vars['blocks'] as $block) {
+    $section = !empty($block['section']) ? $block['section'] : 'left';
+    $vars[$section] .= theme('panels_dashboard_block', $block);
   }
 }
 
@@ -135,6 +183,7 @@ function panels_admin_settings_page() {
  * Settings for panel contexts created by the page manager.
  */
 function panels_admin_panel_context_page() {
-  panels_load_include('common');
+  ctools_include('common', 'panels');
   return drupal_get_form('panels_common_settings', 'panels_page');
 }
+
index 53de582..867a884 100644 (file)
@@ -108,7 +108,7 @@ class panels_allowed_layouts {
    *
    * \n @code
    *  function docdemo_allowed_layouts() {
-   *    panels_load_include('common');
+   *    ctools_include('common', 'panels');
    *    if (!is_a($allowed_layouts = unserialize(variable_get('panels_common_allowed_layouts', serialize(''))), 'panels_allowed_layouts')) {
    *     $allowed_layouts = new panels_allowed_layouts();
    *      $allowed_layouts->allow_new = TRUE;
@@ -213,7 +213,7 @@ class panels_allowed_layouts {
   function list_layouts() {
     static $layouts = array();
     if (empty($layouts)) {
-      panels_load_include('plugins');
+      ctools_include('plugins', 'panels');
       $layouts = array_keys(panels_get_layouts());
     }
     return $layouts;
@@ -225,11 +225,11 @@ class panels_allowed_layouts {
  * any modules that don't already have special requirements.
  */
 function panels_common_settings(&$form_state, $module_name = 'panels_common') {
-  panels_load_include('plugins');
+  ctools_include('plugins', 'panels');
   ctools_include('content');
   $content_types = ctools_get_content_types();
   $skip = FALSE;
-  
+
   $default_types = variable_get($module_name . '_default', NULL);
   if (!isset($default_types)) {
     $default_types = array('other' => TRUE);
@@ -295,6 +295,7 @@ function panels_common_settings(&$form_state, $module_name = 'panels_common') {
           '#title' => t('Allowed @s content', array('@s' => $info['title'])),
           '#options' => $options[$id],
           '#default_value' => array_keys(array_filter($allowed[$id])),
+          '#checkall' => TRUE,
         );
       }
     }
@@ -312,7 +313,7 @@ function panels_common_settings(&$form_state, $module_name = 'panels_common') {
     '#value' => t('Save'),
   );
 
-  drupal_add_css(panels_get_path('css/panels_page.css'));
+  ctools_add_css('panels_page', 'panels');
   return $form;
 }
 
@@ -390,7 +391,7 @@ function panels_common_allowed_layouts_form(&$form, &$form_state, $module_name)
 
   $form_state['allowed_layouts'] = &$allowed_layouts;
 
-  drupal_add_js(panels_get_path('js/layout.js'));
+  ctools_add_js('layout', 'panels');
   $form['layouts'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Select allowed layouts'),
@@ -399,6 +400,7 @@ function panels_common_allowed_layouts_form(&$form, &$form_state, $module_name)
     '#default_value' => array_keys(array_filter($allowed_layouts->allowed_layout_settings)),
     '#prefix' => '<div class="clear-block panels-layouts-checkboxes">',
     '#suffix' => '</div>',
+    '#checkall' => TRUE,
   );
 
   return $form;
@@ -456,13 +458,21 @@ function panels_common_get_allowed_layouts($module_name) {
   }
 
   $allowed = array_filter($allowed_layouts->allowed_layout_settings);
-  $layouts = array();
-  foreach ($available_layouts as $layout_key => $layout_settings) {
-    if (!empty($allowed[$layout_key])) {
-      $layouts[$layout_key] = $layout_settings;
+  $order = array();
+  foreach ($available_layouts as $name => $plugin) {
+    if (!empty($allowed[$name])) {
+      $order[$name] = $plugin['category'] . ':' . $plugin['title'];
     }
   }
 
+  // Sort
+  $layouts = array();
+
+  asort($order);
+  foreach ($order as $name => $junk) {
+    $layouts[$name] = $available_layouts[$name];
+  }
+
   return $layouts;
 }
 
@@ -473,7 +483,7 @@ function panels_common_get_allowed_layouts($module_name) {
 function theme_panels_common_content_list($display) {
   $layout = panels_get_layout($display->layout);
   $content = '<dl class="content-list">';
-  foreach (panels_get_panels($layout, $display) as $panel_id => $title) {
+  foreach (panels_get_regions($layout, $display) as $panel_id => $title) {
     $content .= "<dt>$title</dt><dd>";
     if (!empty($display->panels[$panel_id])) {
       $content .= '<ol>';
@@ -490,3 +500,75 @@ function theme_panels_common_content_list($display) {
   $content .= '</dl>';
   return $content;
 }
+
+/**
+ * Print a selector of layouts, each linked to the next step.
+ *
+ * Most operations use radio buttons for selecting layouts, but some will
+ * give each layout as a link that goes to the next step. This function
+ * makes it easy to simply provide a list of allowed layouts and the base
+ * path.
+ *
+ * One limitation is that it will only append the layout name to the end, so
+ * if the actual layout name is needed in the middle, that can't happen.
+ *
+ * @return
+ *   The rendered output.
+ */
+function panels_common_print_layout_links($layouts, $base_path, $link_options = array()) {
+  $output = '';
+
+  $categories = array();
+  ctools_include('cleanstring');
+  foreach ($layouts as $id => $layout) {
+    $category = ctools_cleanstring($layout['category']);
+
+    $categories[$category] = $layout['category'];
+    $options[$category][$id] = panels_print_layout_link($id, $layout, $base_path . '/' . $id, $link_options);
+  }
+
+  $form = array();
+  $form['categories'] = array(
+    '#title' => t('Category'),
+    '#type' => 'select',
+    '#options' => $categories,
+    '#name' => 'categories',
+    '#id' => 'edit-categories',
+    '#value' => '',
+    '#parents' => array('categories'),
+  );
+
+  $output .= drupal_render($form);
+
+  $output .= '<div class="panels-choose-layout panels-layouts-checkboxes clear-block">';
+
+  // We're doing these dependencies completely manualy, which is unusual, but
+  // the process code only supports doing them in a form.
+  // @todo modify dependent.inc to make this easier.
+
+  $dependencies = array();
+  foreach ($options as $category => $links) {
+    $dependencies['panels-layout-category-' . $category] = array(
+      'values' => array('edit-categories' => array($category)),
+      'num' => 1,
+      'type' => 'hide',
+    );
+
+    $output .= '<div id="panels-layout-category-' . $category . '-wrapper">';
+    $output .= '<div id="panels-layout-category-' . $category . '" class="form-checkboxes clear-block">';
+    $output .= '<div class="panels-layouts-category">' . $categories[$category] . '</div>';
+
+    foreach ($links as $key => $link) {
+      $output .= $link;
+    }
+    $output .= '</div></div>';
+  }
+
+  $output .= '</div>';
+
+  ctools_add_js('dependent');
+  $js['CTools']['dependent'] = $dependencies;
+  drupal_add_js($js, 'setting');
+
+  return $output;
+}
index 0dff83d..bf9bf69 100644 (file)
@@ -8,6 +8,7 @@
  * as well as the ajax modal forms associated with them.
  */
 
+
 /**
  * Method to allow modules to provide their own caching mechanism for the
  * display editor.
@@ -55,47 +56,17 @@ function _panels_edit($display, $destination, $content_types, $title = FALSE) {
     $display = $cache->display;
   }
   else {
-    if (empty($content_types)) {
-      $content_types = ctools_content_get_available_types();
-    }
-
-    $display->cache_key = $did;
-    panels_cache_clear('display', $did);
-    $cache = new stdClass();
-    $cache->display = $display;
-    $cache->content_types = $content_types;
-    $cache->display_title = $title;
-    panels_edit_cache_set($cache);
+    $cache = panels_edit_cache_get_default($display, $content_types, $title);
   }
 
-  ctools_include('form');
-  $form_state = array(
-    'display' => &$display,
-    'content_types' => $cache->content_types,
-    're_render' => FALSE,
-    'no_redirect' => TRUE,
-    'display_title' => !empty($cache->display_title),
-    'cache key' => $did,
-  );
-
-  $output = ctools_build_form('panels_edit_display_form', $form_state);
-  // no output == submit
-  if (!$output) {
-    if (!empty($form_state['clicked_button']['#save-display'])) {
-      drupal_set_message(t('Panel content has been updated.'));
-      panels_save_display($display);
-    }
-    else {
-      drupal_set_message(t('Your changes have been discarded.'));
-    }
+  // Get a renderer.
+  $renderer = panels_get_renderer_handler('editor', $display);
+  $renderer->cache = $cache;
 
-    panels_cache_clear('display', $display->did);
-    if ($destination) {
-      return drupal_goto($destination);
-    }
-    return $form_state['display'];
+  $output = $renderer->edit();
+  if (is_object($output) && $destination) {
+    return panels_goto($destination);
   }
-
   return $output;
 }
 
@@ -112,28 +83,47 @@ function _panels_edit($display, $destination, $content_types, $title = FALSE) {
  */
 function panels_edit_display_form(&$form_state) {
   $display = &$form_state['display'];
+  $renderer = &$form_state['renderer'];
+
+  // Make sure there is a valid cache key.
   $cache_key = isset($display->cache_key) ? $display->cache_key : $display->did;
   $display->cache_key = $cache_key;
 
   // 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);
+  // The flexible layout maker wants to be able to edit a display without
+  // actually editing a display, so we provide this 'setting' to allow
+  // that to go away.
+  if (empty($form_state['no display settings'])) {
+    $links = $renderer->get_display_links();
+  }
+  else {
+    $links = '';
+  }
+  $form['hide']['display-settings'] = array(
+    '#value' => $links,
+  );
+
+  $form += panels_edit_display_settings_form($form_state);
 
   $form['panel'] = array('#tree' => TRUE);
   $form['panel']['pane'] = array('#tree' => TRUE);
 
-  foreach ($layout_panels as $panel_id => $title) {
+  $form['display'] = array(
+    '#value' => $renderer->render(),
+  );
+
+  foreach ($renderer->plugins['layout']['panels'] as $region_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();
+    if (!isset($display->panels[$region_id])) {
+      $display->panels[$region_id] = array();
     }
 
-    $form['panel']['pane'][$panel_id] = array(
+    $form['panel']['pane'][$region_id] = array(
       // Use 'hidden' instead of 'value' so the js can access it.
       '#type' => 'hidden',
-      '#default_value' => implode(',', (array) $display->panels[$panel_id]),
+      '#default_value' => implode(',', (array) $display->panels[$region_id]),
     );
   }
 
@@ -151,62 +141,14 @@ function panels_edit_display_form(&$form_state) {
     );
   }
 
-  $links = panels_edit_display_get_links($display);
-
-  $form['hide']['display-settings'] = array(
-    '#value' => $links,
-  );
-
-  ctools_include('ajax');
-  ctools_include('modal');
-  ctools_modal_add_js();
-
-  drupal_add_js(panels_get_path('js/panels-base.js'));
-  drupal_add_js(panels_get_path('js/display_editor.js'));
-  drupal_add_css(panels_get_path('css/panels_dnd.css'));
-  drupal_add_css(panels_get_path('css/panels_admin.css'));
-
-  $css = array();
-  $js = array(drupal_get_path('module', 'ctools') . '/js/dependent.js' => TRUE);
-  foreach ($form_state['content_types'] as $type_name => $subtypes) {
-    if (is_array($subtypes)) {
-      foreach ($subtypes as $subtype) {
-        if (isset($subtype['js'])) {
-          foreach ($subtype['js'] as $file) {
-            if (file_exists($file)) {
-              $js[$file] = TRUE;
-            }
-            else if (file_exists($subtype['path'] . '/' . $file)) {
-              $js[$subtype['path'] . '/' . $file] = TRUE;
-            }
-          }
-        }
-        if (isset($subtype['css'])) {
-          foreach ($subtype['css'] as $file) {
-            if (file_exists($file)) {
-              $css[$file] = TRUE;
-            }
-            else if (file_exists($subtype['path'] . '/' . $file)) {
-              $css[$subtype['path'] . '/' . $file] = TRUE;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  foreach (array_keys($js) as $file) {
-    drupal_add_js($file);
-  }
-  foreach (array_keys($css) as $file) {
-    drupal_add_css($file);
-  }
-
-  $form += panels_edit_display_settings_form($form_state);
-
   // Build up the preview portion of the form, if necessary.
   if (empty($form_state['no preview'])) {
-    $form['preview'] = array('#tree' => TRUE);
+    $form['preview'] = array(
+      '#tree' => TRUE,
+      '#prefix' => '<h2>' . t('Live preview') . '</h2>' . '<div id="panels-live-preview">',
+      '#suffix' => '</div>',
+    );
+
     ctools_context_replace_form($form['preview'], $display->context);
     $form['preview']['button'] = array(
       '#type' => 'submit',
@@ -221,139 +163,6 @@ function panels_edit_display_form(&$form_state) {
 }
 
 /**
- * Get the links for a panel display.
- *
- * This is abstracted out for easy ajax replacement.
- */
-function panels_edit_display_get_links(&$display) {
-  $cache_key = $display->cache_key;
-  $links = array();
-
-  $panel_settings = $display->panel_settings;
-  $style = panels_get_style((!empty($panel_settings['style'])) ? $panel_settings['style'] : 'default');
-
-  $links[] = array(
-    'title' => t('Display style: @style', array('@style' => $style['title'])),
-    'href' => 'panels/ajax/style-type/display/' . $cache_key,
-    'attributes' => array('class' => 'ctools-use-modal'),
-  );
-  if (panels_plugin_get_function('style', $style, 'settings form')) {
-    $links[] = array(
-      'title' => ' -- ' . t('Style settings'),
-      'href' => 'panels/ajax/style-settings/display/' . $cache_key,
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-  }
-
-  if (user_access('use panels caching features')) {
-    $method = isset($display->cache['method']) ? $display->cache['method'] : 0;
-    $info = panels_get_cache($method);
-    $cache_method = isset($info['title']) ? $info['title'] : t('No caching');
-    $links[] = array(
-      'title' => t('Cache method: @method', array('@method' => $cache_method)),
-      'href' => 'panels/ajax/cache-method/' . $cache_key . '/display',
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-    if (panels_plugin_get_function('cache', $info, 'settings form')) {
-      $links[] = array(
-        'title' => ' -- ' . t('Cache settings'),
-        'href' => 'panels/ajax/cache-settings/' . $cache_key . '/display',
-        'attributes' => array('class' => 'ctools-use-modal'),
-      );
-    }
-  }
-
-  return theme('ctools_dropdown', t('Display settings'), $links, FALSE, 'panels-display-links');
-}
-
-/**
- * Theme the edit display form.
- *
- * This has to do a fair bit of work since it's actually rendering the layout
- * as well as ensuring that all of the gadgets go in the right place.
- */
-function theme_panels_edit_display_form($form) {
-  $output = '';
-  $content = array();
-
-  $display = $form['#display'];
-  $cache_key = $display->cache_key;
-
-  $layout = panels_get_layout($display->layout);
-  $layout_panels = panels_get_panels($layout, $display);
-  $save_buttons = drupal_render($form['buttons']);
-
-  foreach ($layout_panels as $panel_id => $title) {
-    if (empty($content[$panel_id])) {
-      $content[$panel_id] = '';
-    }
-
-    foreach ((array) $display->panels[$panel_id] as $pid) {
-      $pane = $display->content[$pid];
-      $left_buttons = NULL;
-      $content[$pane->panel] .= panels_show_pane($display, $pane);
-    }
-
-    $panel_buttons = panels_edit_panel_get_links($display, $panel_id, $cache_key);
-
-    $content[$panel_id] = panels_render_region_dnd($content[$panel_id], $panel_id, $title, $panel_buttons);
-  }
-
-  $preview = '';
-  if (isset($form['preview'])) {
-    $preview .= '<h2>' . t('Live preview') . '</h2>';
-    $preview .= '<div id="panels-live-preview">';
-    $preview .= drupal_render($form['preview']);
-    $preview .= '</div>';
-  }
-
-  $output .= drupal_render($form);
-
-  panels_load_include('display-render');
-  $output .= panels_render_dnd(panels_render_layout_admin($layout, $content, $display));
-  $output .= $save_buttons;
-  $output .= $preview;
-
-  return $output;
-}
-
-/**
- * Get the links for a panel region.
- *
- * This is abstracted out for easy ajax replacement.
- */
-function panels_edit_panel_get_links($display, $panel_id) {
-  $cache_key = $display->cache_key;
-  $links = array();
-  $links[] = array(
-    'title' => t('Add content'),
-    'href' => "panels/ajax/add-pane/$cache_key/$panel_id",
-    'attributes' => array(
-      'class' => 'ctools-use-modal',
-    ),
-  );
-
-  $panel_settings = $display->panel_settings;
-  $style = panels_get_style((!empty($panel_settings[$panel_id]['style'])) ? $panel_settings[$panel_id]['style'] : '-1');
-  $style_title = isset($style['title']) ? $style['title'] : t('Default');
-
-  $links[] = array(
-    'title' => t('Region style: @style', array('@style' => $style_title)),
-    'href' => 'panels/ajax/style-type/panel/' . $cache_key . '/' . $panel_id,
-    'attributes' => array('class' => 'ctools-use-modal'),
-  );
-  if (panels_plugin_get_function('style', $style, 'settings form')) {
-    $links[] = array(
-      'title' => ' -- ' . t('Style settings'),
-      'href' => 'panels/ajax/style-settings/panel/' . $cache_key . '/' . $panel_id,
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-  }
-
-  return theme('ctools_dropdown', theme('image', panels_get_path("images/icon-addcontent.png")), $links, TRUE, 'pane-add-link panels-region-links-' . $panel_id);
-}
-
-/**
  * Handle form submission of the display content editor.
  *
  * This reads the location of the various panes from the form, which will
@@ -395,280 +204,23 @@ function panels_edit_display_form_preview(&$form, &$form_state) {
   ctools_include('ajax');
 
   $display->context = ctools_context_replace_placeholders($display->context, $form_state['values']['preview']);
+  $display->skip_cache = TRUE;
   $output = panels_render_display($display);
 
+  // Add any extra CSS that some layouts may have added specifically for this.
+  if (!empty($display->add_css)) {
+    $output = "<style type=\"text/css\">\n$display->add_css</style>\n" . $output;
+  }
+
   $commands = array();
   $commands[] = array(
     'command' => 'panel_preview',
     'output' => $output,
   );
-  ctools_ajax_render($commands);
-}
-
-/**
- * Render a single pane in the edit environment.
- *
- * @param $pane
- *   The pane to render.
- * @param $left_buttons
- *   Buttons that go on the left side of the pane.
- * @param $buttons
- *   Buttons that go on the right side of the pane.
- * @param $skin
- *   If true, provide the outside div. Used to provide an easy way to get
- *   just the innards for ajax replacement
- */
-// TODO check and see if $skin is ever FALSE; pane show/hide setting is dependent on it being TRUE. can't imagine it could be...
-function panels_show_pane($display, $pane, $skin = TRUE) {
-  $cache_key = $display->cache_key;
-  ctools_include('content');
-  $content_type = ctools_get_content_type($pane->type);
-
-  // This is just used for the title bar of the pane, not the content itself.
-  // If we know the content type, use the appropriate title for that type,
-  // otherwise, set the title using the content itself.
-  $title = ctools_content_admin_title($content_type, $pane->subtype, $pane->configuration, $display->context);
-  if (!$title) {
-    $title = t('Deleted/missing content type @type', array('@type' => $pane->type));
-  }
-  $links = array();
-
-  if (!empty($pane->shown)) {
-    $links[] = array(
-      'title' => t('Disable this pane'),
-      'href' => "panels/ajax/hide/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-ajax'),
-    );
-  }
-  else {
-    $links[] = array(
-      'title' => t('Enable this pane'),
-      'href' => "panels/ajax/show/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-ajax'),
-    );
-  }
-
-  if (isset($display->title_pane) && $display->title_pane == $pane->pid) {
-    $links['panels-set-title'] = array(
-      'title' => t('&#x2713;Panel title'),
-      'html' => TRUE,
-    );
-  }
-  else {
-    $links['panels-set-title'] = array(
-      'title' => t('Panel title'),
-      'href' => "panels/ajax/panel-title/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-ajax'),
-    );
-  }
-
-  if (isset($content_type['edit form'])) {
-    $links[] = array(
-      'title' => t('Settings'),
-      'href' => "panels/ajax/configure/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-  }
-
-  if (user_access('administer advanced pane settings')) {
-    $links[] = array(
-      'title' => t('CSS properties'),
-      'href' => "panels/ajax/pane-css/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-  }
-
-  $links[] = array(
-    'title' => '<hr />',
-    'html' => TRUE,
-  );
-
-  $style = panels_get_style((!empty($pane->style['style'])) ? $pane->style['style'] : 'default');
-
-  $style_links[] = array(
-    'title' => $style['title'],
-    'attributes' => array('class' => 'panels-text'),
-  );
-
-  $style_links[] = array(
-    'title' => t('Change'),
-    'href' => 'panels/ajax/style-type/pane/' . $cache_key . '/' . $pane->pid,
-    'attributes' => array('class' => 'ctools-use-modal'),
-  );
-
-  if (panels_plugin_get_function('style', $style, 'pane settings form')) {
-    $style_links[] = array(
-      'title' => t('Settings'),
-      'href' => 'panels/ajax/style-settings/pane/' . $cache_key . '/' . $pane->pid,
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-  }
-
-  $links[] = array(
-    'title' => '<span class="dropdown-header">' . t('Style') . '</span>' . theme_links($style_links),
-    'html' => TRUE,
-    'attributes' => array('class' => 'panels-sub-menu'),
-  );
-
-  if (user_access('administer pane access')) {
-    $links[] = array(
-      'title' => '<hr />',
-      'html' => TRUE,
-    );
-
-    $contexts = $display->context;
-    // Make sure we have the logged in user context
-    if (!isset($contexts['logged-in-user'])) {
-      $contexts['logged-in-user'] = ctools_access_get_loggedin_context();
-    }
-
-    $visibility_links = array();
-
-    if (!empty($pane->access['plugins'])) {
-      foreach ($pane->access['plugins'] as $id => $test) {
-        $plugin = ctools_get_access_plugin($test['name']);
-        $access_title  = isset($plugin['title']) ? $plugin['title'] : t('Broken/missing access plugin %plugin', array('%plugin' => $test['name']));
-        $access_description = ctools_access_summary($plugin, $contexts, $test);
-
-        $visibility_links[] = array(
-          'title' => $access_description,
-          'href' => "panels/ajax/access-test/$cache_key/$pane->pid/$id",
-          'attributes' => array('class' => 'ctools-use-modal panels-italic'),
-        );
-      }
-    }
-    if (empty($visibility_links)) {
-      $visibility_links[] = array(
-        'title' => t('No rules'),
-        'attributes' => array('class' => 'panels-text'),
-      );
-    }
-
-    $visibility_links[] = array(
-      'title' => t('Add new rule'),
-      'href' => "panels/ajax/access-add/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-
-    $visibility_links[] = array(
-      'title' => t('Settings'),
-      'href' => "panels/ajax/access-settings/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-
-    $links[] = array(
-      'title' => '<span class="dropdown-header">' . t('Visibility rules') . '</span>' . theme_links($visibility_links),
-      'html' => TRUE,
-      'attributes' => array('class' => 'panels-sub-menu'),
-    );
-  }
-
-  if (panels_get_caches() && user_access('use panels caching features')) {
-    $links[] = array(
-      'title' => '<hr />',
-      'html' => TRUE,
-    );
-
-    $method = isset($pane->cache['method']) ? $pane->cache['method'] : 0;
-    $info = panels_get_cache($method);
-    $cache_method = isset($info['title']) ? $info['title'] : t('No caching');
-    $cache_links[] = array(
-      'title' => $cache_method,
-      'attributes' => array('class' => 'panels-text'),
-    );
-    $cache_links[] = array(
-      'title' => t('Change'),
-      'href' => "panels/ajax/cache-method/$cache_key/$pane->pid",
-      'attributes' => array('class' => 'ctools-use-modal'),
-    );
-    if (panels_plugin_get_function('cache', $info, 'settings form')) {
-      $cache_links[] = array(
-        'title' => t('Settings'),
-        'href' => "panels/ajax/cache-settings/$cache_key/$pane->pid",
-        'attributes' => array('class' => 'ctools-use-modal'),
-      );
-    }
-
-    $links[] = array(
-      'title' => '<span class="dropdown-header">' . t('Caching') . '</span>' . theme_links($cache_links),
-      'html' => TRUE,
-      'attributes' => array('class' => 'panels-sub-menu'),
-    );
-  }
-
-  $links[] = array(
-    'title' => '<hr />',
-    'html' => TRUE,
-  );
-
-  $links[] = array(
-    'title' => t('Remove'),
-    'href' => '#',
-    'attributes' => array(
-      'class' => 'pane-delete',
-      'id' => "pane-delete-panel-pane-$pane->pid",
-    ),
-  );
-
-  $buttons = theme('ctools_dropdown', theme('image', panels_get_path("images/icon-configure.png")), $links, TRUE);
-  // Render administrative buttons for the pane.
-
-  $block = new stdClass();
-  if (empty($content_type)) {
-    $block->title = '<em>' . t('Missing content type') . '</em>';
-    $block->content = t('This pane\'s content type is either missing or has been deleted. This pane will not render.');
-  }
-  else {
-    $block = ctools_content_admin_info($content_type, $pane->subtype, $pane->configuration, $display->context);
-  }
-
-  $output = panels_render_pane_dnd($block, $pane->pid, $title, '', $buttons);
-  if ($skin) {
-    $class = 'panel-pane';
-
-    if (empty($pane->shown)) {
-      $class .= ' hidden-pane';
-    }
 
-    if (isset($display->title_pane) && $display->title_pane == $pane->pid) {
-      $class .= ' panel-pane-is-title';
-    }
-
-    $output = '<div class="' . $class . '" id="panel-pane-' . $pane->pid . '">' . $output . '</div>';
-  }
-  return $output;
+  ctools_ajax_render($commands);
 }
 
-/**
- * @defgroup panels_ajax Functions for panels ajax features
- * @{
- */
-
-/**
- * Entry point for AJAX: 'Display settings' modal form, from which the user
- * can control settings for the display.
- *
- * @param $cache
- *  The display id of the $display object currently being edited.
- */
-function panels_ajax_display_settings($cache) {
-  $form_state = array(
-    'display' => &$cache->display,
-    'display_title' => !empty($cache->display_title),
-    'title' => t('Display settings'),
-    'ajax' => TRUE,
-  );
-
-  $output = ctools_modal_form_wrapper('panels_edit_display_settings_form', $form_state);
-  if (empty($output)) {
-    panels_edit_cache_set($cache);
-
-    $output = array();
-    $output[] = ctools_modal_command_dismiss();
-  }
-
-  ctools_ajax_render($output);
-}
 
 /**
  * Form for display settings.
@@ -712,6 +264,8 @@ function panels_edit_display_settings_form(&$form_state) {
 
       // We have to create a manual fieldset because fieldsets do not support IDs.
       // Use 'hidden' instead of 'markup' so that the process will run.
+      // Add js for collapsible fieldsets manually
+      drupal_add_js('misc/collapse.js');
       $form['display_title']['contexts_prefix'] = array(
         '#type' => 'hidden',
         '#id' => 'edit-display-substitutions',
@@ -731,7 +285,9 @@ function panels_edit_display_settings_form(&$form_state) {
       }
 
       $header = array(t('Keyword'), t('Value'));
-      $form['display_title']['contexts']['context'] = array('#value' => theme('table', $header, $rows));
+      $form['display_title']['contexts'] = array(
+        '#value' => theme('table', $header, $rows),
+      );
       $form['display_title']['contexts_suffix'] = array(
         '#value' => '</fieldset></div>',
       );
@@ -775,1243 +331,3 @@ function panels_edit_display_settings_form_submit($form, &$form_state) {
     $display->hide_title = $form_state['values']['display_title']['hide_title'];
   }
 }
-
-/**
- * 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_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_add_content($cache, $panel_id, $category)
-    );
-  }
-
-  ctools_modal_render(t('Error'), t('Invalid input'));
-}
-
-/**
- * Display a list of content available.
- */
-function panels_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 = '<div class="content-type-button clear-block">';
-        $url = "panels/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 .= '<div>' . ctools_ajax_text_button($title, $url, $description, 'panels-modal-add-config') . '</div>';
-        $output .= '</div>';
-        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 = '<div class="panels-add-content-modal">';
-    natcasesort($category_names);
-    $selector = '<div class="panels-categories-box">';
-
-    // 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/ajax/add-pane/$cache_key/$panel_id/$category";
-      $selector .= ctools_ajax_text_button($name, $url, '', $class);
-    }
-
-    $selector .= '</div>';
-    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 = '<div class="panels-categories-description">';
-      $center .= t('Content options are divided by category. Please select a category from the left to proceed.');
-      $center .= '</div>';
-    }
-    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 .= '<div class="panels-section-column panels-section-column-' . $id . '">'
-        . '<div class="inside">' . $column . '</div></div>';
-      }
-      $center .= '</div>'; // columns
-    }
-    $output .= '<div class="panels-section-column panels-section-column-categories">'
-      . '<div class="inside">' . $selector . '</div></div>';
-    $output .= '<div class="panels-section-columns">';
-    $output .= $center;
-    $output .= '</div>'; // categories box
-  }
-  return $output;
-}
-
-/**
- * AJAX entry point for to configure a pane that has just been added.
- */
-function panels_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/ajax/add-pane-config/$cache_key/$panel_id/$type_name/$subtype_name/%step",
-    'next callback' => 'panels_ajax_edit_pane_config_next',
-    'finish callback' => 'panels_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_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_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/ajax/configure/$cache_key/$pid/%step",
-    'next callback' => 'panels_ajax_edit_pane_config_next',
-    'finish callback' => 'panels_ajax_edit_pane_config_finish',
-  );
-
-  ctools_content_form('edit', $form_info, $form_state, $content_type, $pane->subtype,  $subtype, $pane->configuration, $step);
-}
-
-function panels_ajax_edit_pane_config_next(&$form_state) {
-  $form_state['cache']->new_pane = $form_state['pane'];
-  panels_edit_cache_set($form_state['cache']);
-}
-
-function panels_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;
-
-  panels_edit_cache_set($cache);
-
-  $form_state['commands'][] = ctools_ajax_command_append("#panel-pane-$pane->panel", panels_show_pane($cache->display, $pane, TRUE));
-  $form_state['commands'][] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
-}
-
-function panels_ajax_edit_pane_config_finish(&$form_state) {
-  $cache = &$form_state['cache'];
-  $pane = &$form_state['pane'];
-  panels_edit_cache_set($cache);
-
-  $form_state['commands'][] = ctools_ajax_command_replace("#panel-pane-$pane->pid", panels_show_pane($cache->display, $pane, TRUE));
-  $form_state['commands'][] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
-}
-
-/**
- * Entry point for AJAX: toggle pane show/hide status.
- *
- * @param int $cache
- *  The display id for the display object currently being edited. Errors out silently if absent.
- * @param int $pid
- *  The pane id for the pane object whose show/hide state we're toggling.
- * @param string $op
- *  The operation - showing or hiding - that this should perform. This could be calculated from
- *  cached values, but given that this is a toggle button and people may click it too fast,
- *  it's better to leave the decision on which $op to use up to the js than to calculate it here.
- */
-function panels_ajax_toggle_shown($op, $cache, $pid = NULL) {
-  if (empty($cache->display->content[$pid])) {
-    ctools_ajax_render_error(t('Invalid pane id.'));
-  }
-
-  $pane = &$cache->display->content[$pid];
-
-  $pane->shown = ($op == 'show');
-  panels_edit_cache_set($cache);
-
-  $output = array();
-  $output[] = ctools_ajax_command_replace("#panel-pane-$pane->pid", panels_show_pane($cache->display, $pane, TRUE));
-  $output[] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
-
-  ctools_ajax_render($output);
-}
-
-/**
- * Entry point for AJAX: switch which pane is being used to set the display title.
- *
- * @param int $cache
- *  The display id for the display object currently being edited. Errors out silently if absent.
- * @param int $pid
- *  The pane id for the pane object whose show/hide state we're toggling.
- */
-function panels_ajax_set_display_title($cache, $pid = NULL) {
-  if (empty($cache->display->content[$pid])) {
-    ctools_ajax_render_error(t('Invalid pane id.'));
-  }
-
-  $pane = &$cache->display->content[$pid];
-
-  $old_title = !empty($cache->display->title_pane) ? $cache->display->title_pane : NULL;
-  $cache->display->title_pane = $pid;
-
-  panels_edit_cache_set($cache);
-
-  $output = array();
-  $output[] = ctools_ajax_command_replace("#panel-pane-$pane->pid", panels_show_pane($cache->display, $pane, TRUE));
-
-  if ($old_title && !empty($cache->display->content[$old_title])) {
-    $old_pane = $cache->display->content[$old_title];
-    $output[] = ctools_ajax_command_replace("#panel-pane-$old_pane->pid", panels_show_pane($cache->display, $old_pane, TRUE));
-  }
-
-  // Update the text stating which pane is currently selected as the title.
-//  $output[] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
-
-  ctools_ajax_render($output);
-}
-
-/**
- * Entry point for AJAX modal: configure pane cache method
- */
-function panels_ajax_cache_method($cache, $pid = NULL) {
-  ctools_include('content');
-  // This lets us choose whether we're doing the display's cache or
-  // a pane's.
-  if ($pid == 'display') {
-    $conf = &$cache->display->cache;
-    $title = t('Cache method for this display');
-  }
-  else if (!empty($cache->display->content[$pid])) {
-    $pane = &$cache->display->content[$pid];
-    $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-    $conf = &$pane->cache;
-    $title = t('Cache method for !subtype_title', array('!subtype_title' => $subtype['title']));
-  }
-  else {
-    ctools_modal_render(t('Error'), t('Invalid pane id.'));
-  }
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'conf' => &$conf,
-    'title' => $title,
-    'ajax' => TRUE,
-  );
-
-  $output = ctools_modal_form_wrapper('panels_edit_cache_method_form', $form_state);
-  if (empty($output)) {
-    // Preserve this; this way we don't actually change the method until they
-    // have saved the form.
-    $info = panels_get_cache($form_state['method']);
-    $function = panels_plugin_get_function('cache', $info, 'settings form');
-    if (!$function) {
-      $conf['method'] = $form_state['method'];
-      $conf['settings'] = array();
-      panels_edit_cache_set($cache);
-
-      $output = array();
-      $output[] = ctools_modal_command_dismiss();
-
-      if ($pid != 'display') {
-        $output[] = ctools_ajax_command_replace("#panel-pane-$pid", panels_show_pane($cache->display, $pane, TRUE));
-        $output[] = ctools_ajax_command_changed("#panel-pane-$pid", "div.grabber span.text");
-      }
-      else {
-        $links = panels_edit_display_get_links($cache->display);
-        $output[] = ctools_ajax_command_replace('.panels-display-links', $links);
-      }
-    }
-    else {
-      $cache->method = $form_state['method'];
-      panels_edit_cache_set($cache);
-      // send them to next form.
-      return panels_ajax_cache_settings($cache, $pid);
-    }
-  }
-
-  ctools_ajax_render($output);
-}
-
-/**
- * Choose cache method form
- */
-function panels_edit_cache_method_form(&$form_state) {
-  $display = &$form_state['display'];
-  $conf = &$form_state['conf'];
-
-  // Set to 0 to ensure we get a selected radio.
-  if (!isset($conf['method'])) {
-    $conf['method'] = 0;
-  }
-
-  $caches = panels_get_caches();
-  if (empty($caches)) {
-    $form['markup'] = array('#value' => t('No caching options are available at this time. Please enable a panels caching module in order to use caching options.'));
-    return $form;
-  }
-
-  $options[0] = t('No caching');
-  foreach ($caches as $cache => $info) {
-    $options[$cache] = check_plain($info['title']);
-  }
-
-  $form['method'] = array(
-    '#prefix' => '<div class="no-float">',
-    '#suffix' => '</div>',
-    '#type' => 'radios',
-    '#title' => t('Method'),
-    '#options' => $options,
-    '#default_value' => $conf['method'],
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Next'),
-  );
-  return $form;
-}
-
-/**
- * Submit callback for panels_edit_cache_method_form.
- *
- * All this needs to do is return the method.
- */
-function panels_edit_cache_method_form_submit($form, &$form_state) {
-  $form_state['method'] = $form_state['values']['method'];
-}
-
-/**
- * Handle the cache settings form
- */
-function panels_ajax_cache_settings($cache, $pid = 0) {
-  ctools_include('content');
-  $cache_key = $cache->display->cache_key;
-  // This lets us choose whether we're doing the display's cache or
-  // a pane's.
-  if ($pid == 'display') {
-    $conf = &$cache->display->cache;
-    $title = t('Cache settings for this display');
-  }
-  else if (!empty($cache->display->content[$pid])) {
-    $pane = &$cache->display->content[$pid];
-    $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-
-    $conf = &$pane->cache;
-    $title = t('Cache settings for !subtype_title', array('!subtype_title' => $subtype['title']));
-  }
-  else {
-    ctools_modal_render(t('Error'), t('Invalid pane id.'));
-  }
-
-  if (isset($cache->method) && (empty($conf['method']) || $conf['method'] != $cache->method)) {
-    $conf['method'] = $cache->method;
-    $info = panels_get_cache($conf['method']);
-    $conf['settings'] = isset($info['defaults']) ? $info['defaults'] : array();
-  }
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'pid' => $pid,
-    'conf' => &$conf,
-    'ajax' => TRUE,
-    'title' => $title,
-    'url' => url("panels/ajax/cache-settings/$cache_key/$pid", array('absolute' => TRUE)),
-  );
-
-  $output = ctools_modal_form_wrapper('panels_edit_cache_settings_form', $form_state);
-  if (empty($output)) {
-    panels_edit_cache_set($cache);
-
-    $output = array();
-    $output[] = ctools_modal_command_dismiss();
-
-    if ($pid != 'display') {
-      $output[] = ctools_ajax_command_replace("#panel-pane-$pid", panels_show_pane($cache->display, $pane, TRUE));
-      $output[] = ctools_ajax_command_changed("#panel-pane-$pid", "div.grabber span.text");
-    }
-    else {
-      $links = panels_edit_display_get_links($cache->display);
-      $output[] = ctools_ajax_command_replace('.panels-display-links', $links);
-    }
-  }
-  ctools_ajax_render($output);
-}
-
-/**
- * Cache settings form
- */
-function panels_edit_cache_settings_form(&$form_state) {
-  $display = &$form_state['display'];
-  $conf = &$form_state['conf'];
-  $pid = $form_state['pid'];
-  $info = panels_get_cache($conf['method']);
-
-  $form['#action'] = $form_state['url'];
-
-  $form['description'] = array(
-    '#prefix' => '<div class="description">',
-    '#suffix' => '</div>',
-    '#value' => check_plain($info['description']),
-  );
-
-  $function = panels_plugin_get_function('cache', $conf['method'], 'settings form');
-
-  $form['settings'] = $function($conf['settings'], $display, $pid);
-  $form['settings']['#tree'] = TRUE;
-
-  $form['display'] = array(
-    '#type' => 'value',
-    '#value' => $display,
-  );
-
-  $form['pid'] = array(
-    '#type' => 'value',
-    '#value' => $pid,
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Validate cache settings.
- */
-function panels_edit_cache_settings_form_validate($form, &$form_state) {
-  if ($function = panels_plugin_get_function('cache', $form_state['conf']['method'], 'settings form validate')) {
-    $function($form, $form_state['values']['settings']);
-  }
-}
-
-/**
- * Allows panel styles to validate their style settings.
- */
-function panels_edit_cache_settings_form_submit($form, &$form_state) {
-  if ($function = panels_plugin_get_function('cache', $form_state['conf']['method'], 'settings form submit')) {
-    $function($form_state['values']['settings']);
-  }
-
-  $form_state['conf']['settings'] = $form_state['values']['settings'];
-}
-
-/**
- * Entry point for AJAX modal: configure pane style method
- */
-function panels_ajax_style_type($type, $cache, $pid = NULL) {
-  // This lets us choose whether we're doing the display's cache or
-  // a pane's.
-  switch ($type) {
-    case 'display':
-      $style = isset($cache->display->panel_settings['style']) ? $cache->display->panel_settings['style'] : 'default';
-      $title = t('Default style for this display');
-      break;
-
-    case 'panel':
-      $style = isset($cache->display->panel_settings[$pid]['style']) ? $cache->display->panel_settings[$pid]['style'] : '-1'; // -1 signifies to use the default setting.
-      $layout = panels_get_layout($cache->display->layout);
-      $layout_panels = panels_get_panels($layout, $cache->display);
-      $title = t('Panel style for region "!region"', array('!region' => $layout_panels[$pid]));
-      break;
-
-    case 'pane':
-      ctools_include('content');
-      $pane = &$cache->display->content[$pid];
-      $style = isset($pane->style['style']) ? $pane->style['style'] : 'default';
-      $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-      $title = t('Pane style for "!pane"', array('!pane' => $subtype['title']));
-      break;
-
-    default:
-      ctools_modal_render(t('Error'), t('Invalid pane id.'));
-  }
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'style' => $style,
-    'title' => $title,
-    'ajax' => TRUE,
-    'type' => $type,
-  );
-
-  $output = ctools_modal_form_wrapper('panels_edit_style_type_form', $form_state);
-  if (empty($output)) {
-    // Preserve this; this way we don't actually change the method until they
-    // have saved the form.
-    $style = panels_get_style($form_state['style']);
-    $function = panels_plugin_get_function('style', $style, ($type == 'pane') ? 'pane settings form' : 'settings form');
-    if (!$function) {
-      // If there's no settings form, just change the style and exit.
-      switch($type) {
-        case 'display':
-          $cache->display->panel_settings['style'] = $form_state['style'];
-          if (isset($cache->display->panel_settings['style_settings']['default'])) {
-            unset($cache->display->panel_settings['style_settings']['default']);
-          }
-          break;
-
-        case 'panel':
-          $cache->display->panel_settings[$pid]['style'] = $form_state['style'];
-          if (isset($cache->display->panel_settings['style_settings'][$pid])) {
-            unset($cache->display->panel_settings['style_settings'][$pid]);
-          }
-          break;
-
-        case 'pane':
-          $pane->style['style'] = $form_state['style'];
-          if (isset($pane->style['settings'])) {
-            unset($pane->style['settings']);
-          }
-
-          break;
-      }
-      panels_edit_cache_set($cache);
-
-      $output = array();
-      $output[] = ctools_modal_command_dismiss();
-
-      if ($type == 'pane') {
-        $output[] = ctools_ajax_command_replace("#panel-pane-$pane->pid", panels_show_pane($cache->display, $pane, TRUE));
-        $output[] = ctools_ajax_command_changed("#panel-pane-$pid", "div.grabber span.text");
-      }
-      else if ($type == 'panel') {
-        $links = panels_edit_panel_get_links($cache->display, $pid);
-        $output[] = ctools_ajax_command_replace('.panels-region-links-' . $pid, $links);
-      }
-      else {
-        $links = panels_edit_display_get_links($cache->display);
-        $output[] = ctools_ajax_command_replace('.panels-display-links', $links);
-      }
-    }
-    else {
-      if ($form_state['style'] != $form_state['old_style']) {
-        $cache->style = $form_state['style'];
-        panels_edit_cache_set($cache);
-      }
-
-      // send them to next form.
-      return panels_ajax_style_settings($type, $cache, $pid);
-    }
-  }
-
-  ctools_ajax_render($output);
-}
-
-/**
- * Choose cache method form
- */
-function panels_edit_style_type_form(&$form_state) {
-  $display = &$form_state['display'];
-  $style = $form_state['style'];
-  $type = $form_state['type'];
-
-  $styles = panels_get_styles();
-
-  $function = ($type == 'pane' ? 'render pane' : 'render panel');
-  $options = array();
-  if ($type == 'panel') {
-    $options[-1] = t('Use display default style');
-  }
-
-  foreach ($styles as $id => $info) {
-    if (empty($info['hidden']) && (!empty($info[$function]) || $id == 'default')) {
-      $options[$id] = check_plain($info['title']);
-    }
-  }
-
-  asort($options);
-
-  $form['style'] = array(
-    '#prefix' => '<div class="no-float">',
-    '#suffix' => '</div>',
-    '#type' => 'radios',
-    '#title' => t('Style'),
-    '#options' => $options,
-    '#default_value' => $style,
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Next'),
-  );
-  return $form;
-}
-
-/**
- * Submit callback for panels_edit_cache_method_form.
- *
- * All this needs to do is return the method.
- */
-function panels_edit_style_type_form_submit($form, &$form_state) {
-  $form_state['old_style'] = $form_state['style'];
-  $form_state['style'] = $form_state['values']['style'];
-}
-
-/**
- * Handle the cache settings form
- */
-function panels_ajax_style_settings($type, $cache, $pid = '') {
-  if (isset($cache->style)) {
-    $style = panels_get_style($cache->style);
-    $defaults = isset($style['defaults']) ? $style['defaults'] : array();
-    // Get the &$conf variable based upon whose style we're editing.
-    switch ($type) {
-      case 'display':
-        $cache->display->panel_settings['style'] = $cache->style;
-        $cache->display->panel_settings['style_settings']['default'] = $defaults;
-        $conf = &$cache->display->panel_settings['style_settings']['default'];
-        break;
-
-      case 'panel':
-        $cache->display->panel_settings[$pid]['style'] = $cache->style;
-        $cache->display->panel_settings['style_settings'][$pid] = $defaults;
-        $conf = &$cache->display->panel_settings['style_settings'][$pid];
-        break;
-
-      case 'pane':
-        $pane = &$cache->display->content[$pid];
-        $pane->style['style'] = $cache->style;
-        $pane->style['settings'] = $defaults;
-        $conf = &$pane->style['settings'];
-        break;
-    }
-  }
-  else {
-    switch ($type) {
-      case 'display':
-        $style = panels_get_style($cache->display->panel_settings['style']);
-        break;
-
-      case 'panel':
-        $style = panels_get_style($cache->display->panel_settings[$pid]['style']);
-        break;
-
-      case 'pane':
-        $pane = &$cache->display->content[$pid];
-        $style = panels_get_style($pane->style['style']);
-        break;
-    }
-  }
-  $cache_key = $cache->display->cache_key;
-
-  // Set up our $conf reference.
-  // We should only be able to get here after a style has already been set up with
-  // defaults so I think it is safe to not check.
-  switch ($type) {
-    case 'display':
-      $conf = &$cache->display->panel_settings['style_settings']['default'];
-      $title = t('Style settings for @style (display)', array('@style' => $style['title']));
-      break;
-
-    case 'panel':
-      $conf = &$cache->display->panel_settings['style_settings'][$pid];
-      $layout = panels_get_layout($cache->display->layout);
-      $layout_panels = panels_get_panels($layout, $cache->display);
-      $title = t('Style settings for style @style (Region "!region")', array('@style' => $style['title'], '!region' => $layout_panels[$pid]));
-      break;
-
-    case 'pane':
-      ctools_include('content');
-      $pane = &$cache->display->content[$pid];
-      $conf = &$pane->style['settings'];
-      $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-      $title = t('Style settings for style @style (Pane "!pane")', array('@style' => $style['title'], '!pane' => $subtype['title']));
-      break;
-  }
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'type' => $type,
-    'pid' => $pid,
-    'conf' => &$conf,
-    'style' => $style,
-    'ajax' => TRUE,
-    'title' => $title,
-    'url' => url("panels/ajax/style-settings/$type/$cache_key/$pid", array('absolute' => TRUE)),
-  );
-
-  $output = ctools_modal_form_wrapper('panels_edit_style_settings_form', $form_state);
-  if (empty($output)) {
-    if (isset($cache->style)) {
-      unset($cache->style);
-    }
-
-    // $conf was a reference so it should just modify.
-    panels_edit_cache_set($cache);
-
-    $output = array();
-    $output[] = ctools_modal_command_dismiss();
-
-    if ($type == 'pane') {
-      $output[] = ctools_ajax_command_replace("#panel-pane-$pane->pid", panels_show_pane($cache->display, $pane, TRUE));
-      $output[] = ctools_ajax_command_changed("#panel-pane-$pid", "div.grabber span.text");
-    }
-    else if ($type == 'panel') {
-      $links = panels_edit_panel_get_links($cache->display, $pid);
-      $output[] = ctools_ajax_command_replace('.panels-region-links-' . $pid, $links);
-    }
-    else {
-      $links = panels_edit_display_get_links($cache->display);
-      $output[] = ctools_ajax_command_replace('.panels-display-links', $links);
-    }
-  }
-  ctools_ajax_render($output);
-}
-
-/**
- * Cache settings form
- */
-function panels_edit_style_settings_form(&$form_state) {
-  $display = &$form_state['display'];
-  $conf = &$form_state['conf'];
-  $pid = $form_state['pid'];
-  $style = $form_state['style'];
-  $type = $form_state['type'];
-
-  $form['#action'] = $form_state['url'];
-
-  $form['description'] = array(
-    '#prefix' => '<div class="description">',
-    '#suffix' => '</div>',
-    '#value' => check_plain($style['description']),
-  );
-
-  $function = panels_plugin_get_function('cache', $style, ($type == 'pane') ? 'pane settings form' : 'settings form');
-
-  $form['settings'] = $function($conf, $display, $pid, $type);
-  $form['settings']['#tree'] = TRUE;
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Validate cache settings.
- */
-function panels_edit_style_settings_form_validate($form, &$form_state) {
-  $name = $form_state['type'] == 'pane' ? 'pane settings form validate' : 'settings form validate';
-  if ($function = panels_plugin_get_function('style', $form_state['style'], $name)) {
-    $function($form, $form_state['values']['settings']);
-  }
-}
-
-/**
- * Allows panel styles to validate their style settings.
- */
-function panels_edit_style_settings_form_submit($form, &$form_state) {
-  $name = $form_state['type'] == 'pane' ? 'pane settings form submit' : 'settings form submit';
-  if ($function = panels_plugin_get_function('style', $form_state['style'], $name)) {
-    $function($form_state['values']['settings']);
-  }
-
-  $form_state['conf'] = $form_state['values']['settings'];
-}
-
-/**
- * AJAX entry point to configure CSS for a pane.
- */
-function panels_ajax_configure_pane_css($cache, $pid = NULL) {
-  ctools_include('content');
-  if (empty($cache->display->content[$pid])) {
-    ctools_modal_render(t('Error'), t('Invalid pane id.'));
-  }
-
-  $pane = &$cache->display->content[$pid];
-  $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'pane' => &$pane,
-    'ajax' => TRUE,
-    'title' => t('Configure CSS on !subtype_title', array('!subtype_title' => $subtype['title'])),
-  );
-
-  $output = ctools_modal_form_wrapper('panels_content_configure_pane_css_form', $form_state);
-  if (empty($output)) {
-    panels_edit_cache_set($cache);
-    $output = array();
-    $output[] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
-    $output[] = ctools_modal_command_dismiss();
-  }
-
-  ctools_ajax_render($output);
-}
-
-
-/**
- * Configure CSS on a pane form.
- */
-function panels_content_configure_pane_css_form(&$form_state) {
-  $display = &$form_state['display'];
-  $pane = &$form_state['pane'];
-
-  $form['css_id'] = array(
-    '#type' => 'textfield',
-    '#default_value' => isset($pane->css['css_id']) ? $pane->css['css_id'] : '',
-    '#title' => t('CSS ID'),
-    '#description' => t('CSS ID to apply to this pane. This may be blank.'),
-  );
-  $form['css_class'] = array(
-    '#type' => 'textfield',
-    '#default_value' => isset($pane->css['css_class']) ? $pane->css['css_class'] : '',
-    '#title' => t('CSS class'),
-    '#description' => t('CSS class to apply to this pane. This may be blank.'),
-  );
-
-  $form['next'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * FAPI submission function for the CSS configure form.
- *
- * All this does is set up $pane properly. The caller is responsible for
- * actually storing this somewhere.
- */
-function panels_content_configure_pane_css_form_submit($form, &$form_state) {
-  $pane = &$form_state['pane'];
-  $display = $form_state['display'];
-
-  $pane->css['css_id'] = $form_state['values']['css_id'];
-  $pane->css['css_class'] = $form_state['values']['css_class'];
-}
-
-/**
- * AJAX entry point to configure access settings for a pane.
- */
-function panels_ajax_configure_access_settings($cache, $pid = NULL) {
-  ctools_include('content');
-  if (empty($cache->display->content[$pid])) {
-    ctools_modal_render(t('Error'), t('Invalid pane id.'));
-  }
-
-  $pane = &$cache->display->content[$pid];
-  $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'pane' => &$pane,
-    'ajax' => TRUE,
-    'title' => t('Access settings on !subtype_title', array('!subtype_title' => $subtype['title'])),
-  );
-
-  $output = ctools_modal_form_wrapper('panels_content_configure_access_settings_form', $form_state);
-  if (empty($output)) {
-    panels_edit_cache_set($cache);
-    $output = array();
-    $output[] = ctools_ajax_command_replace("#panel-pane-$pane->pid", panels_show_pane($cache->display, $pane, TRUE));
-    $output[] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
-    $output[] = ctools_modal_command_dismiss();
-  }
-
-  ctools_ajax_render($output);
-}
-
-
-/**
- * Form to control basic visibility settings.
- */
-function panels_content_configure_access_settings_form(&$form_state) {
-  $display = &$form_state['display'];
-  $pane = &$form_state['pane'];
-
-  $form['logic'] = array(
-    '#type' => 'radios',
-    '#options' => array(
-      'and' => t('All criteria must pass.'),
-      'or' => t('Only one criteria must pass.'),
-    ),
-    '#default_value' => isset($pane->access['logic']) ? $pane->access['logic'] : 'and',
-  );
-
-  $form['next'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * FAPI submission function for the edit access settings form.
- *
- * All this does is set up $pane properly. The caller is responsible for
- * actually storing this somewhere.
- */
-function panels_content_configure_access_settings_form_submit($form, &$form_state) {
-  $pane = &$form_state['pane'];
-  $display = $form_state['display'];
-
-  $pane->access['logic'] = $form_state['values']['logic'];
-}
-
-/**
- * AJAX entry point for to add a visibility rule.
- */
-function panels_ajax_add_access_test($cache, $pid = NULL) {
-  ctools_include('content');
-  if (empty($cache->display->content[$pid])) {
-    ctools_modal_render(t('Error'), t('Invalid pane id.'));
-  }
-
-  $pane = &$cache->display->content[$pid];
-  $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'pane' => &$pane,
-    'ajax' => TRUE,
-    'title' => t('Add visibility rule for !subtype_title', array('!subtype_title' => $subtype['title'])),
-  );
-
-  $output = ctools_modal_form_wrapper('panels_content_add_access_test_form', $form_state);
-  if (empty($output)) {
-    // Set up the plugin in cache
-    $plugin = ctools_get_access_plugin($form_state['values']['type']);
-    $cache->new_plugin = ctools_access_new_test($plugin);
-    panels_edit_cache_set($cache);
-
-    // go to the next step.
-    return panels_ajax_configure_access_test($cache, $pid, 'add');
-  }
-
-  ctools_ajax_render($output);
-}
-
-/**
- * Form to add a visibility rule.
- */
-function panels_content_add_access_test_form(&$form_state) {
-  $display = &$form_state['display'];
-  $pane = &$form_state['pane'];
-
-  $plugins = ctools_get_relevant_access_plugins($display->context);
-  $options = array();
-  foreach ($plugins as $id => $plugin) {
-    $options[$id] = $plugin['title'];
-  }
-
-  asort($options);
-
-  $form['type'] = array(
-    // This ensures that the form item is added to the URL.
-    '#type' => 'radios',
-    '#options' => $options,
-  );
-
-  $form['next'] = array(
-    '#type' => 'submit',
-    '#value' => t('Next'),
-  );
-
-  return $form;
-}
-
-/**
- * AJAX entry point for to configure vsibility rule
- */
-function panels_ajax_configure_access_test($cache, $pid = NULL, $id = 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];
-  $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
-
-  // Set this up here because $id gets changed later.
-  $url = "panels/ajax/access-test/$cache_key/$pid/$id";
-
-  // If we're adding a new one, get the stored data from cache and
-  // add it. It's stored as a cache so that if this is closed
-  // we don't accidentally add an unconfigured plugin.
-  if ($id == 'add') {
-    $pane->access['plugins'][] = $cache->new_plugin;
-    $id = max(array_keys($pane->access['plugins']));
-  }
-  else if (empty($pane->access['plugins'][$id])) {
-    ctools_modal_render(t('Error'), t('Invalid test id.'));
-  }
-
-  $form_state = array(
-    'display' => &$cache->display,
-    'pane' => &$pane,
-    'ajax' => TRUE,
-    'title' => t('Configure visibility rule for !subtype_title', array('!subtype_title' => $subtype['title'])),
-    'test' => &$pane->access['plugins'][$id],
-    'plugin' => ctools_get_access_plugin($pane->access['plugins'][$id]['name']),
-    'url' => url($url, array('absolute' => TRUE)),
-  );
-
-  $output = ctools_modal_form_wrapper('panels_content_configure_access_test_form', $form_state);
-  if (empty($output)) {
-    // Unset the new plugin
-    if (isset($cache->new_plugin)) {
-      unset($cache->new_plugin);
-    }
-
-    if (!empty($form_state['remove'])) {
-      unset($pane->access['plugins'][$id]);
-    }
-
-    panels_edit_cache_set($cache);
-    $output = array();
-    $output[] = ctools_ajax_command_replace("#panel-pane-$pane->pid", panels_show_pane($cache->display, $pane, TRUE));
-    $output[] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
-    $output[] = ctools_modal_command_dismiss();
-  }
-
-  ctools_ajax_render($output);
-}
-
-/**
- * Form to configure a visibility rule.
- */
-function panels_content_configure_access_test_form(&$form_state) {
-  $display = &$form_state['display'];
-  $test = &$form_state['test'];
-  $plugin = &$form_state['plugin'];
-
-  $form['#action'] = $form_state['url'];
-
-  $contexts = $display->context;
-  if (!isset($contexts['logged-in-user'])) {
-    $contexts['logged-in-user'] = ctools_access_get_loggedin_context();
-  }
-
-  if (isset($plugin['required context'])) {
-    $form['context'] = ctools_context_selector($contexts, $plugin['required context'], $test['context']);
-  }
-
-  $form['settings'] = array('#tree' => TRUE);
-  if ($function = ctools_plugin_get_function($plugin, 'settings form')) {
-    $function($form, $form_state, $test['settings']);
-  }
-
-  $form['save'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  $form['remove'] = array(
-    '#type' => 'submit',
-    '#value' => t('Remove'),
-    '#remove' => TRUE,
-  );
-
-  return $form;
-}
-
-/**
- * Validate handler for visibility rule settings
- */
-function panels_content_configure_access_test_form_validate(&$form, &$form_state) {
-  if (!empty($form_state['clicked_button']['#remove'])) {
-    return;
-  }
-
-  if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form validate')) {
-    $function($form, $form_state);
-  }
-}
-
-/**
- * Submit handler for visibility rule settings
- */
-function panels_content_configure_access_test_form_submit(&$form, &$form_state) {
-  if (!empty($form_state['clicked_button']['#remove'])) {
-    $form_state['remove'] = TRUE;
-    return;
-  }
-
-  if ($function = ctools_plugin_get_function($form_state['plugin'], 'settings form submit')) {
-    $function($form, $form_state);
-  }
-
-  $form_state['test']['settings'] = $form_state['values']['settings'];
-  if (isset($form_state['values']['context'])) {
-    $form_state['test']['context'] = $form_state['values']['context'];
-  }
-}
-
-/**
- * }@ End of 'defgroup panels_ajax'
- */
-
-// ---------------------------------------------------------------------------
-// Panels theming functions
-
-// @DND
-function panels_render_dnd($content) {
-  $output = '<div class="panels-dnd" id="panels-dnd-main">' . $content . '</div>';
-  return $output;
-}
-
-// @DND
-function panels_render_region_dnd($content, $area, $label, $footer) {
-  return "<div class='panels-display' id='panel-pane-$area'>$footer<h2 class='label'>$label</h2>$content</div>";
-}
-
-// @DND
-function panels_render_pane_dnd($block, $id, $label, $left_buttons = NULL, $buttons = NULL) {
-  $output = '';
-  if (!$block->title) {
-    $block->title = t('No title');
-  }
-  static $count = 0;
-  $output .= '<div class="grabber">';
-  if ($buttons) {
-    $output .= '<span class="buttons">' . $buttons . '</span>';
-  }
-  if ($left_buttons) {
-    $output .= '<span class="left_buttons">' . $left_buttons . '</span>';
-  }
-  $output .= '<span class="text">' . $label . '</span></div>';
-  $output .= '<div class="panel-pane-collapsible">';
-  $output .= panels_render_pane_collapsible($block);
-  $output .= '</div>';
-  return $output;
-}
-
-// @DND
-function panels_render_pane_collapsible($block) {
-  $output = '';
-  $output .= '<div class="pane-title">' . $block->title . '</div>';
-  $output .= '<div class="pane-content">' . filter_xss_admin($block->content) . '</div>';
-  return $output;
-}
index 2ce9712..5ce7fbe 100644 (file)
@@ -15,7 +15,7 @@
  * @see panels_edit_layout() for details on the various behaviors of this function.
  */
 function _panels_edit_layout($display, $finish, $destination, $allowed_layouts) {
-  panels_load_include('common');
+  ctools_include('common', 'panels');
   ctools_include('form');
 
   $form_state = array(
@@ -59,7 +59,7 @@ function _panels_edit_layout($display, $finish, $destination, $allowed_layouts)
       }
 
       if ($destination) {
-        return drupal_goto($destination);
+        return panels_goto($destination);
       }
       return $change_form_state['display'];
     }
@@ -74,27 +74,77 @@ function _panels_edit_layout($display, $finish, $destination, $allowed_layouts)
  */
 function panels_choose_layout(&$form_state) {
   $display = &$form_state['display'];
-  panels_load_include('common');
+  ctools_include('common', 'panels');
+  ctools_include('cleanstring');
 
   $layouts = panels_common_get_allowed_layouts($form_state['allowed_layouts']);
-  
+  $categories = array();
+  $current = '';
   foreach ($layouts as $id => $layout) {
-    $options[$id] = panels_print_layout_icon($id, $layout, check_plain($layout['title']));
+    $category = ctools_cleanstring($layout['category']);
+    // Default category to first in case layout doesn't exist or there isn't one.
+    if (empty($current)) {
+      $current = $category;
+    }
+
+    $categories[$category] = $layout['category'];
+    $options[$category][$id] = panels_print_layout_icon($id, $layout, check_plain($layout['title']));
+
+    // Set current category to what is chosen.
+    if ($id == $display->layout) {
+      $current = $category;
+    }
   }
 
-  drupal_add_js(panels_get_path('js/layout.js'));
-  $form['layout'] = array(
-    '#type' => 'radios',
-    '#title' => t('Choose layout'),
-    '#options' => $options,
-    '#default_value' => in_array($display->layout, array_keys($layouts)) ? $display->layout : NULL,
+  ctools_add_js('layout', 'panels');
+
+  $form['categories'] = array(
+    '#title' => t('Category'),
+    '#type' => 'select',
+    '#options' => $categories,
+    '#default_value' => $current,
   );
 
-  $form['clearer'] = array(
-    // TODO: Fix this to use clear-block instead
-    '#value' => '<div style="clear: both;"></div>',
+  $form['layout'] = array(
+    '#prefix' => '<div class="panels-choose-layout panels-layouts-checkboxes clear-block">',
+    '#suffix' => '</div>',
   );
 
+  // We set up the dependencies manually because these aren't really form
+  // items. It's possible there's a simpler way to do this, but I could not
+  // think of one at the time.
+  $dependencies = array();
+  foreach ($options as $category => $radios) {
+    $dependencies['panels-layout-category-' . $category] = array(
+      'values' => array('edit-categories' => array($category)),
+      'num' => 1,
+      'type' => 'hide',
+    );
+
+    $form['layout'][$category] = array(
+      '#prefix' => '<div id="panels-layout-category-' . $category . '-wrapper"><div id="panels-layout-category-' . $category . '" class="form-checkboxes clear-block"><div class="panels-layouts-category">' . $categories[$category] . '</div>',
+      '#suffix' => '</div></div>',
+    );
+
+    foreach ($radios as $key => $choice) {
+      // Generate the parents as the autogenerator does, so we will have a
+      // unique id for each radio button.
+      $form['layout'][$category][$key] = array(
+        '#type' => 'radio',
+        '#title' => $choice,
+        '#parents' => array('layout'),
+        '#id' => form_clean_id('edit-layout-' . $key),
+        '#return_value' => check_plain($key),
+        '#default_value' => in_array($display->layout, array_keys($layouts)) ? $display->layout : NULL,
+      );
+    }
+  }
+
+  ctools_add_js('dependent');
+  $js['CTools']['dependent'] = $dependencies;
+  drupal_add_js($js, 'setting');
+
+
   if (empty($form_state['no buttons'])) {
     $form['submit'] = array(
       '#type' => 'submit',
@@ -137,7 +187,7 @@ function panels_change_layout(&$form_state) {
   $display = &$form_state['display'];
 
   $new_layout = panels_get_layout($form_state['layout']);
-  $new_layout_panels = panels_get_panels($new_layout, $display);
+  $new_layout_panels = panels_get_regions($new_layout, $display);
 
   $options = $new_layout_panels;
   $keys = array_keys($options);
@@ -172,7 +222,7 @@ function panels_change_layout(&$form_state) {
     '#suffix' => '</div>',
   );
 
-  $old_layout_panels = panels_get_panels($old_layout, $display);
+  $old_layout_panels = panels_get_regions($old_layout, $display);
   if (empty($display->panels)) {
     $form['old'] = array(
       '#prefix' => '<div>',
index e528536..7b24461 100644 (file)
@@ -8,98 +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;
-  }
-
-  // TODO: This may not be necessary now. Check this.
-  panels_sanitize_display($display);
-
-  $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;
-}
-
-/**
- * For external use: Given a layout ID and a $content array, return the
- * panel display. The content array is filled in based upon the content
- * available in the layout. If it's a two column with a content
- * array defined like array('left' => t('Left side'), 'right' =>
- * t('Right side')), then the $content array should be array('left' =>
- * $output_left, 'right' => $output_right)
- * @render
- */
-function _panels_print_layout($id, $content) {
-  $layout = panels_get_layout($id);
-  if (!$layout) {
-    return;
-  }
-
-  return panels_render_layout($layout, $content);
-}
-
-/**
- * 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
@@ -121,95 +29,16 @@ function panels_render_layout_admin($layout, $content, $display) {
   }
 
   $theme = isset($layout['admin theme']) ? $layout['admin theme'] : $layout['theme'];
-  return theme($theme, isset($display->css_id) ? $display->css_id : '', $content, $display->layout_settings, $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;
+  return theme($theme, isset($display->css_id) ? $display->css_id : '', $content, $display->layout_settings, $display, $layout);
 }
 
 /**
  * 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
@@ -231,20 +60,20 @@ function panels_render_pane($content, $pane, &$display) {
     }
   }
 
-  if (!empty($pane->style['style'])) {
-    $style = panels_get_style($pane->style['style']);
+  if (!empty($content->content)) {
+    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, $display);
+      if (isset($style) && isset($style['render pane'])) {
+        $output = theme($style['render pane'], $content, $pane, $display, $style);
 
-      // This could be null if no theme function existed.
-      if (isset($output)) {
-        return $output;
+        // 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, $display);
   }
@@ -274,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 a/includes/legacy.inc b/includes/legacy.inc
new file mode 100644 (file)
index 0000000..5ba90b5
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+// $Id$
+
+/**
+ * Legacy state manager for Panels.
+ *
+ * Checks all possible ways (using discovery of patterned method names) in which
+ * Panels may need to operate in legacy mode,
+ * sets variables as appropriate, and returns an informational
+ *
+ */
+class PanelsLegacyState {
+  var $legacy = NULL;
+
+  function t() {
+    $func = get_t();
+    $args = func_get_args();
+    return call_user_func_array($func, $args);
+  }
+
+  function getStatus() {
+    if (!isset($this->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 a/includes/page-wizard.inc b/includes/page-wizard.inc
new file mode 100644 (file)
index 0000000..e6f7012
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Contains helper functions for the Panels page wizards.
+ */
+function panels_page_wizard_add_layout(&$form, &$form_state) {
+  $form_state['allowed_layouts'] = 'panels_page';
+  $form_state['display'] = $form_state['cache']->display;
+
+  // Tell the Panels form not to display buttons.
+  $form_state['no buttons'] = TRUE;
+
+  // Change the #id of the form so the CSS applies properly.
+  $form['#id'] = 'panels-choose-layout';
+
+  $form['layout_prefix'] = array(
+    '#value' => '<fieldset><legend>' . t('Layout') . '</legend>',
+  );
+
+  ctools_include('common', 'panels');
+  ctools_include('display-layout', 'panels');
+  ctools_include('plugins', 'panels');
+
+  $form = array_merge($form, panels_choose_layout($form_state));
+
+  $form['layout_suffix'] = array(
+    '#value' => '</fieldset>',
+  );
+}
+
+function panels_page_wizard_add_content(&$form, &$form_state) {
+  ctools_include('ajax');
+  ctools_include('plugins', 'panels');
+  ctools_include('common', 'panels');
+  ctools_include('display-edit', 'panels');
+
+  // Panels provides this caching mechanism to make it easy to use the
+  // wizard to cache the display.
+
+  $cache = panels_edit_cache_get('panels_page_wizard:node_override');
+
+  $form_state['renderer'] = panels_get_renderer_handler('editor', $cache->display);
+  $form_state['renderer']->cache = &$cache;
+
+  $form_state['display'] = &$cache->display;
+  $form_state['content_types'] = $cache->content_types;
+  // Tell the Panels form not to display buttons.
+  $form_state['no buttons'] = TRUE;
+  $form_state['display_title'] = !empty($cache->display_title);
+
+  $form = array_merge($form, panels_edit_display_form($form_state));
+  // Make sure the theme will work since our form id is different.
+  $form['#theme'] = 'panels_edit_display_form';
+}
+
+function panels_page_wizard_add_content_submit(&$form, &$form_state) {
+  // Call the normal panels edit form submit to make sure values are stored
+  // on the display
+  panels_edit_display_form_submit($form, $form_state);
+  $cache = &$form_state['cache'];
+
+  // Copy the "being edited" cached display to the "actual" cached display.
+  $cache->display = &$form_state['display'];
+  unset($cache->display_cache);
+}
+
diff --git a/includes/panels.pipelines.inc b/includes/panels.pipelines.inc
new file mode 100644 (file)
index 0000000..7e86d6c
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Bulk export of panels_layouts objects generated by Bulk export module.
+ */
+
+/**
+ * Implementation of hook_default_panels_layout()
+ */
+function panels_default_panels_renderer_pipeline() {
+  $pipelines = array();
+
+  $pipeline = new stdClass;
+  $pipeline->disabled = FALSE; /* Edit this to true to make a default pipeline disabled initially */
+  $pipeline->api_version = 1;
+  $pipeline->name = 'standard';
+  $pipeline->admin_title = t('Standard');
+  $pipeline->admin_description = t('Renders a panel normally. This is the most common option.');
+  $pipeline->weight = -100;
+  $pipeline->settings = array(
+    'renderers' => array(
+      0 => array(
+        'access' => array(),
+        'renderer' => 'standard',
+        'options' => array(),
+      ),
+    ),
+  );
+  $pipelines[$pipeline->name] = $pipeline;
+
+  return $pipelines;
+}
index b06f945..85fa282 100644 (file)
@@ -23,9 +23,9 @@ function panels_pane_access($pane, $display) {
 /**
  * Get a list of panels available in the layout.
  */
-function panels_get_panels($layout, $display) {
+function panels_get_regions($layout, $display) {
   if (!empty($layout['panels function']) && function_exists($layout['panels function'])) {
-    return $layout['panels function']($display, $display->layout_settings);
+    return $layout['panels function']($display, $display->layout_settings, $layout);
   }
   if (!empty($layout['panels'])) {
     return $layout['panels'];
@@ -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->content = $cache->content;
-    }
-  }
-
-  return $content;
-}
-
-/**
  * Get cached content for a given display and possibly pane.
  *
  * @return
@@ -298,26 +254,12 @@ function panels_get_pane_title(&$pane, $context = array(), $incoming_content = N
 }
 
 /**
- * Select a context for a pane.
- *
- * @param $pane
- *   A fully populated pane.
- * @param $contexts
- *   A keyed array of available contexts.
- *
- * @return
- *   The matching contexts or NULL if none or necessary, or FALSE if
- *   requirements can't be met.
- */
-function panels_pane_select_context($pane, $contexts) {
-  return ctools_content_select_context($pane->type, $pane->subtype, $pane->configuration, $contexts);
-}
-
-/**
  * Fetch metadata on a specific layout plugin.
  *
  * @param $layout
- *   Name of a panel layout.
+ *   Name of a panel layout. If the layout name contains a ':' this
+ *   indicates that we need to separate the sublayout out and
+ *   load it individually.
  *
  * @return
  *   An array with information about the requested panel layout.
@@ -339,6 +281,29 @@ function panels_get_layouts() {
 }
 
 /**
+ * Fetch metadata for all layout plugins that provide builders.
+ *
+ * The layout builders allow reusable layouts be stored in the database and
+ * exported. Since there are different methods, we are not limiting this
+ * to just one plugin.
+ *
+ * @return
+ *   An array of arrays with information about panel layouts with builders.
+ */
+function panels_get_layout_builders() {
+  ctools_include('plugins');
+  $plugins = ctools_get_plugins('panels', 'layouts');
+  $builders = array();
+  foreach ($plugins as $name => $plugin) {
+    if (!empty($plugin['builder'])) {
+      $builders[$name] = $plugin;
+    }
+  }
+
+  return $builders;
+}
+
+/**
  * Fetch metadata on a specific style plugin.
  *
  * @param $style
@@ -389,6 +354,131 @@ 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($renderer) {
+  ctools_include('plugins');
+  return ctools_get_plugins('panels', 'display_renderers', $renderer);
+}
+
+/**
+ * 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 and initialize the class to handle rendering a display.
+ *
+ * @return
+ *   Either the instantiated renderer or FALSE if one could not be found.
+ */
+function panels_get_renderer_handler($plugin, &$display) {
+  if (is_string($plugin)) {
+    $plugin = panels_get_display_renderer($plugin);
+  }
+
+  $class = ctools_plugin_get_class($plugin, 'handler');
+  if ($class) {
+    $renderer = new $class();
+    $renderer->init($plugin, $display);
+    return $renderer;
+  }
+
+  return FALSE;
+}
+
+/**
+ * Choose a renderer for a display based on a render pipeline setting.
+ */
+function panels_get_renderer($pipeline_name, &$display) {
+  // If operating in legacy mode, only the legacy renderer is available:
+  if (variable_get('panels_legacy_rendering_mode', TRUE)) {
+    return panels_get_renderer_handler('legacy', $display);
+  }
+
+  // Load the pipeline
+  ctools_include('export');
+  $pipeline = ctools_export_crud_load('panels_renderer_pipeline', $pipeline_name);
+
+  // If we can't, or it has no renderers, default.
+  if (!$pipeline || empty($pipeline->settings['renderers'])) {
+    return panels_get_renderer_handler('standard', $display);
+  }
+
+  // Get contexts set on the pipeline:
+  $contexts = array();
+  if (!empty($pipeline->settings['contexts'])) {
+    $contexts = ctools_context_load_contexts($pipeline->settings['context']);
+  }
+
+  // Cycle through our renderers and see.
+  foreach ($pipeline->settings['renderers'] as $candidate) {
+    // See if this passes selection criteria.
+    if (!ctools_access($candidate['access'], $contexts)) {
+      continue;
+    }
+
+    $renderer = panels_get_renderer_handler($candidate['renderer'], $display);
+
+    if (!empty($candidate['options'])) {
+      $renderer->set_options($candidate['options']);
+    }
+
+    return $renderer;
+  }
+
+  // Fall through. If no renderer is selected, use the standard renderer
+  return panels_get_renderer_handler('standard', $display);
+}
+
+/**
+ * Sort callback for sorting renderer pipelines.
+ *
+ * Sort first by weight, then by title.
+ */
+function _panels_renderer_pipeline_sort($a, $b) {
+  if ($a->weight == $b->weight) {
+      if ($a->admin_title == $b->admin_title) {
+        return 0;
+      }
+    return ($a->admin_title < $b->admin_title) ? -1 : 1;
+  }
+  return ($a->weight < $b->weight) ? -1 : 1;
+}
+
+/**
+ * Get a list of available renderer pipelines.
+ *
+ * This can be used to form a select or radios widget by enabling
+ * sorting. Descriptions are left in.
+ */
+function panels_get_renderer_pipelines($sort = TRUE) {
+  // If operating in legacy mode, only the legacy renderer is available:
+  if (variable_get('panels_legacy_rendering_mode', TRUE)) {
+    return array();
+  }
+
+  ctools_include('export');
+  $pipelines = ctools_export_crud_load_all('panels_renderer_pipeline');
+  if ($sort) {
+    uasort($pipelines, '_panels_renderer_pipeline_sort');
+  }
+
+  return $pipelines;
+}
+
+/**
  * Get a function from a plugin, if it exists.
  *
  * @param $plugin
@@ -414,6 +504,7 @@ function panels_plugin_get_function($plugin, $which, $function_name) {
 
 }
 
+// @todo these are DEPRECATED and can probably be removed.
 // These are placeholders to prevent crashes from the former plugins
 class panels_required_context { function filter() { } };
 class panels_optional_context extends panels_required_context {};
index ce94921..025aeb6 100644 (file)
       }
       draggable.timeCount = draggable.timeCount + 1;
       var left = $(window).scrollLeft();
-      var right = left + $(window).innerWidth();
+      var right = left + $(window).width();
       var top = $(window).scrollTop();
-      var bottom = top + $(window).innerHeight();
+      var bottom = top + $(window).height();
 
       if (draggable.mousePos.x < left + scrollBuffer && left > 0) {
         window.scrollTo(left - scrollDistance, top);
     // Show javascript only items.
     $('span#panels-js-only').css('display', 'inline');
 
-    $('div.panel-pane:not(.panel-portlet)', context)
+    $('#panels-dnd-main div.panel-pane:not(.panel-portlet)')
       .addClass('panel-portlet')
       .each(Drupal.Panels.bindPortlet);
 
index e7a0cef..7d350c9 100644 (file)
@@ -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,12 +43,173 @@ 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'),
+      'value' => NULL,
+      '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");
+
+    // Add the reasons why Panels is acting in legacy mode.
+    $list = array();
+    foreach ($state as $values) {
+      $modules = array();
+      foreach ($values['modules'] as $module => $type) {
+        $modules[] = array('data' => check_plain($module) . ' - ' . $type);
+      }
+
+      $list[] = array('data' => $values['explanation'] ."\n" . theme('item_list', $modules));
+    }
+
+    $description .= theme('item_list', $list);
+
+    $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() {
   // This should always point to our 'current' schema. This makes it relatively easy
   // to keep a record of schema as we make changes to it.
-  return panels_schema_2();
+  return panels_schema_3();
+}
+
+/**
+ * Schema that adds the panels_layout table.
+ */
+function panels_schema_3() {
+  // Schema 3 is now locked. If you need to make changes, please create
+  // schema 4 and add them.
+  $schema = panels_schema_2();
+
+  $schema['panels_renderer_pipeline'] = array(
+    'description' => 'Contains renderer pipelines for Panels. Each pipeline contains one or more renderers and access rules to select which renderer gets used.',
+    'export' => array(
+      'identifier' => 'pipeline',
+      'bulk export' => TRUE,
+      'primary key' => 'rpid',
+      'api' => array(
+        'owner' => 'panels',
+        'api' => 'pipelines',
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+    ),
+    'fields' => array(
+      'rpid' => array(
+        'type' => 'serial',
+        'description' => 'A database primary key to ensure uniqueness.',
+        'not null' => TRUE,
+        'no export' => TRUE,
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'Unique ID for this content. Used to identify it programmatically.',
+      ),
+      'admin_title' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'Administrative title for this pipeline.',
+      ),
+      'admin_description' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'description' => 'Administrative description for this pipeline.',
+        'object default' => '',
+      ),
+      'weight' => array(
+        'type' => 'int',
+        'size' => 'small',
+        'default' => 0,
+      ),
+      'settings' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'description' => 'Serialized settings for the actual pipeline. The contents of this field are up to the plugin that uses it.',
+        'serialize' => TRUE,
+        'object default' => array(),
+      ),
+    ),
+    'primary key' => array('rpid'),
+  );
+
+  $schema['panels_layout'] = array(
+    'description' => 'Contains exportable customized layouts for this site.',
+    'export' => array(
+      'identifier' => 'layout',
+      'bulk export' => TRUE,
+      'primary key' => 'lid',
+      'api' => array(
+        'owner' => 'panels',
+        'api' => 'layouts',
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+    ),
+    'fields' => array(
+      'lid' => array(
+        'type' => 'serial',
+        'description' => 'A database primary key to ensure uniqueness.',
+        'not null' => TRUE,
+        'no export' => TRUE,
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'Unique ID for this content. Used to identify it programmatically.',
+      ),
+      'admin_title' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'Administrative title for this layout.',
+      ),
+      'admin_description' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'description' => 'Administrative description for this layout.',
+        'object default' => '',
+      ),
+      'category' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'Administrative category for this layout.',
+      ),
+      'plugin' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'description' => 'The layout plugin that owns this layout.',
+      ),
+      'settings' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'description' => 'Serialized settings for the actual layout. The contents of this field are up to the plugin that uses it.',
+        'serialize' => TRUE,
+        'object default' => array(),
+      ),
+    ),
+    'primary key' => array('lid'),
+  );
+
+  return $schema;
 }
 
 /**
@@ -399,7 +568,7 @@ function panels_update_5209() {
   // This code removed due to call to panels_get_argument(). People with
   // older versions will just have to suffer.
   return $ret;
-  panels_load_include('plugins');
+  ctools_include('plugins', 'panels');
 
   while ($page = db_fetch_object($result)) {
     $args = unserialize($page->arguments);
@@ -427,7 +596,6 @@ function panels_update_5209() {
         // Give it an identifier if it doesn't already have one
         if (empty($argument['identifier'])) {
           $argument['identifier'] = $info['title'] . ($id > 1 ? ' ' . $id : '');
-          error_log($argument['identifier']);
         }
 
         // Give it a unique keyword if it doesn't already have one
@@ -439,7 +607,6 @@ function panels_update_5209() {
           }
           $keywords[$keyword] = TRUE;
           $argument['keyword'] = $keyword;
-          error_log($argument['keyword']);
         }
         $arguments[$id] = $argument;
       }
@@ -1289,3 +1456,53 @@ function panels_update_6306() {
 
   return $ret;
 }
+
+/**
+ * This update function does nothing, it was committed in error and is
+ * left in to prevent update problems.
+ */
+function panels_update_6307() {
+  return array();
+}
+
+/**
+ * Add the panels_layout table
+ */
+function panels_update_6308() {
+  $ret = array();
+
+  // Schema 3 is locked and should not be changed.
+  $schema = panels_schema_3();
+
+  db_create_table($ret, 'panels_layout', $schema['panels_layout']);
+  return $ret;
+}
+
+/**
+ * Add the panels_renderer_pipeline table
+ */
+function panels_update_6309() {
+  $ret = array();
+
+  // Schema 3 is locked and should not be changed.
+  $schema = panels_schema_3();
+
+  db_create_table($ret, 'panels_renderer_pipeline', $schema['panels_renderer_pipeline']);
+  return $ret;
+}
+
+/**
+ * Move stylizer data from Panels to CTools.
+ */
+function panels_update_6310() {
+  $ret = array();
+  // Safety: go away if CTools is not at an appropriate version.
+  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+    $ret['#abort'] = array('success' => FALSE, 'query' => t('Panels cannot be updated because CTools 1.7 (API v1.7.2) is required. Please update CTools and then try update.php again.'));
+    return $ret;
+  }
+
+  // Enable the stylizer module to make everything as seamless as possible.
+  drupal_install_modules(array('stylizer'));
+  return $ret;
+}
index e5777f5..bd74c68 100644 (file)
@@ -7,7 +7,7 @@
  * Core functionality for the Panels engine.
  */
 
-define('PANELS_REQUIRED_CTOOLS_API', '1.1.1');
+define('PANELS_REQUIRED_CTOOLS_API', '1.8');
 
 define('PANELS_TITLE_FIXED', 0); // Hide title use to be true/false. So false remains old behavior.
 define('PANELS_TITLE_NONE', 1); // And true meant no title.
@@ -19,9 +19,12 @@ 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);
 }
 
+// --------------------------------------------------------------------------
+// Core Drupal hook implementations
+
 /**
  * Implementation of hook_theme()
  */
@@ -38,14 +41,6 @@ function panels_theme() {
   $theme['panels_layout_icon'] = array(
     'arguments' => array('id', 'image', 'title' => NULL),
   );
-  $theme['panels_edit_display_form'] = array(
-    'arguments' => array('form'),
-    'file' => 'includes/display-edit.inc',
-  );
-  $theme['panels_edit_layout_form_choose'] = array(
-    'arguments' => array('form'),
-    'file' => 'includes/display-edit.inc',
-  );
   $theme['panels_pane'] = array(
     'arguments' => array('output' => array(), 'pane' => array(), 'display' => array()),
     'path' => drupal_get_path('module', 'panels') . '/templates',
@@ -66,8 +61,27 @@ function panels_theme() {
     'template' => 'panels-dashboard',
   );
 
+  $theme['panels_dashboard_link'] = array(
+    'arguments' => array('link' => array()),
+    'path' => drupal_get_path('module', 'panels') . '/templates',
+    'file' => '../includes/callbacks.inc',
+    'template' => 'panels-dashboard-link',
+  );
+
+  $theme['panels_dashboard_block'] = array(
+    'arguments' => array('block' => array()),
+    'path' => drupal_get_path('module', 'panels') . '/templates',
+    'file' => '../includes/callbacks.inc',
+    'template' => 'panels-dashboard-block',
+  );
+
+  // We don't need layout and style themes in maintenance mode.
+  if (defined('MAINTENANCE_MODE')) {
+    return $theme;
+  }
+
   // Register layout and style themes on behalf of all of these items.
-  panels_load_include('plugins');
+  ctools_include('plugins', 'panels');
 
   // No need to worry about files; the plugin has to already be loaded for us
   // to even know what the theme function is, so files will be auto included.
@@ -76,8 +90,9 @@ function panels_theme() {
     foreach (array('theme', 'admin theme') as $callback) {
       if (!empty($data[$callback])) {
         $theme[$data[$callback]] = array(
-          'arguments' => array('css_id' => NULL, 'content' => NULL, 'settings' => NULL, 'display' => NULL),
+          'arguments' => array('css_id' => NULL, 'content' => NULL, 'settings' => NULL, 'display' => NULL, 'layout' => NULL, 'renderer' => NULL),
           'path' => $data['path'],
+          'file' => $data['file'],
         );
 
         // if no theme function exists, assume template.
@@ -93,12 +108,26 @@ function panels_theme() {
   foreach ($styles as $name => $data) {
     if (!empty($data['render pane'])) {
       $theme[$data['render pane']] = array(
-        'arguments' => array('output' => NULL, 'pane' => NULL, 'display' => NULL),
+        'arguments' => array('output' => NULL, 'pane' => NULL, 'display' => NULL, 'style' => NULL),
+        'path' => $data['path'],
+        'file' => $data['file'],
       );
     }
-    if (!empty($data['render panel'])) {
-      $theme[$data['render panel']] = array(
-        'arguments' => array('display' => NULL, 'panel_id' => NULL, 'panes' => NULL, 'settings' => NULL),
+    // If we're in legacy mode, include the old callback key for legacy styles.
+    if (variable_get('panels_legacy_rendering_mode', TRUE)) {
+      if (!empty($data['render panel'])) {
+        $theme[$data['render panel']] = array(
+          'arguments' => array('display' => NULL, 'panel_id' => NULL, 'panes' => NULL, 'settings' => NULL, 'style' => NULL),
+          'path' => $data['path'],
+          'file' => $data['file'],
+        );
+      }
+    }
+    if (!empty($data['render region'])) {
+      $theme[$data['render region']] = array(
+        'arguments' => array('display' => NULL, 'region_id' => NULL, 'panes' => NULL, 'settings' => NULL, 'style' => NULL),
+        'path' => $data['path'],
+        'file' => $data['file'],
       );
     }
 
@@ -125,69 +154,13 @@ function panels_menu() {
   }
   $items = array();
 
-  // Provide some common options to reduce code repetition.
-  // By using array addition and making sure these are the rightmost
-  // value, they won't override anything already set.
-  $base = array(
+  // Base AJAX router callback.
+  $items['panels/ajax'] = array(
     'access arguments' => array('access content'),
+    'page callback' => 'panels_ajax_router',
     'type' => MENU_CALLBACK,
-    'file' => 'includes/display-edit.inc',
-    'page arguments' => array(3),
   );
 
-  $items['panels/ajax/add-pane/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_add_pane_choose',
-  ) + $base;
-  $items['panels/ajax/add-pane-config/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_add_pane_config',
-  ) + $base;
-  $items['panels/ajax/configure/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_configure_pane',
-  ) + $base;
-  $items['panels/ajax/show/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_toggle_shown',
-    'page arguments' => array('show', 3),
-  ) + $base;
-  $items['panels/ajax/hide/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_toggle_shown',
-    'page arguments' => array('hide', 3),
-  ) + $base;
-  $items['panels/ajax/cache-method/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_cache_method',
-  ) + $base;
-  $items['panels/ajax/cache-settings/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_cache_settings',
-  ) + $base;
-  $items['panels/ajax/display-settings/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_display_settings',
-  ) + $base;
-  $items['panels/ajax/panel-title/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_set_display_title',
-  ) + $base;
-  $items['panels/ajax/style-type/%/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_style_type',
-    'page arguments' => array(3, 4),
-  ) + $base;
-  $items['panels/ajax/style-settings/%/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_style_settings',
-    'page arguments' => array(3, 4),
-  ) + $base;
-  $items['panels/ajax/pane-css/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_configure_pane_css',
-  ) + $base;
-  $items['panels/ajax/access-settings/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_configure_access_settings',
-  ) + $base;
-  $items['panels/ajax/access-test/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_configure_access_test',
-  ) + $base;
-  $items['panels/ajax/access-add/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_add_access_test',
-  ) + $base;
-  $items['panels/ajax/preview/%panels_edit_cache'] = array(
-    'page callback' => 'panels_ajax_preview',
-  ) + $base;
-
   $admin_base = array(
     'file' => 'includes/callbacks.inc',
     'access arguments' => array('use panels dashboard'),
@@ -196,7 +169,7 @@ function panels_menu() {
   $items['admin/build/panels'] = array(
     'title' => 'Panels',
     'page callback' => 'panels_admin_page',
-    'description' => 'Administer items related to the Panels module.',
+    'description' => 'Get a bird\'s eye view of items related to Panels.',
   ) + $admin_base;
 
   $items['admin/build/panels/dashboard'] = array(
@@ -231,7 +204,7 @@ function panels_menu() {
     ) + $admin_base;
   }
 
-  panels_load_include('plugins');
+  ctools_include('plugins', 'panels');
   $layouts = panels_get_layouts();
   foreach ($layouts as $name => $data) {
     if (!empty($data['hook menu'])) {
@@ -244,6 +217,7 @@ function panels_menu() {
     }
   }
 
+
   return $items;
 }
 
@@ -254,8 +228,8 @@ function panels_menu() {
  * cache object and makes sure it is valid.
  */
 function panels_edit_cache_load($cache_key) {
-  panels_load_include('display-edit');
-  panels_load_include('plugins');
+  ctools_include('display-edit', 'panels');
+  ctools_include('plugins', 'panels');
   ctools_include('ajax');
   ctools_include('modal');
   ctools_include('context');
@@ -269,30 +243,14 @@ function panels_edit_cache_load($cache_key) {
 function panels_init() {
   // Safety: go away if CTools is not at an appropriate version.
   if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+    if (user_access('administer site configuration')) {
+      drupal_set_message(t('Panels is enabled but CTools is out of date. All Panels modules are disabled until CTools is updated. See the status page for more information.'), 'error');
+    }
     return;
   }
 
-  drupal_add_css(panels_get_path('css/panels.css'));
-  drupal_add_js(panels_get_path('js/panels.js'));
-}
-
-/**
- * Load a panels include file.
- */
-function panels_load_include($include, $path = 'includes/') {
-  static $loaded = array();
-  if (empty($loaded["$path$include.inc"])) {
-    require_once './' . panels_get_path("$path$include.inc");
-    $loaded["$path$include.inc"] = TRUE;
-  }
-}
-
-/**
- * panels path helper function
- */
-function panels_get_path($file, $base_path = FALSE, $module = 'panels') {
-  $output = $base_path ? base_path() : '';
-  return $output . drupal_get_path('module', $module) . '/' . $file;
+  ctools_add_css('panels', 'panels');
+  ctools_add_js('panels', 'panels');
 }
 
 /**
@@ -305,40 +263,198 @@ function panels_perm() {
     'administer pane visibility',
     'administer pane access',
     'administer advanced pane settings',
+    'administer panels layouts',
     'use panels caching features',
     'use panels dashboard',
+    'use panels in place editing',
   );
 }
 
 /**
- * Get an object from cache.
+ * 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_cache_get($obj, $did, $skip_cache = FALSE) {
-  ctools_include('object-cache');
-  // we often store contexts in cache, so let's just make sure we can load
-  // them.
-  ctools_include('context');
-  return ctools_object_cache_get($obj, 'panels_display:' . $did, $skip_cache);
+function panels_flush_caches() {
+  $legacy = panels_get_legacy_state();
+  $legacy->determineStatus();
 }
 
+// ---------------------------------------------------------------------------
+// CTools hook implementations
+//
+// These aren't core Drupal hooks but they are just as important.
+
 /**
- * Save the edited object into the cache.
+ * Implementation of hook_ctools_plugin_directory() to let the system know
+ * we implement task and task_handler plugins.
  */
-function panels_cache_set($obj, $did, $cache) {
-  ctools_include('object-cache');
-  return ctools_object_cache_set($obj, 'panels_display:' . $did, $cache);
+function panels_ctools_plugin_directory($module, $plugin) {
+  // Safety: go away if CTools is not at an appropriate version.
+  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+    return;
+  }
+  if ($module == 'page_manager' || $module == 'panels' || $module == 'ctools') {
+    return 'plugins/' . $plugin;
+  }
 }
 
 /**
- * Clear a object from the cache; used if the editing is aborted.
+ * Inform CTools that the layout plugin can be loaded from themes.
  */
-function panels_cache_clear($obj, $did) {
-  ctools_include('object-cache');
-  return ctools_object_cache_clear($obj, 'panels_display:' . $did);
+function panels_ctools_plugin_layouts() {
+  return array(
+    'load themes' => TRUE,
+    'use hooks' => TRUE,
+    'process' => 'panels_layout_process',
+    'child plugins' => TRUE,
+  );
+}
+
+/**
+ * Ensure a layout has a minimal set of data.
+ */
+function panels_layout_process(&$plugin) {
+  $plugin += array(
+    'category' => t('Miscellaneous'),
+    'description' => '',
+  );
+}
+
+/**
+ * Inform CTools that the style plugin can be loaded from themes.
+ */
+function panels_ctools_plugin_styles() {
+  return array(
+    'load themes' => TRUE,
+    'use hooks' => TRUE,
+    'process' => 'panels_plugin_styles_process',
+    'child plugins' => TRUE,
+  );
+}
+
+/**
+ * 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);
+  }
+
+  if ($owner == 'panels' && $api == 'pipelines') {
+    return array(
+      'version' => 1,
+      'path' => drupal_get_path('module', 'panels') . '/includes',
+    );
+  }
+}
+
+/**
+ * Implementation of hook_views_api().
+ */
+function panels_views_api() {
+  return array(
+    'api' => 2,
+    'path' => drupal_get_path('module', 'panels') . '/plugins/views',
+  );
+}
+
+/**
+ * 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) {
+  $plugin += array(
+    'weight' => 0,
+  );
+
+  $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'];
+}
+
+/**
+ * Declare what style types Panels uses.
+ */
+function panels_ctools_style_base_types() {
+  return array(
+    'region' => array(
+      'title' => t('Panel region'),
+      'preview' => 'panels_stylizer_region_preview',
+      'theme arguments' => array('settings' => NULL, 'classes' => NULL, 'content' => NULL),
+    ),
+    'pane' => array(
+      'title' => t('Panel pane'),
+      'preview' => 'panels_stylizer_pane_preview',
+      'theme arguments' => array('settings' => NULL, 'content' => NULL, 'pane' => NULL, 'display' => NULL),
+    ),
+  );
+}
+
+function panels_stylizer_lipsum() {
+  return "
+    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at velit dolor. Donec egestas tellus sit amet urna rhoncus adipiscing. Proin nec porttitor sem. Maecenas aliquam, purus nec tempus dignissim, nulla arcu aliquam diam, non tincidunt massa ante vel dolor. Aliquam sapien sapien, tincidunt id tristique at, pretium sagittis libero.</p>
+
+    <p>Nulla facilisi. Curabitur lacinia, tellus sed tristique consequat, diam lorem scelerisque felis, at dictum purus augue facilisis lorem. Duis pharetra dignissim rutrum. Curabitur ac elit id dui dapibus tincidunt. Nulla eget sem quam, non eleifend eros. Cras porttitor tempus lectus ac scelerisque. Curabitur vehicula bibendum lorem, vitae ornare ligula venenatis ut.</p>
+  ";
+}
+
+/**
+ * Generate a preview given the current settings.
+ */
+function panels_stylizer_region_preview($plugin, $settings) {
+  ctools_stylizer_add_css($plugin, $settings);
+  return theme($plugin['theme'], $settings, ctools_stylizer_get_css_class($plugin, $settings), panels_stylizer_lipsum());
+}
+
+/**
+ * Generate a preview given the current settings.
+ */
+function panels_stylizer_pane_preview($plugin, $settings) {
+  ctools_stylizer_add_css($plugin, $settings);
+  $pane = new stdClass();
+
+  $content = new stdClass;
+  $content->title = t('Lorem ipsum');
+  $content->content = panels_stylizer_lipsum();
+  $content->type = 'dummy';
+  $content->subtype = 'dummy';
+
+  $content->css_class = ctools_stylizer_get_css_class($plugin, $settings);
+
+  $display = new panels_display();
+
+  if (!empty($plugin['theme'])) {
+    return theme($plugin['theme'], $settings, $content, $pane, $display);
+  }
+  else {
+    return theme('panels_pane', $content, $pane, $display);
+  }
 }
 
 // ---------------------------------------------------------------------------
-// panels display editing
+// Panels display editing
 
 /**
  * @defgroup mainapi Functions comprising the main panels API
@@ -404,9 +520,9 @@ function panels_cache_clear($obj, $did) {
  *        given in $destination and NO value will be returned.
  */
 function panels_edit($display, $destination = NULL, $content_types = NULL, $title = FALSE) {
-  panels_load_include('display-edit');
+  ctools_include('display-edit', 'panels');
   ctools_include('ajax');
-  panels_load_include('plugins');
+  ctools_include('plugins', 'panels');
   return _panels_edit($display, $destination, $content_types, $title);
 }
 
@@ -449,13 +565,13 @@ function panels_edit($display, $destination = NULL, $content_types = NULL, $titl
  * @see panels_edit()
  */
 function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) {
-  panels_load_include('display-layout');
-  panels_load_include('plugins');
+  ctools_include('display-layout', 'panels');
+  ctools_include('plugins', 'panels');
   return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
 }
 
 // ---------------------------------------------------------------------------
-// panels database functions
+// Panels database functions
 
 /**
  * Forms the basis of a panel display
@@ -469,19 +585,29 @@ class panels_display {
   var $css_id = NULL;
   var $context = array();
   var $did = 'new';
+  var $renderer;
 
-  function add_pane($pane, $location = FALSE) {
-    $pane->pid = $this->next_new_pid();
-    if (!$location || !isset($this->panels[$location])) {
-      foreach ($this->panels as $panel_name => $panel) {
-        if (array_key_exists($pane->pid, $panel)) {
-          $this->panels[$panel_name][] = $pane->pid;
-        }
-      }
+  function panels_display() {
+    // Set the default renderer to either the legacy or the standard renderer,
+    // depending on the legacy rendering state
+    $this->renderer = variable_get('panels_legacy_rendering_mode', TRUE) ? 'legacy' : 'standard';
+  }
+
+  function add_pane(&$pane, $location = NULL) {
+    // If no location specified, use what's set in the pane.
+    if (empty($location)) {
+      $location = $pane->panel;
     }
     else {
-      $this->panels[$location][] = $pane->pid;
+      $pane->panel = $location;
     }
+
+    // Get a temporary pid for this pane.
+    $pane->pid = "new-" . $this->next_new_pid();
+
+    // Add the pane to the approprate spots.
+    $this->content[$pane->pid] = &$pane;
+    $this->panels[$location][] = $pane->pid;
   }
 
   function duplicate_pane($pid, $location = FALSE) {
@@ -491,16 +617,13 @@ class panels_display {
 
   function clone_pane($pid) {
     $pane = drupal_clone($this->content[$pid]);
-    foreach (array_keys($this->content) as $pidcheck) {
-      // necessary?
-      unset($pane->position);
-    }
     return $pane;
   }
 
   function next_new_pid() {
-    // necessary if/until we use this method and ONLY this method for adding temporary pids.
-    // then we can do it with a nice static var.
+    // We don't use static vars to record the next new pid because
+    // temporary pids can last for years in exports and in caching
+    // during editing.
     $id = array(0);
     foreach (array_keys($this->content) as $pid) {
       if (!is_numeric($pid)) {
@@ -510,43 +633,84 @@ class panels_display {
     $next_id = max($id);
     return ++$next_id;
   }
-}
-
-/**
- * }@ End of 'defgroup mainapi', although other functions are specifically added later
- */
 
-/**
- * Clean up a display object and add some required information, if missing.
- *
- * Currently a display object needs 'args', 'incoming content', 'context'
- * and a 'css_id'.
- *
- * @param &$display
- *   The display object to be sanitized.
- * @return
- *   The sanitized display object.
- */
-function panels_sanitize_display(&$display) {
-  return;
-  if (!isset($display->args)) {
-    $display->args = array();
+  /**
+   * 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;
+    }
   }
 
-  if (!isset($display->incoming_content)) {
-    $display->incoming_content = 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 instantiated 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 (!isset($display->context)) {
-    $display->context = array();
-  }
+    // If we were not given a renderer object, load it.
+    if (!is_object($renderer)) {
+      // If the renderer was not specified, default to $this->renderer
+      // which is either standard or was already set for us.
+      $renderer = panels_get_renderer_handler(!empty($renderer) ? $renderer : $this->renderer, $this);
+      if (!$renderer) {
+        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($this, $renderer);
+    }
+
+    $output .= $renderer->render();
 
-  if (!isset($display->css_id)) {
-    $display->css_id = NULL;
+    // Let modules act just after render.
+    foreach (module_implements('panels_post_render') as $module) {
+      $function = $module . '_panels_post_render';
+      $output .= $function($this, $renderer);
+    }
+    return $output;
   }
 }
 
 /**
+ * }@ End of 'defgroup mainapi', although other functions are specifically added later
+ */
+
+/**
  * Creates a new display, setting the ID to our magic new id.
  */
 function panels_new_display() {
@@ -561,12 +725,18 @@ function panels_new_display() {
  *
  * @todo -- use schema API for some of this?
  */
-function panels_new_pane($type, $subtype) {
+function panels_new_pane($type, $subtype, $set_defaults = FALSE) {
   ctools_include('export');
   $pane = ctools_export_new_object('panels_pane', FALSE);
   $pane->pid = 'new';
   $pane->type = $type;
   $pane->subtype = $subtype;
+  if ($set_defaults) {
+    $content_type = ctools_get_content_type($type);
+    $content_subtype = ctools_content_get_subtype($content_type, $subtype);
+    $pane->configuration = ctools_content_get_defaults($content_type, $content_subtype);
+  }
+
   return $pane;
 }
 
@@ -673,7 +843,7 @@ function panels_save_display(&$display) {
   }
 
   // update all the panes
-  panels_load_include('plugins');
+  ctools_include('plugins', 'panels');
   ctools_include('content');
 
   foreach ($display->panels as $id => $panes) {
@@ -726,6 +896,19 @@ function panels_save_display(&$display) {
   // Clear any cached content for this display.
   panels_clear_cached_content($display);
 
+  // Allow other modules to take action when a display is saved.
+  module_invoke_all('panels_display_save', $display);
+
+  // Log the change to watchdog, using the same style as node.module
+  $watchdog_args = array('%did' => $display->did);
+  if (!empty($display->title)) {
+    $watchdog_args['%title'] = $display->title;
+    watchdog('content', 'Panels: saved display "%title" with display id %did', $watchdog_args, WATCHDOG_NOTICE);
+  }
+  else {
+    watchdog('content', 'Panels: saved display with id %did', $watchdog_args, WATCHDOG_NOTICE);
+  }
+
   // to be nice, even tho we have a reference.
   return $display;
 }
@@ -822,12 +1005,11 @@ 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) {
-  panels_load_include('display-render');
-  panels_load_include('plugins');
+function panels_render_display(&$display, $renderer = NULL) {
+  ctools_include('display-render', 'panels');
+  ctools_include('plugins', 'panels');
   ctools_include('context');
 
   if (!empty($display->context)) {
@@ -838,7 +1020,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);
 }
 
 /**
@@ -850,30 +1032,16 @@ function panels_render_display(&$display) {
  * 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);
 }
 
-/**
- * For external use: Given a layout ID and a $content array, return the
- * panel display. The content array is filled in based upon the content
- * available in the layout. If it's a two column with a content
- * array defined like array('left' => t('Left side'), 'right' =>
- * t('Right side')), then the $content array should be array('left' =>
- * $output_left, 'right' => $output_right)
- * @render
- */
-function panels_print_layout($id, $content) {
-  panels_load_include('plugins');
-  return _panels_print_layout($id, $content);
-}
-
 // @layout
 function panels_print_layout_icon($id, $layout, $title = NULL) {
-  drupal_add_css(panels_get_path('css/panels_admin.css'));
+  ctools_add_css('panels_admin', 'panels');
   $file = $layout['path'] . '/' . $layout['icon'];
-  return theme('panels_layout_icon', $id, theme('image', $file), $title);
+  return theme('panels_layout_icon', $id, theme('image', $file, strip_tags($layout['title']), strip_tags($layout['description'])), $title);
 }
 
 /**
@@ -912,43 +1080,27 @@ function panels_print_layout_link($id, $layout, $link, $options = array()) {
     unset($options['query']['q']);
   }
 
-  drupal_add_css(panels_get_path('css/panels_admin.css'));
+  ctools_add_css('panels_admin', 'panels');
   $file = $layout['path'] . '/' . $layout['icon'];
   $image = l(theme('image', $file), $link, array('html' => true) + $options);
   $title = l($layout['title'], $link, $options);
   return theme('panels_layout_link', $title, $id, $image, $link);
 }
 
-/**
- * Implementation of hook_ctools_plugin_directory() to let the system know
- * we implement task and task_handler plugins.
- */
-function panels_ctools_plugin_directory($module, $plugin) {
-  // Safety: go away if CTools is not at an appropriate version.
-  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
-    return;
-  }
-  if ($module == 'page_manager' || $module == 'panels') {
-    return 'plugins/' . $plugin;
-  }
-}
-
-/**
- * Inform CTools that the layout plugin can be loaded from themes.
- */
-function panels_ctools_plugin_layouts() {
-  return array(
-    'load themes' => TRUE,
-  );
-}
 
 /**
- * Inform CTools that the style plugin can be loaded from themes.
+ * 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_ctools_plugin_styles() {
-  return array(
-    'load themes' => TRUE,
-  );
+function panels_get_legacy_state() {
+  static $legacy = NULL;
+  if (!isset($legacy)) {
+    ctools_include('legacy', 'panels');
+    $legacy = new PanelsLegacyState();
+  }
+  return $legacy;
 }
 
 /**
@@ -970,13 +1122,283 @@ function panels_get_current_page_display($change = NULL) {
 }
 
 /**
+ * Clean up the panel pane variables for the template.
+ */
+function template_preprocess_panels_pane($vars) {
+  $content = $vars['output'];
+  // basic classes
+  $vars['classes'] = 'panel-pane';
+  $vars['id'] = '';
+
+  // Add some usable classes based on type/subtype
+  ctools_include('cleanstring');
+  $type_class = $content->type ? 'pane-'. ctools_cleanstring($content->type, array('lower case' => TRUE)) : '';
+  $subtype_class = $content->subtype ? 'pane-'. ctools_cleanstring($content->subtype, array('lower case' => TRUE)) : '';
+
+  // Sometimes type and subtype are the same. Avoid redudant classes.
+  if ($type_class != $subtype_class) {
+    $vars['classes'] .= " $type_class $subtype_class";
+  }
+  else {
+    $vars['classes'] .= " $type_class";
+  }
+
+  // Add id and custom class if sent in.
+  if (!empty($content->content)) {
+    if (!empty($content->css_id)) {
+      $vars['id'] = ' id="' . $content->css_id . '"';
+    }
+    if (!empty($content->css_class)) {
+      $vars['classes'] .= ' ' . $content->css_class;
+    }
+  }
+
+  // administrative links, only if there is permission.
+  $vars['admin_links'] = '';
+  if (user_access('view pane admin links') && !empty($content->admin_links)) {
+    $vars['admin_links'] = theme('links', $content->admin_links);
+  }
+
+  $vars['title'] = !empty($content->title) ? $content->title : '';
+
+  $vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : '';
+  $vars['content'] = !empty($content->content) ? $content->content : '';
+
+  $vars['links'] = !empty($content->links) ? theme('links', $content->links) : '';
+  $vars['more'] = '';
+  if (!empty($content->more)) {
+    if (empty($content->more['title'])) {
+      $content->more['title'] = t('more');
+    }
+    $vars['more'] = l($content->more['title'], $content->more['href'], $content->more);
+  }
+}
+
+/**
+ * Route Panels' AJAX calls to the correct object.
+ *
+ * Panels' AJAX is controlled mostly by renderer objects. This menu callback
+ * accepts the incoming request, figures out which object should handle the
+ * request, and attempts to route it. If no object can be found, the default
+ * Panels editor object is used.
+ *
+ * Calls are routed via the ajax_* method space. For example, if visiting
+ * panels/ajax/add-pane then $renderer::ajax_add_pane() will be called.
+ * This means commands can be added without having to create new callbacks.
+ *
+ * The first argument *must always* be the cache key so that a cache object
+ * can be passed through. Other arguments will be passed through untouched
+ * so that the method can do whatever it needs to do.
+ */
+function panels_ajax_router() {
+  $args = func_get_args();
+  if (count($args) < 3) {
+    return MENU_NOT_FOUND;
+  }
+
+  ctools_include('display-edit', 'panels');
+  ctools_include('plugins', 'panels');
+  ctools_include('ajax');
+  ctools_include('modal');
+  ctools_include('context');
+  ctools_include('content');
+
+  $plugin_name = array_shift($args);
+  $method = array_shift($args);
+  $cache_key = array_shift($args);
+
+  $plugin = panels_get_display_renderer($plugin_name);
+  if (!$plugin) {
+    // This is the default renderer for handling AJAX commands.
+    $plugin = panels_get_display_renderer('editor');
+  }
+
+  $cache = panels_edit_cache_get($cache_key);
+  if (empty($cache)) {
+    return MENU_ACCESS_DENIED;
+  }
+
+  $renderer = panels_get_renderer_handler($plugin, $cache->display);
+  if (!$renderer) {
+    return MENU_ACCESS_DENIED;
+  }
+
+  $method = 'ajax_' . str_replace('-', '_', $method);
+  if (!method_exists($renderer, $method)) {
+    return MENU_NOT_FOUND;
+  }
+
+  $renderer->cache = &$cache;
+  ctools_include('cleanstring');
+  $renderer->clean_key = ctools_cleanstring($cache_key);
+
+  $output = call_user_func_array(array($renderer, $method), $args);
+  if (empty($output) && !empty($renderer->commands)) {
+    return ctools_ajax_render($renderer->commands);
+  }
+  return $output;
+}
+
+// --------------------------------------------------------------------------
+// Panels caching functions and callbacks
+//
+// When editing displays and the like, Panels has a caching system that relies
+// on a callback to determine where to get the actual cache.
+
+// @todo This system needs to be better documented so that it can be
+// better used.
+
+/**
+ * Get an object from cache.
+ */
+function panels_cache_get($obj, $did, $skip_cache = FALSE) {
+  ctools_include('object-cache');
+  // we often store contexts in cache, so let's just make sure we can load
+  // them.
+  ctools_include('context');
+  return ctools_object_cache_get($obj, 'panels_display:' . $did, $skip_cache);
+}
+
+/**
+ * Save the edited object into the cache.
+ */
+function panels_cache_set($obj, $did, $cache) {
+  ctools_include('object-cache');
+  return ctools_object_cache_set($obj, 'panels_display:' . $did, $cache);
+}
+
+/**
+ * Clear a object from the cache; used if the editing is aborted.
+ */
+function panels_cache_clear($obj, $did) {
+  ctools_include('object-cache');
+  return ctools_object_cache_clear($obj, 'panels_display:' . $did);
+}
+
+/**
+ * Create the default cache for editing panel displays.
+ *
+ * If an application is using the Panels display editor without having
+ * specified a cache key, this method can be used to create the default
+ * cache.
+ */
+function panels_edit_cache_get_default(&$display, $content_types = NULL, $title = FALSE) {
+  if (empty($content_types)) {
+    $content_types = ctools_content_get_available_types();
+  }
+
+  $display->cache_key = $display->did;
+  panels_cache_clear('display', $display->did);
+
+  $cache = new stdClass();
+  $cache->display = &$display;
+  $cache->content_types = $content_types;
+  $cache->display_title = $title;
+
+  panels_edit_cache_set($cache);
+  return $cache;
+}
+
+/**
+ * Method to allow modules to provide their own caching mechanism for the
+ * display editor.
+ */
+function panels_edit_cache_get($cache_key) {
+  if (strpos($cache_key, ':') !== FALSE) {
+    list($module, $argument) = explode(':', $cache_key, 2);
+    return module_invoke($module, 'panels_cache_get', $argument);
+  }
+
+  // Fall back to our normal method:
+  return panels_cache_get('display', $cache_key);
+}
+
+/**
+ * Method to allow modules to provide their own caching mechanism for the
+ * display editor.
+ */
+function panels_edit_cache_set($cache) {
+  $cache_key = $cache->display->cache_key;
+  if (strpos($cache_key, ':') !== FALSE) {
+    list($module, $argument) = explode(':', $cache_key, 2);
+    return module_invoke($module, 'panels_cache_set', $argument, $cache);
+  }
+
+  // Fall back to our normal method:
+  return panels_cache_set('display', $cache_key, $cache);
+}
+
+/**
+ * Method to allow modules to provide their own mechanism to write the
+ * cache used in the display editor.
+ */
+function panels_edit_cache_save($cache) {
+  $cache_key = $cache->display->cache_key;
+  if (strpos($cache_key, ':') !== FALSE) {
+    list($module, $argument) = explode(':', $cache_key, 2);
+    if (function_exists($module . '_panels_cache_save')) {
+      return module_invoke($module, 'panels_cache_save', $argument, $cache);
+    }
+  }
+
+  // Fall back to our normal method:
+  return panels_save_display($cache->display);
+}
+
+/**
+ * Method to allow modules to provide their own mechanism to clear the
+ * cache used in the display editor.
+ */
+function panels_edit_cache_clear($cache) {
+  $cache_key = $cache->display->cache_key;
+  if (strpos($cache_key, ':') !== FALSE) {
+    list($module, $argument) = explode(':', $cache_key, 2);
+    if (function_exists($module . '_panels_cache_clear')) {
+      return module_invoke($module, 'panels_cache_clear', $argument, $cache);
+    }
+  }
+
+  // Fall back to our normal method:
+  return panels_cache_clear('display', $cache_key);
+}
+
+/**
+ * Method to allow modules to provide a mechanism to break locks.
+ */
+function panels_edit_cache_break_lock($cache) {
+  if (empty($cache->locked)) {
+    return;
+  }
+
+  $cache_key = $cache->display->cache_key;
+  if (strpos($cache_key, ':') !== FALSE) {
+    list($module, $argument) = explode(':', $cache_key, 2);
+    if (function_exists($module . '_panels_cache_break_lock')) {
+      return module_invoke($module, 'panels_cache_break_lock', $argument, $cache);
+    }
+  }
+
+  // Normal panel display editing has no locks, so we do nothing if there is
+  // no fallback.
+  return;
+}
+
+// --------------------------------------------------------------------------
+// Callbacks on behalf of the panel_context plugin.
+//
+// The panel_context plugin lets Panels be used in page manager. These
+// callbacks allow the display editing system to use the page manager
+// cache rather than the default display cache. They are routed by the cache
+// key via panels_edit_cache_* functions.
+
+/**
  * Get display edit cache on behalf of panel context.
  *
  * The key is the second half of the key in this form:
  * panel_context:TASK_NAME:HANDLER_NAME;
  */
 function panel_context_panels_cache_get($key) {
-  panels_load_include('common');
+  ctools_include('common', 'panels');
   ctools_include('context');
   ctools_include('context-task-handler');
   // this loads the panel context inc even if we don't use the plugin.
@@ -1001,14 +1423,15 @@ function panel_context_panels_cache_get($key) {
   $cache->display->cache_key = 'panel_context:' . $key;
   $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
   $cache->display_title = TRUE;
+  $cache->locked = $page->locked;
 
   return $cache;
 }
 
 /**
- * Store a display edit in progress in the page cache.
+ * Get the Page Manager cache for the panel_context plugin.
  */
-function panel_context_panels_cache_set($key, $cache) {
+function _panel_context_panels_cache_get_page_cache($key, $cache) {
   list($task_name, $handler_name) = explode(':', $key, 2);
   $page = page_manager_get_page_cache($task_name);
   $page->display_cache[$handler_name] = $cache;
@@ -1019,58 +1442,244 @@ function panel_context_panels_cache_set($key, $cache) {
   else {
     $page->new_handler->conf['display'] = $cache->display;
   }
+
+  return $page;
+}
+
+/**
+ * Store a display edit in progress in the page cache.
+ */
+function panel_context_panels_cache_set($key, $cache) {
+  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
   page_manager_set_page_cache($page);
 }
 
 /**
- * Clean up the panel pane variables for the template.
+ * Save all changes made to a display using the Page Manager page cache.
  */
-function template_preprocess_panels_pane($vars) {
-  $content = $vars['output'];
-  // basic classes
-  $vars['classes'] = 'panel-pane';
-  $vars['id'] = '';
+function panel_context_panels_cache_clear($key, $cache) {
+  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
+  page_manager_clear_page_cache($page->task_name);
+}
 
-  // Add some usable classes based on type/subtype
-  ctools_include('cleanstring');
-  $type_class = $content->type ? 'pane-'. ctools_cleanstring($content->type, array('lower case' => TRUE)) : '';
-  $subtype_class = $content->subtype ? 'pane-'. ctools_cleanstring($content->subtype, array('lower case' => TRUE)) : '';
+/**
+ * Save all changes made to a display using the Page Manager page cache.
+ */
+function panel_context_panels_cache_save($key, $cache) {
+  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
+  page_manager_save_page_cache($page);
+}
 
-  // Sometimes type and subtype are the same. Avoid redudant classes.
-  if ($type_class != $subtype_class) { 
-    $vars['classes'] .= " $type_class $subtype_class";
+/**
+ * Break the lock on a page manager page.
+ */
+function panel_context_panels_cache_break_lock($key, $cache) {
+  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
+  ctools_object_cache_clear_all('page_manager_page', $page->task_name);
+}
+
+// --------------------------------------------------------------------------
+// Callbacks on behalf of the panels page wizards
+//
+// The page wizards are a pluggable set of 'wizards' to make it easy to create
+// specific types of pages based upon whatever someone felt like putting
+// together. Since they will very often have content editing, we provide
+// a generic mechanism to allow them to store their editing cache in the
+// wizard cache.
+//
+// For them to use this mechanism, they just need to use:
+//   $cache = panels_edit_cache_get('panels_page_wizard:' . $plugin['name']);
+
+/**
+ * Get display edit cache for the panels mini export UI
+ *
+ * The key is the second half of the key in this form:
+ * panels_page_wizard:TASK_NAME:HANDLER_NAME;
+ */
+function panels_page_wizard_panels_cache_get($key) {
+  ctools_include('page-wizard');
+  ctools_include('context');
+  $wizard_cache = page_manager_get_wizard_cache($key);
+  if (isset($wizard_cache->display_cache)) {
+    return $wizard_cache->display_cache;
   }
-  else { 
-    $vars['classes'] .= " $type_class";
+
+  ctools_include('common', 'panels');
+  $cache = new stdClass();
+  $cache->display = $wizard_cache->display;
+  $cache->display->context = !empty($wizard_cache->context) ? $wizard_cache->context : array();
+  $cache->display->cache_key = 'panels_page_wizard:' . $key;
+  $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
+  $cache->display_title = TRUE;
+
+  return $cache;
+}
+
+/**
+ * Store a display edit in progress in the page cache.
+ */
+function panels_page_wizard_panels_cache_set($key, $cache) {
+  ctools_include('page-wizard');
+  $wizard_cache = page_manager_get_wizard_cache($key);
+  $wizard_cache->display_cache = $cache;
+  page_manager_set_wizard_cache($wizard_cache);
+}
+
+// --------------------------------------------------------------------------
+// General utility functions
+
+/**
+ * Perform a drupal_goto on a destination that may be an array like url().
+ */
+function panels_goto($destination) {
+  if (!is_array($destination)) {
+    return drupal_goto($destination);
+  }
+  else {
+    // Prevent notices by adding defaults
+    $destination += array(
+      'query' => NULL,
+      'fragment' => NULL,
+      'http_response_code' => NULL,
+    );
+
+    return drupal_goto($destination['path'], $destination['query'], $destination['fragment'], $destination['http_response_code']);
   }
+}
 
-  // Add id and custom class if sent in.
-  if (!empty($content->content)) {
-    if (!empty($content->css_id)) {
-      $vars['id'] = ' id="' . $content->css_id . '"';
+
+/**
+ * For external use: Given a layout ID and a $content array, return the
+ * panel display.
+ *
+ * The content array is filled in based upon the content available in the
+ * layout. If it's a two column with a content array defined like
+ * @code
+ *   array(
+ *    'left' => t('Left side'),
+ *    'right' => t('Right side')
+ *  ),
+ * @endcode
+ *
+ * Then the $content array should be
+ * @code
+ * array(
+ *   'left' => $output_left,
+ *   'right' => $output_right,
+ * )
+ * @endcode
+ *
+ * The output within each panel region can be either a single rendered
+ * HTML string or an array of rendered HTML strings as though they were
+ * panes. They will simply be concatenated together without separators.
+ */
+function panels_print_layout($layout, $content, $meta = 'standard') {
+  ctools_include('plugins', 'panels');
+
+  // Create a temporary display for this.
+  $display = panels_new_display();
+  $display->layout = is_array($layout) ? $layout['name'] : $layout;
+  $display->content = $content;
+
+  // Get our simple renderer
+  $renderer = panels_get_renderer_handler('simple', $display);
+  $renderer->meta_location = $meta;
+
+  return $renderer->render();
+}
+
+// --------------------------------------------------------------------------
+// Deprecated functions
+//
+// Everything below this line will eventually go away.
+
+/**
+ * Load a panels include file.
+ *
+ * @deprecated This function is deprecated and should no longer be used. It will
+ * be removed in the next major version of Panels. Use ctools_include() instead.
+ */
+function panels_load_include($include, $path = 'includes/') {
+  static $loaded = array();
+  if (empty($loaded["$path$include.inc"])) {
+    require_once './' . panels_get_path("$path$include.inc");
+    $loaded["$path$include.inc"] = TRUE;
+  }
+}
+
+/**
+ * panels path helper function
+ */
+function panels_get_path($file, $base_path = FALSE, $module = 'panels') {
+  $output = $base_path ? base_path() : '';
+  return $output . drupal_get_path('module', $module) . '/' . $file;
+}
+
+/**
+ * Given a full layout structure and a content array, render a panel display.
+ *
+ * @deprecated This function represents an old approach to rendering, and is
+ * retained only as a temporary support for other modules still using that
+ * approach. It will be removed in the next major version of Panels.
+ */
+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']);
     }
-    if (!empty($content->css_class)) {
-      $vars['classes'] .= ' ' . $content->css_class;
+    else {
+      drupal_add_css($layout['path'] . '/' . $layout['css']);
     }
   }
-
-  // administrative links, only if there is permission.
-  $vars['admin_links'] = '';
-  if (user_access('view pane admin links') && !empty($content->admin_links)) {
-    $vars['admin_links'] = theme('links', $content->admin_links);
+  // 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;
+    }
   }
 
-  $vars['title'] = !empty($content->title) ? $content->title : '';
+  $output = theme($layout['theme'], check_plain($css_id), $content, $settings, $display);
 
-  $vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : '';
-  $vars['content'] = !empty($content->content) ? $content->content : '';
+  return $output;
+}
 
-  $vars['links'] = !empty($content->links) ? theme('links', $content->links) : '';
-  $vars['more'] = '';
-  if (!empty($content->more)) {
-    if (empty($content->more['title'])) {
-      $content->more['title'] = t('more');
-    }
-    $vars['more'] = l($content->more['title'], $content->more['href'], $content->more);;
-  }
+/**
+ * Get a list of panel regions available in the layout.
+ *
+ * @deprecated Use panels_get_regions instead.
+ */
+function panels_get_panels($layout, $display) {
+  return panels_get_regions($layout, $display);
 }
+
+/**
+ * Select a context for a pane.
+ *
+ * @param $pane
+ *   A fully populated pane.
+ * @param $contexts
+ *   A keyed array of available contexts.
+ *
+ * @return
+ *   The matching contexts or NULL if none or necessary, or FALSE if
+ *   requirements can't be met.
+ *
+ * @deprecated this function will be removed.
+ */
+function panels_pane_select_context($pane, $contexts) {
+  return ctools_content_select_context($pane->type, $pane->subtype, $pane->configuration, $contexts);
+}
+
diff --git a/panels2.doxy b/panels2.doxy
deleted file mode 100644 (file)
index c8a6bbd..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-# $Id$
-# Doxyfile 1.5.6
-
-# MAKE SURE to replace the ${variable} values with settings appropriate
-# to your system!
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-DOXYFILE_ENCODING      = UTF-8
-PROJECT_NAME           = "Panels 2"
-PROJECT_NUMBER         = 
-OUTPUT_DIRECTORY       = ${OUTPUT}/panels2
-CREATE_SUBDIRS         = NO
-OUTPUT_LANGUAGE        = English
-BRIEF_MEMBER_DESC      = YES
-REPEAT_BRIEF           = NO
-ABBREVIATE_BRIEF       = 
-ALWAYS_DETAILED_SEC    = YES
-INLINE_INHERITED_MEMB  = NO
-FULL_PATH_NAMES        = YES
-STRIP_FROM_PATH        = ${MODULE_PATH}
-STRIP_FROM_INC_PATH    = 
-SHORT_NAMES            = NO
-JAVADOC_AUTOBRIEF      = NO
-QT_AUTOBRIEF           = NO
-MULTILINE_CPP_IS_BRIEF = NO
-DETAILS_AT_TOP         = YES
-INHERIT_DOCS           = YES
-SEPARATE_MEMBER_PAGES  = NO
-TAB_SIZE               = 2
-ALIASES                = "L{1}=\ref @w{\m}\1" \
-                         "m=panels_" \
-                         "mA=@w{@m}api_" \
-                         "mG=@w{@m}glossary_" \
-                         "mAP=@w{@mA}plugins_" \
-                                                                                                "mGs=@w{@mG}sec_" \
-                                                                                                "mGt=@w{@mG}term_" \
-                         "LA{1}=\ref @w{@mA}\1" \
-                         "LAP{1}=\ref @w{@mAP}\1" \
-                         "LGt{1}=\ref @w{@mGt}\1" \
-                         "L{2}=\ref @w{@m}\1 \"\2\"" \
-                         "LA{2}=\ref @w{@mA}\1 \"\2\"" \
-                         "LAP{2}=\ref @w{@mAP}\1 \"\2\"" \
-                                                                                                "LAP{3}=\ref @w{@mAP}\1_\2 \"\3\"" \
-                         "LGt{2}=\ref @w{@mGt}\1 \"\2\"" \
-                         "AG{2}=\par \anchor @w{\mGt}\1 \LITI{\2} \n" \
-                                                                                                "AAP{3}=\anchor @w{\mAP}\1_\2 \3" \
-                                                                                                "SECglossary{2}=\section @w{@mGs}\1 \2" \
-                                                                                                "SSECglossary{3}=\subsection @w{@mGs}\1_\2 \3" \
-                         "Psubplug{2}=\subpage @w{@mAP}\1 \"\2\"" \
-                                                                                                "Pplug{2}=\page @w{@mAP}\1 \2" \
-                                                                                                "SECplug{3}=\section @w{@mAP}\1_\2 \3" \
-                                                                                                "SSECplug{4}=\subsection @w{@mAP}\1_\2_\3 \4" \
-                         "S{1}=<strong>@w{\1}</strong>" \
-                         "E{1}=<em>@w{\1}</em>" \
-                         "LITI{1}=<strong><em>@w{\1}:</em></strong>" \
-                         "IMG{2}=\image html \1 \"\2\"" \
-                         "TR{1}=<tr>@w{\1}</tr>" \
-                         "TD{1}=<td>@w{\1}</td>" \
-                         "pdarray=\LGt{p-d-array,plugin definition array}" \
-                         "pdfunc=\LGt{p-d-func,plugin declaration function}" \
-                         "pdarrays=\LGt{p-d-array,plugin definition arrays}" \
-                         "pdfuncs=\LGt{p-d-func,plugin declaration functions}" \
-                         "plugprops=\LGt{plugprop,properties}" \
-                         "plugprop=\LGt{plugprop,property}" \
-                                                                                                "w{1}=\1" # uber wrapper of godly doxy substitution!
-OPTIMIZE_OUTPUT_FOR_C  = NO
-OPTIMIZE_OUTPUT_JAVA   = NO
-OPTIMIZE_FOR_FORTRAN   = NO
-OPTIMIZE_OUTPUT_VHDL   = NO
-BUILTIN_STL_SUPPORT    = NO
-CPP_CLI_SUPPORT        = NO
-SIP_SUPPORT            = NO
-IDL_PROPERTY_SUPPORT   = NO
-DISTRIBUTE_GROUP_DOC   = NO
-SUBGROUPING            = YES
-TYPEDEF_HIDES_STRUCT   = NO
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-EXTRACT_ALL            = YES
-EXTRACT_PRIVATE        = YES
-EXTRACT_STATIC         = YES
-EXTRACT_LOCAL_CLASSES  = YES
-EXTRACT_LOCAL_METHODS  = NO
-EXTRACT_ANON_NSPACES   = YES
-HIDE_UNDOC_MEMBERS     = NO
-HIDE_UNDOC_CLASSES     = NO
-HIDE_FRIEND_COMPOUNDS  = NO
-HIDE_IN_BODY_DOCS      = NO
-INTERNAL_DOCS          = NO
-CASE_SENSE_NAMES       = YES
-HIDE_SCOPE_NAMES       = NO
-SHOW_INCLUDE_FILES     = YES
-INLINE_INFO            = YES
-SORT_MEMBER_DOCS       = YES
-SORT_BRIEF_DOCS        = YES
-SORT_GROUP_NAMES       = YES
-SORT_BY_SCOPE_NAME     = YES
-GENERATE_TODOLIST      = YES
-GENERATE_TESTLIST      = YES
-GENERATE_BUGLIST       = YES
-GENERATE_DEPRECATEDLIST= YES
-ENABLED_SECTIONS       = YES
-MAX_INITIALIZER_LINES  = 30
-SHOW_USED_FILES        = YES
-SHOW_DIRECTORIES       = YES
-SHOW_FILES             = YES
-SHOW_NAMESPACES        = YES
-FILE_VERSION_FILTER    = "sed -n 's/^.*\$Id: [^ \]* \(.*\) Exp.*/\1/p'"
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-QUIET                  = YES
-WARNINGS               = YES
-WARN_IF_UNDOCUMENTED   = NO
-WARN_IF_DOC_ERROR      = YES
-WARN_NO_PARAMDOC       = YES
-WARN_FORMAT            = "$line: $text"
-WARN_LOGFILE           = /var/log/doxy/drupal-doxy.log
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-INPUT                  = ${MODULE_PATH}
-INPUT_ENCODING         = UTF-8
-FILE_PATTERNS          = *.module \
-                         *.theme \
-                         *.engine \
-                         *.install \
-                         *.inc
-RECURSIVE              = YES
-EXCLUDE                = 
-EXCLUDE_SYMLINKS       = YES
-EXCLUDE_PATTERNS       = */.svn/* \
-                         */CVS/* \
-                         */cvs-release-notes.php \
-                         */patches/*
-EXCLUDE_SYMBOLS        = 
-EXAMPLE_PATH           = ${MODULE_PATH}/docs
-EXAMPLE_PATTERNS       = *
-EXAMPLE_RECURSIVE      = NO
-IMAGE_PATH             = html/images
-INPUT_FILTER           = 
-FILTER_PATTERNS        = 
-FILTER_SOURCE_FILES    = NO
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-SOURCE_BROWSER         = YES
-INLINE_SOURCES         = YES
-STRIP_CODE_COMMENTS    = NO
-REFERENCED_BY_RELATION = YES
-REFERENCES_RELATION    = YES
-REFERENCES_LINK_SOURCE = YES
-USE_HTAGS              = NO
-VERBATIM_HEADERS       = YES
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-ALPHABETICAL_INDEX     = YES
-COLS_IN_ALPHA_INDEX    = 5
-IGNORE_PREFIX          = 
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-GENERATE_HTML          = YES
-HTML_OUTPUT            = html
-HTML_FILE_EXTENSION    = .html
-HTML_HEADER            = 
-HTML_FOOTER            = 
-HTML_STYLESHEET        = ${MODULE_PATH}/docs/doxygen.css
-HTML_ALIGN_MEMBERS     = YES
-GENERATE_HTMLHELP      = NO
-GENERATE_DOCSET        = NO
-DOCSET_FEEDNAME        = 
-DOCSET_BUNDLE_ID       = 
-HTML_DYNAMIC_SECTIONS  = YES
-CHM_FILE               = 
-HHC_LOCATION           = 
-GENERATE_CHI           = NO
-CHM_INDEX_ENCODING     = 
-BINARY_TOC             = NO
-TOC_EXPAND             = YES
-DISABLE_INDEX          = NO
-ENUM_VALUES_PER_LINE   = 4
-GENERATE_TREEVIEW      = YES
-TREEVIEW_WIDTH         = 250
-FORMULA_FONTSIZE       = 10
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-GENERATE_LATEX         = NO
-LATEX_OUTPUT           = latex
-LATEX_CMD_NAME         = latex
-MAKEINDEX_CMD_NAME     = 
-COMPACT_LATEX          = NO
-PAPER_TYPE             = letter
-EXTRA_PACKAGES         = 
-LATEX_HEADER           = 
-PDF_HYPERLINKS         = YES
-USE_PDFLATEX           = YES
-LATEX_BATCHMODE        = YES
-LATEX_HIDE_INDICES     = NO
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-GENERATE_RTF           = NO
-RTF_OUTPUT             = rtf
-COMPACT_RTF            = NO
-RTF_HYPERLINKS         = NO
-RTF_STYLESHEET_FILE    = 
-RTF_EXTENSIONS_FILE    = 
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-GENERATE_MAN           = NO
-MAN_OUTPUT             = man
-MAN_EXTENSION          = .3
-MAN_LINKS              = NO
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-GENERATE_XML           = YES
-XML_OUTPUT             = xml
-XML_SCHEMA             = 
-XML_DTD                = 
-XML_PROGRAMLISTING     = YES
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-GENERATE_AUTOGEN_DEF   = NO
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-GENERATE_PERLMOD       = NO
-PERLMOD_LATEX          = NO
-PERLMOD_PRETTY         = YES
-PERLMOD_MAKEVAR_PREFIX = 
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
-#---------------------------------------------------------------------------
-ENABLE_PREPROCESSING   = YES
-MACRO_EXPANSION        = NO
-EXPAND_ONLY_PREDEF     = NO
-SEARCH_INCLUDES        = YES
-INCLUDE_PATH           = 
-INCLUDE_FILE_PATTERNS  = 
-PREDEFINED             = 
-EXPAND_AS_DEFINED      = 
-SKIP_FUNCTION_MACROS   = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references   
-#---------------------------------------------------------------------------
-TAGFILES               = 
-GENERATE_TAGFILE       = 
-ALLEXTERNALS           = NO
-EXTERNAL_GROUPS        = YES
-PERL_PATH              = /usr/bin/perl
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
-#---------------------------------------------------------------------------
-CLASS_DIAGRAMS         = YES
-MSCGEN_PATH            = /usr/local/bin
-HIDE_UNDOC_RELATIONS   = NO
-HAVE_DOT               = YES
-DOT_FONTNAME           = FreeSans
-DOT_FONTPATH           = 
-CLASS_GRAPH            = YES
-COLLABORATION_GRAPH    = YES
-GROUP_GRAPHS           = YES
-UML_LOOK               = YES
-TEMPLATE_RELATIONS     = YES
-INCLUDE_GRAPH          = YES
-INCLUDED_BY_GRAPH      = YES
-CALL_GRAPH             = YES
-CALLER_GRAPH           = YES
-GRAPHICAL_HIERARCHY    = YES
-DIRECTORY_GRAPH        = NO
-DOT_IMAGE_FORMAT       = png
-DOT_PATH               = 
-DOTFILE_DIRS           = 
-DOT_GRAPH_MAX_NODES    = 50
-MAX_DOT_GRAPH_DEPTH    = 0
-DOT_TRANSPARENT        = YES
-DOT_MULTI_TARGETS      = YES
-GENERATE_LEGEND        = YES
-DOT_CLEANUP            = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine   
-#---------------------------------------------------------------------------
-SEARCHENGINE           = YES
diff --git a/panels_ipe/css/panels_ipe.css b/panels_ipe/css/panels_ipe.css
new file mode 100644 (file)
index 0000000..77edc15
--- /dev/null
@@ -0,0 +1,209 @@
+div.panels-ipe-handlebar-wrapper {
+  border-bottom: #898AAB solid 2px;
+}
+
+.panels-ipe-editing div.panels-ipe-portlet-wrapper {
+  margin-top: 1em;
+  border: #898AAB solid 2px;
+  -moz-border-radius-bottomleft:8px;
+  -moz-border-radius-bottomright:8px;
+  -moz-border-radius-topleft:2px;
+  -moz-border-radius-topright:2px;
+
+  -webkit-border-radius-bottomleft:8px;
+  -webkit-border-radius-bottomright:8px;
+  -webkit-border-radius-topleft:2px;
+  -webkit-border-radius-topright:2px;
+}
+
+/* Hide empty panes when not editing them. */
+.panels-ipe-empty-pane {
+  display: none;
+}
+
+.panels-ipe-editing .panels-ipe-empty-pane {
+  display: block;
+}
+
+
+.panels-ipe-editing div.panels-ipe-portlet-wrapper:hover {
+  border: #FF000A solid 2px;
+}
+
+.panels-ipe-editing .panels-ipe-sort-container .ui-sortable-helper {
+  background: white;
+}
+
+.panels-ipe-editing div.panel-pane div.admin-links {
+  display: none !important;
+}
+
+.panels-ipe-editing .panels-ipe-sort-container .ui-sortable-placeholder {
+  -moz-border-radius: 0;
+  -webkit-border-radius: 0;
+  border: 1px dotted red;
+  background-color: white;
+}
+
+div.panels-ipe-handlebar-wrapper ul {
+  float: left;
+  margin: 0;
+  padding: 0;
+  text-align: right;
+}
+
+div.panels-ipe-handlebar-wrapper li {
+  background: none repeat scroll 0 0 transparent;
+  list-style: none outside none;
+  margin: 0;
+  padding: 0;
+  float: left;
+  font: 12px/170% Verdana,sans-serif !important;
+}
+
+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 {
+  background: #898AAB url(../images/dragger.png) top right no-repeat;
+  cursor: move;
+  height: 24px;
+}
+
+div.panels-ipe-placeholder {
+  border: 1px solid black;
+  padding: .5em;
+  margin-top: .5em;
+  background-color: #f6f6f6;
+  color: black;
+  background-color: white;
+  font: 12px/170% Verdana,sans-serif !important;
+  text-transform: none;
+  letter-spacing: 0;
+  text-align: left;
+  word-spacing: 0;
+}
+
+div.panels-ipe-placeholder h3 {
+  font-weight: bold;
+}
+
+/* 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;
+  color: blue;
+  display: block;
+  padding: 0.1em 0.5em;
+  font: 12px/170% Verdana,sans-serif !important;
+  text-transform: none;
+  letter-spacing: 0;
+}
+
+div.panels-ipe-newblock a {
+  display: inline;
+  border: 1px solid #CCC;
+  color: blue;
+}
+
+.panels-ipe-editing .panels-ipe-portlet-content {
+  margin-bottom: 10px;
+  border: transparent dotted 1px;
+  overflow: hidden;
+}
+
+.panels-ipe-editing .panels-ipe-region {
+  border: transparent dotted 1px;
+  float: left;
+  width: 100%;
+  margin-bottom: 5px;
+}
+
+div.panels-ipe-draghandle {
+  border: none;
+}
+
+.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;
+  min-width: 9.5em;
+  max-width: 12.5em;
+  min-height: 2em;
+  max-height: 2.5em;
+  -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;
+}
+
+div.panels-ipe-form-container {
+  min-width: 12.5em;
+  min-height: 2em;
+  margin-left: auto;
+  margin-right: auto;
+}
diff --git a/panels_ipe/images/dragger.png b/panels_ipe/images/dragger.png
new file mode 100644 (file)
index 0000000..bb3b57b
Binary files /dev/null and b/panels_ipe/images/dragger.png differ
diff --git a/panels_ipe/includes/panels_ipe.pipelines.inc b/panels_ipe/includes/panels_ipe.pipelines.inc
new file mode 100644 (file)
index 0000000..680f196
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Bulk export of panels_layouts objects generated by Bulk export module.
+ */
+
+/**
+ * Implementation of hook_default_panels_layout()
+ */
+function panels_ipe_default_panels_renderer_pipeline() {
+  $pipelines = array();
+
+  $pipeline = new stdClass;
+  $pipeline->disabled = FALSE; /* Edit this to true to make a default pipeline disabled initially */
+  $pipeline->api_version = 1;
+  $pipeline->name = 'ipe';
+  $pipeline->admin_title = t('In-Place Editor');
+  $pipeline->admin_description = t('Allows privileged users to update and rearrange the content while viewing this panel.');
+  $pipeline->weight = 0;
+  $pipeline->settings = array(
+    'renderers' => array(
+      0 => array(
+        'access' => array(
+          'plugins' => array(
+            0 => array(
+              'name' => 'perm',
+              'settings' => array(
+                'perm' => 'use panels in place editing',
+              ),
+              'context' => 'logged-in-user',
+            ),
+          ),
+          'logic' => 'and',
+        ),
+        'renderer' => 'ipe',
+        'options' => array(),
+      ),
+    ),
+  );
+  $pipelines[$pipeline->name] = $pipeline;
+
+  return $pipelines;
+}
diff --git a/panels_ipe/js/panels_ipe.js b/panels_ipe/js/panels_ipe.js
new file mode 100644 (file)
index 0000000..0ed435b
--- /dev/null
@@ -0,0 +1,253 @@
+// $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('medium', function() {
+            $(this).empty().remove();
+          });
+          $(this).parents('div.panels-ipe-display-container').addClass('changed');
+        }
+        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);
+};
+
+Drupal.CTools.AJAX.commands.initIPE = function(data) {
+  if (Drupal.PanelsIPE.editors[data.key]) {
+    Drupal.PanelsIPE.editors[data.key].initEditing(data.data);
+  }
+};
+
+Drupal.CTools.AJAX.commands.unlockIPE = function(data) {
+  if (confirm(data.message)) {
+    var ajaxOptions = {
+      type: "POST",
+      url: data.break_path,
+      data: { 'js': 1 },
+      global: true,
+      success: Drupal.CTools.AJAX.respond,
+      error: function(xhr) {
+        Drupal.CTools.AJAX.handleErrors(xhr, ipe.cfg.formPath);
+      },
+      dataType: 'json'
+    };
+
+    $.ajax(ajaxOptions);
+  };
+};
+
+Drupal.CTools.AJAX.commands.endIPE = function(data) {
+  if (Drupal.PanelsIPE.editors[data.key]) {
+    Drupal.PanelsIPE.editors[data.key].endEditing(data);
+  }
+};
+
+/**
+ * Base object (class) definition for the Panels In-Place Editor.
+ *
+ * A new instance of this object is instanciated for every unique IPE on a given
+ * page.
+ *
+ * Note that this form is provisional, and we hope to replace it with a more
+ * flexible, loosely-coupled model that utilizes separate controllers for the
+ * discrete IPE elements. This will result in greater IPE flexibility.
+ */
+function DrupalPanelsIPE(cache_key, cfg) {
+  var ipe = this;
+  this.key = cache_key;
+  this.state = {};
+  this.control = $('div#panels-ipe-control-' + cache_key);
+  this.initButton = $('div.panels-ipe-startedit', this.control);
+  this.cfg = cfg;
+  this.changed = false;
+  this.sortableOptions = $.extend({
+    revert: 200,
+    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',
+    cursorAt: 'top',
+    update: this.setChanged,
+    scroll: true
+    // containment: ipe.topParent,
+  }, cfg.sortableOptions || {});
+
+  this.initEditing = function(formdata) {
+    ipe.topParent = $('div#panels-ipe-display-' + cache_key);
+    ipe.backup = this.topParent.clone();
+
+    // See http://jqueryui.com/demos/sortable/ for details on the configuration
+    // parameters used here.
+    ipe.changed = false;
+
+    $('div.panels-ipe-sort-container', ipe.topParent).sortable(ipe.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']);
+
+    $('div.panels-ipe-sort-container', ipe.topParent).bind('sortupdate', function() {
+      ipe.changed = true;
+    });
+
+    $('.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: Drupal.CTools.AJAX.respond,
+          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.initButton.css('position', 'absolute');
+    ipe.initButton.fadeOut('normal');
+    $('.panels-ipe-on').show('normal');
+//    $('.panels-ipe-on').fadeIn('normal');
+    ipe.topParent.addClass('panels-ipe-editing');
+  }
+
+  this.endEditing = function(data) {
+    $('.panels-ipe-form-container', ipe.control).empty();
+    // Re-show all the IPE non-editing meta-elements
+    $('div.panels-ipe-off').show('fast');
+
+    // Re-hide all the IPE meta-elements
+    $('div.panels-ipe-on').hide('fast');
+    ipe.initButton.css('position', 'normal');
+    ipe.topParent.removeClass('panels-ipe-editing');
+  };
+
+  this.saveEditing = function() {
+    // Put our button in.
+    this.form.clk = this;
+
+    $('div.panels-ipe-region', ipe.topParent).each(function() {
+      var val = '';
+      var region = $(this).attr('id').split('panels-ipe-regionid-')[1];
+      $(this).find('div.panels-ipe-portlet-wrapper').each(function() {
+        var id = $(this).attr('id').split('panels-ipe-paneid-')[1];
+        if (id) {
+          if (val) {
+            val += ',';
+          }
+          val += id;
+        }
+      });
+      $('input#edit-panel-pane-' + region, ipe.control).val(val);
+    });
+  }
+
+  this.cancelEditing = function() {
+    // Put our button in.
+    this.form.clk = this;
+
+    if (ipe.topParent.hasClass('changed')) {
+      ipe.changed = true;
+    }
+
+    if (!ipe.changed || confirm(Drupal.t('This will discard all unsaved changes. Are you sure?'))) {
+      ipe.topParent.fadeOut('medium', function() {
+        ipe.topParent.replaceWith(ipe.backup.clone());
+        ipe.topParent = $('div#panels-ipe-display-' + ipe.key);
+
+        // Processing of these things got lost in the cloning, but the classes remained behind.
+        // @todo this isn't ideal but I can't seem to figure out how to keep an unprocessed backup
+        // that will later get processed.
+        $('.ctools-use-modal-processed', ipe.topParent).removeClass('ctools-use-modal-processed');
+        $('.pane-delete-processed', ipe.topParent).removeClass('pane-delete-processed');
+        ipe.topParent.fadeIn('medium');
+        Drupal.attachBehaviors();
+      });
+    }
+    else {
+      // Cancel the submission.
+      return false;
+    }
+  };
+
+  this.createSortContainers = function() {
+    $('div.panels-ipe-region', this.topParent).each(function() {
+      $('div.panels-ipe-portlet-marker', this).parent()
+        .wrapInner('<div class="panels-ipe-sort-container" />');
+
+      // Move our gadgets outside of the sort container so that sortables
+      // cannot be placed after them.
+      $('div.panels-ipe-portlet-static', this).each(function() {
+        $(this).appendTo($(this).parent().parent());
+      });
+
+      // Add a marker so we can drag things to empty containers.
+      $('div.panels-ipe-sort-container', this).append('<div>&nbsp;</div>');
+    });
+  }
+
+  this.createSortContainers();
+
+  var ajaxOptions = {
+    type: "POST",
+    url: ipe.cfg.formPath,
+    data: { 'js': 1 },
+    global: true,
+    success: Drupal.CTools.AJAX.respond,
+    error: function(xhr) {
+      Drupal.CTools.AJAX.handleErrors(xhr, ipe.cfg.formPath);
+    },
+    dataType: 'json'
+  };
+
+  $('div.panels-ipe-startedit', this.control).click(function() {
+    var $this = $(this);
+    $.ajax(ajaxOptions);
+  });
+};
+
+})(jQuery);
diff --git a/panels_ipe/panels_ipe.info b/panels_ipe/panels_ipe.info
new file mode 100644 (file)
index 0000000..c27a2a9
--- /dev/null
@@ -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 a/panels_ipe/panels_ipe.module b/panels_ipe/panels_ipe.module
new file mode 100644 (file)
index 0000000..c0520a2
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+
+/**
+ * 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_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_ipe_ctools_plugin_api($owner, $api) {
+  if ($owner == 'panels' && $api == 'pipelines') {
+    return array(
+      'version' => 1,
+      'path' => drupal_get_path('module', 'panels_ipe') . '/includes',
+    );
+  }
+}
+
+/**
+ * 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, 'renderer' => NULL),
+    ),
+    'panels_ipe_add_pane_button' => array(
+      'arguments' => array('region_id' => NULL, 'display' => NULL, 'renderer' => 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 = '<div class="panels-ipe-placeholder-content">';
+  $output .= "<h3>$region_title</h3>";
+  $output .= '</div>';
+  return $output;
+}
+
+function theme_panels_ipe_pane_wrapper($output, $pane, $display, $renderer) {
+  $content_type = ctools_get_content_type($pane->type);
+  $subtype = ctools_content_get_subtype($content_type, $pane->subtype);
+  $links = array();
+
+  if (ctools_content_editable($content_type, $subtype, $pane->configuration)) {
+    $links['edit'] = array(
+      'title' => isset($content_type['edit text']) ? $content_type['edit text'] : t('Settings'),
+      'href' => $renderer->get_url('edit-pane', $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
+  $links['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 .= '<div class="panels-ipe-draghandle">&nbsp;</div>';
+  $handlebar = '<div class="panels-ipe-handlebar-wrapper panels-ipe-on clear-block">' . $links . '</div>';
+  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, $renderer) {
+  $attr = array('class' => 'ctools-use-modal');
+  $link = l(t('Add new pane'), $renderer->get_url('select-content', $region_id), array('attributes' => $attr));
+  return '<div class="panels-ipe-newblock panels-ipe-on panels-ipe-portlet-static">' . $link . '</div>';
+}
+
+function panels_ipe_get_cache_key($key = NULL) {
+  static $cache;
+  if (isset($key)) {
+    $cache = $key;
+  }
+  return $cache;
+}
+
+/**
+ * Implementation of hook_footer()
+ *
+ * Adds the IPE control container.
+ *
+ * @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 = "<div id='panels-ipe-control-container' class='clear-block'>";
+  $output .= "<div id='panels-ipe-control-$key' class='panels-ipe-control'>";
+  $output .= "<div class='panels-ipe-startedit panels-ipe-pseudobutton panels-ipe-off'>";
+  $output .= "<span>" . t('Customize this page') . "</span>";
+  $output .= "</div>";
+  $output .= "<div class='panels-ipe-form-container panels-ipe-on clear-block'</div>";
+  $output .= "</div></div>";
+  return $output;
+}
diff --git a/panels_ipe/plugins/display_renderers/ipe.inc b/panels_ipe/plugins/display_renderers/ipe.inc
new file mode 100644 (file)
index 0000000..afe1e44
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+
+$plugin = array(
+  'handler' => array(
+    'class' => 'panels_renderer_ipe',
+    'parent' => 'editor',
+  ),
+);
\ No newline at end of file
diff --git a/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php b/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php
new file mode 100644 (file)
index 0000000..fad426c
--- /dev/null
@@ -0,0 +1,253 @@
+<?php
+// $Id$
+
+/**
+ * Renderer class for all In-Place Editor (IPE) behavior.
+ */
+class panels_renderer_ipe extends panels_renderer_editor {
+  // The IPE operates in normal render mode, not admin mode.
+  var $admin = FALSE;
+
+  function render() {
+    $output = parent::render();
+    return "<div id='panels-ipe-display-{$this->clean_key}' class='panels-ipe-display-container'>$output</div>";
+  }
+
+  function add_meta() {
+    ctools_include('display-edit', 'panels');
+    ctools_include('content');
+
+    if (empty($this->display->cache_key)) {
+      $this->cache = panels_edit_cache_get_default($this->display);
+    }
+    // @todo we may need an else to load the cache, but I am not sure we
+    // actually need to load it if we already have our cache key, and doing
+    // so is a waste of resources.
+
+    ctools_include('cleanstring');
+    $this->clean_key = ctools_cleanstring($this->display->cache_key);
+    panels_ipe_get_cache_key($this->clean_key);
+
+    ctools_include('ajax');
+    ctools_include('modal');
+    ctools_modal_add_js();
+
+    ctools_add_css('panels_dnd', 'panels');
+    ctools_add_css('panels_admin', 'panels');
+    ctools_add_js('panels_ipe', 'panels_ipe');
+    ctools_add_css('panels_ipe', 'panels_ipe');
+
+    $settings = array(
+      'formPath' => url($this->get_url('save-form')),
+    );
+    drupal_add_js(array('PanelsIPECacheKeys' => array($this->clean_key)), 'setting');
+    drupal_add_js(array('PanelsIPESettings' => array($this->clean_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;
+    }
+
+    if (empty($pane->IPE_empty)) {
+      // Add an inner layer wrapper to the pane content before placing it into
+      // draggable portlet
+      $output = "<div class=\"panels-ipe-portlet-content\">$output</div>";
+    }
+    else {
+      $output = "<div class=\"panels-ipe-portlet-content panels-ipe-empty-pane\">$output</div>";
+    }
+    // Hand it off to the plugin/theme for placing draggers/buttons
+    $output = theme('panels_ipe_pane_wrapper', $output, $pane, $this->display, $this);
+    return "<div id=\"panels-ipe-paneid-{$pane->pid}\" class=\"panels-ipe-portlet-wrapper panels-ipe-portlet-marker\">" . $output . "</div>";
+  }
+
+  function render_pane_content(&$pane) {
+    $content = parent::render_pane_content($pane);
+    // Ensure that empty panes have some content.
+    if (empty($content->content)) {
+      // Get the administrative title.
+      $content_type = ctools_get_content_type($pane->type);
+      $title = ctools_content_admin_title($content_type, $pane->subtype, $pane->configuration, $this->display->context);
+
+      $content->content = t('Placeholder for empty "@title"', array('@title' => $title));
+      $pane->IPE_empty = TRUE;
+    }
+
+    return $content;
+  }
+
+  /**
+   * Add an 'empty' pane placeholder above all the normal panes.
+   *
+   * @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'] = '<div class="panels-ipe-placeholder panels-ipe-on panels-ipe-portlet-marker panels-ipe-portlet-static">' . $empty_ph . "</div>";
+
+    // 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, $this);
+
+    $output = parent::render_region($region_id, $panes);
+    $output = theme('panels_ipe_region_wrapper', $output, $region_id, $this->display);
+    $classes = 'panels-ipe-region';
+
+    ctools_include('cleanstring');
+    $region_id = ctools_cleanstring($region_id);
+    return "<div id='panels-ipe-regionid-$region_id' class='panels-ipe-region'>$output</div>";
+  }
+
+  /**
+   * AJAX entry point to create the controller form for an IPE.
+   */
+  function ajax_save_form($break = NULL) {
+    ctools_include('form');
+    if (!empty($this->cache->locked)) {
+      if ($break != 'break') {
+        $account  = user_load($this->cache->locked->uid);
+        $name     = theme('username', $account);
+        $lock_age = format_interval(time() - $this->cache->locked->updated);
+
+        $message = t("This panel is being edited by user !user, and is therefore locked from editing by others. This lock is !age old.\n\nClick OK to break this lock and discard any changes made by !user.", array('!user' => $name, '!age' => $lock_age));
+
+        $this->commands[] = array(
+          'command' => 'unlockIPE',
+          'message' => $message,
+          'break_path' => url($this->get_url('save-form', 'break'))
+        );
+        return;
+      }
+
+      // Break the lock.
+      panels_edit_cache_break_lock($this->cache);
+    }
+
+    $form_state = array(
+      'display' => &$this->display,
+      'content_types' => $this->cache->content_types,
+      'rerender' => FALSE,
+      'no_redirect' => TRUE,
+      // Panels needs this to make sure that the layout gets callbacks
+      'layout' => $this->plugins['layout'],
+    );
+
+    $output = ctools_build_form('panels_ipe_edit_control_form', $form_state);
+    if ($output) {
+      // At this point, we want to save the cache to ensure that we have a lock.
+      panels_edit_cache_set($this->cache);
+      $this->commands[] = array(
+        'command' => 'initIPE',
+        'key' => $this->clean_key,
+        'data' => $output,
+      );
+      return;
+    }
+
+    // no output == submit
+    if (!empty($form_state['clicked_button']['#save-display'])) {
+      // Saved. Save the cache.
+      panels_edit_cache_save($this->cache);
+    }
+    else {
+      // Cancelled. Clear the cache.
+      panels_edit_cache_clear($this->cache);
+    }
+
+    $this->commands[] = array(
+      'command' => 'endIPE',
+      'key' => $this->clean_key,
+      'data' => $output,
+    );
+  }
+
+  /**
+   * Create a command array to redraw a pane.
+   */
+  function command_update_pane($pid) {
+    if (is_object($pid)) {
+      $pane = $pid;
+    }
+    else {
+      $pane = $this->display->content[$pid];
+    }
+
+    $this->commands[] = ctools_ajax_command_replace("#panels-ipe-paneid-$pane->pid", $this->render_pane($pane));
+    $this->commands[] = ctools_ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
+  }
+
+  /**
+   * Create a command array to add a new pane.
+   */
+  function command_add_pane($pid) {
+    if (is_object($pid)) {
+      $pane = $pid;
+    }
+    else {
+      $pane = $this->display->content[$pid];
+    }
+
+    ctools_include('cleanstring');
+    $region_id = ctools_cleanstring($pane->panel);
+    $this->commands[] = ctools_ajax_command_append("#panels-ipe-regionid-$region_id div.panels-ipe-sort-container", $this->render_pane($pane));
+    $this->commands[] = ctools_ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
+  }
+}
+
+/**
+ * FAPI callback to create the Save/Cancel form for the IPE.
+ */
+function panels_ipe_edit_control_form(&$form_state) {
+  $display = &$form_state['display'];
+  // @todo -- this should be unnecessary as we ensure cache_key is set in add_meta()
+//  $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_regions($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;
+}
index c85d823..4adc7ca 100644 (file)
@@ -18,28 +18,7 @@ function _panels_mini_menu() {
     'file' => 'panels_mini.admin.inc',
   );
 
-  $items['admin/build/panel-mini'] = array(
-    'title' => 'Mini panels',
-    'page callback' => 'panels_mini_list_page',
-    'description' => 'Create and administer mini panels (panels exposed as blocks).',
-  ) + $base;
-  $items['admin/build/panel-mini/list'] = array(
-    'title' => 'List',
-    'page callback' => 'panels_mini_list_page',
-    'weight' => -10,
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/add'] = array(
-    'title' => 'Add',
-    'page callback' => 'panels_mini_add_page',
-    'type' => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/import'] = array(
-    'title' => 'Import',
-    'page callback' => 'panels_mini_import_mini',
-    'type' => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/settings'] = array(
+  $items['admin/build/mini-panels/settings'] = array(
     'title' => 'Settings',
     'page callback' => 'panels_mini_settings',
     'type' => MENU_LOCAL_TASK,
@@ -50,74 +29,6 @@ function _panels_mini_menu() {
     'page callback' => 'panels_mini_settings',
     'type' => MENU_LOCAL_TASK,
   ) + $base;
-  $items['admin/build/panel-mini/disable'] = array(
-    'page callback' => 'panels_mini_disable_page',
-    'weight' => -1,
-    'type' => MENU_CALLBACK,
-  ) + $base;
-  $items['admin/build/panel-mini/enable'] = array(
-    'page callback' => 'panels_mini_enable_page',
-    'weight' => -1,
-    'type' => MENU_CALLBACK,
-  ) + $base;
-
-  $base['access arguments'] = array('administer mini panels');
-
-  $items['admin/build/panel-mini/%panels_mini_admin'] = array(
-    'title' => 'Preview',
-    'page callback' => 'panels_mini_preview_panel',
-    'page arguments' => array(3),
-    'weight' => -10,
-    'type' => MENU_CALLBACK,
-  ) + $base;
-  $items['admin/build/panel-mini/%panels_mini_admin/preview'] = array(
-    'title' => 'Preview',
-    'page callback' => 'panels_mini_preview_panel',
-    'page arguments' => array(3),
-    'weight' => -10,
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/%panels_mini_admin/edit-layout'] = array(
-    'title' => 'Layout',
-    'page callback' => 'panels_mini_edit_layout',
-    'page arguments' => array(3),
-    'weight' => -9,
-    'type' => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/%panels_mini_admin/edit-general'] = array(
-    'title' => 'Settings',
-    'page callback' => 'panels_mini_edit',
-    'page arguments' => array(3),
-    'weight' => -5,
-    'type' => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/%panels_mini_admin/edit-context'] = array(
-    'title' => 'Context',
-    'page callback' => 'panels_mini_edit_context',
-    'page arguments' => array(3),
-    'weight' => -2,
-    'type' => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/%panels_mini_admin/edit-content'] = array(
-    'title' => 'Content',
-    'page callback' => 'panels_mini_edit_content',
-    'page arguments' => array(3),
-    'weight' => -1,
-    'type' => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/%panels_mini_admin/export'] = array(
-    'title' => 'Export',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('panels_mini_edit_export', 3),
-    'weight' => 0,
-    'type' => MENU_LOCAL_TASK,
-  ) + $base;
-  $items['admin/build/panel-mini/%panels_mini_admin/delete'] = array(
-    'title' => 'Delete mini panel',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('panels_mini_delete_confirm', 3),
-    'type' => MENU_CALLBACK,
-  ) + $base;
 
   return $items;
 }
@@ -126,553 +37,6 @@ function _panels_mini_menu() {
  * Settings for mini panels.
  */
 function panels_mini_settings() {
-  panels_load_include('common');
+  ctools_include('common', 'panels');
   return drupal_get_form('panels_common_settings', 'panels_mini');
 }
-
-/**
- * Provide a list of mini panels, with links to edit or delete them.
- */
-function panels_mini_list_page() {
-  panels_load_include('plugins');
-  $layouts = panels_get_layouts();
-  $items = array();
-  $sorts = array();
-
-  $header = array(
-    array('data' => t('Title'), 'field' => 'title'),
-    array('data' => t('Name'), 'field' => 'name', 'sort' => 'asc'),
-    array('data' => t('Type'), 'field' => 'type'),
-    t('Layout'),
-    t('Operations'),
-  );
-  $ts = tablesort_init($header);
-
-  // Load all mini panels and their displays.
-  $panel_minis = panels_mini_load_all();
-
-  foreach ($panel_minis as $panel_mini) {
-    $ops = array();
-    if (empty($panel_mini->disabled)) {
-      $ops[] = l(t('Edit'), "admin/build/panel-mini/$panel_mini->name/edit-general");
-      $ops[] = l(t('Export'), "admin/build/panel-mini/$panel_mini->name/export");
-    }
-    if ($panel_mini->type != t('Default')) {
-      $text = ($panel_mini->type == t('Overridden')) ? t('Revert') : t('Delete');
-      $ops[] = l($text, "admin/build/panel-mini/$panel_mini->name/delete");
-    }
-    else {
-      if (empty($panel_mini->disabled)) {
-        $ops[] = l(t('Disable'), "admin/build/panel-mini/disable/$panel_mini->name", array('query' => drupal_get_destination()));
-      }
-      else {
-        $ops[] = l(t('Enable'), "admin/build/panel-mini/enable/$panel_mini->name", array('query' => drupal_get_destination()));
-      }
-    }
-
-    $item = array();
-    $item[] = check_plain($panel_mini->title);
-    $item[] = check_plain($panel_mini->name);
-    // this is safe as it's always programmatic
-    $item[] = $panel_mini->type;
-
-    if (empty($panel_mini->display)) {
-      $panel_mini->display = $displays[$panel_mini->did];
-    }
-
-    $item[] = check_plain($layouts[$panel_mini->display->layout]['title']);
-    $item[] = implode(' | ', $ops);
-    $items[] = $item;
-    switch ($ts['sql']) {
-      case 'title':
-        $sorts[] = $item[0];
-        break;
-
-      case 'name':
-      default:
-        $sorts[] = $item[1];
-        break;
-
-      case 'type':
-        $sorts[] = $panel_mini->type . $item[0];
-        break;
-    }
-  }
-
-  if (drupal_strtolower($ts['sort']) == 'desc') {
-    arsort($sorts);
-  }
-  else {
-    asort($sorts);
-  }
-
-  $i = array();
-  foreach ($sorts as $id => $title) {
-    $i[] = $items[$id];
-  }
-
-  return theme('table', $header, $i);
-}
-
-/**
- * Provide a form to confirm deletion of a mini panel.
- */
-function panels_mini_delete_confirm(&$form_state, $panel_mini) {
-  if (!is_object($panel_mini)) {
-    $panel_mini = panels_mini_load($panel_mini);
-  }
-  $form['name'] = array('#type' => 'value', '#value' => $panel_mini->name);
-  $form['did'] = array('#type' => 'value', '#value' => $panel_mini->did);
-  return confirm_form($form,
-    t('Are you sure you want to delete the mini panel "@title"?', array('@title' => $panel_mini->title)),
-    !empty($_GET['destination']) ? $_GET['destination'] : 'admin/build/panel-mini',
-    t('This action cannot be undone.'),
-    t('Delete'), t('Cancel')
-  );
-}
-
-/**
- * Handle the submit button to delete a mini panel.
- */
-function panels_mini_delete_confirm_submit($form, &$form_state) {
-  $mini = panels_mini_load($form_state['values']['name']);
-  panels_mini_delete($mini);
-  $form_state['redirect'] = 'admin/build/panel-mini';
-}
-
-/**
- * Provide an administrative preview of a mini panel.
- */
-function panels_mini_preview_panel($mini) {
-  $mini->display->args = array();
-  $mini->display->css_id = panels_mini_get_id($mini->name);
-
-  ctools_include('context');
-  $mini->context = $mini->display->context = ctools_context_load_contexts($mini);
-
-  drupal_set_title(filter_xss_admin($mini->title));
-  return panels_render_display($mini->display);
-}
-
-/**
- * Page callback to export a mini panel to PHP code.
- */
-function panels_mini_edit_export(&$form_state, $panel_mini) {
-  if (!is_object($panel_mini)) {
-    $panel_mini = panels_mini_load($panel_mini);
-  }
-  drupal_set_title(check_plain($panel_mini->title));
-  $code = panels_mini_export($panel_mini);
-
-  $lines = substr_count($code, "\n");
-  $form['code'] = array(
-    '#type' => 'textarea',
-    '#title' => $panel_mini->title,
-    '#default_value' => $code,
-    '#rows' => $lines,
-  );
-  return $form;
-}
-
-/**
- * Page callback to import a mini panel from PHP code.
- */
-function panels_mini_import_mini() {
-  if (isset($_POST['form_id']) && $_POST['form_id'] == 'panels_mini_edit_form') {
-    $panel_mini = unserialize($_SESSION['pm_import']);
-    drupal_set_title(t('Import panel mini "@s"', array('@s' => $panel_mini->title)));
-    return drupal_get_form('panels_mini_edit_form', $panel_mini);
-  }
-
-  return drupal_get_form('panels_mini_import_form');
-}
-
-/**
- * Form for the mini panel import.
- */
-function panels_mini_import_form() {
-  $form['panel_mini'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Panel mini code'),
-    '#cols' => 60,
-    '#rows' => 15,
-    '#description' => t('Cut and paste the results of an exported mini panel here.'),
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Import'),
-  );
-
-  $form['#redirect'] = NULL;
-  return $form;
-}
-
-/**
- * Handle the submit button on importing a mini panel.
- */
-function panels_mini_import_form_submit($form, &$form_state) {
-  ob_start();
-  eval($form_state['values']['panel_mini']);
-  ob_end_clean();
-
-  if (isset($mini)) {
-    drupal_set_title(t('Import mini panel "@s"', array('@s' => $mini->title)));
-    $mini->pid = 'new';
-    // As $mini contains non-stdClass objects,
-    // it needs to be serialized before being stored in the session variable.
-    $_SESSION['pm_import'] = serialize($mini);
-    $output = drupal_get_form('panels_mini_edit_form', $mini);
-    print theme('page', $output);
-    exit;
-  }
-  else {
-    drupal_set_message(t('Unable to get a mini panel out of that.'));
-  }
-}
-
-/**
- * Handle the add mini panel page.
- */
-function panels_mini_add_page($layout = NULL) {
-  panels_load_include('plugins');
-  panels_load_include('common');
-  $layouts = panels_common_get_allowed_layouts('panels_mini');
-  $output = '';
-
-  if ($layout === NULL) {
-    foreach ($layouts as $id => $layout) {
-      $output .= panels_print_layout_link($id, $layout, $_GET['q'] .'/'. $id);
-    }
-    return $output;
-  }
-
-  if (!$layouts[$layout]) {
-    return drupal_not_found();
-  }
-
-  $panel_mini = new stdClass();
-  $panel_mini->display = panels_new_display();
-  $panel_mini->display->layout = $layout;
-  $panel_mini->pid = 'new';
-  $panel_mini->did = 'new';
-  $panel_mini->title = '';
-  $panel_mini->name = '';
-  $panel_mini->category = '';
-
-  drupal_set_title(t('Add mini panel'));
-  return panels_mini_edit($panel_mini);
-}
-
-/**
- * Edit a mini panel.
- *
- * Called from both the add and edit points to provide for common flow.
- */
-function panels_mini_edit($panel_mini) {
-  if (!is_object($panel_mini)) {
-    $panel_mini = panels_mini_load($panel_mini);
-  }
-  if ($panel_mini) {
-    drupal_set_title(check_plain($panel_mini->title));
-  }
-
-  return drupal_get_form('panels_mini_edit_form', $panel_mini);
-}
-
-/**
- * Form to edit the settings of a mini panel.
- */
-function panels_mini_edit_form(&$form_state, $panel_mini) {
-  panels_load_include('common');
-  panels_load_include('plugins');
-  drupal_add_css(panels_get_path('css/panels_admin.css'));
-
-  $form['pid'] = array(
-    '#type' => 'value',
-    '#value' => isset($panel_mini->pid) ? $panel_mini->pid : '',
-  );
-
-  $form['panel_mini'] = array(
-    '#type' => 'value',
-    '#value' => $panel_mini,
-  );
-
-  $form['right'] = array(
-    '#prefix' => '<div class="layout-container">',
-    '#suffix' => '</div>',
-  );
-  $form['left'] = array(
-    '#prefix' => '<div class="info-container">',
-    '#suffix' => '</div>',
-  );
-
-  $form['left']['settings'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Settings'),
-  );
-  $form['left']['settings']['title'] = array(
-    '#type' => 'textfield',
-    '#size' => 24,
-    '#default_value' => $panel_mini->title,
-    '#title' => t('Mini panel title'),
-    '#description' => t('The title for this mini panel. It can be overridden in the block configuration.'),
-  );
-
-  $form['left']['settings']['name'] = array(
-    '#type' => 'textfield',
-    '#size' => 24,
-    '#default_value' => $panel_mini->name,
-    '#title' => t('Mini panel name'),
-    '#maxlength' => 32,
-    '#description' => t('A unique name used to identify this panel page internally. It must be only be alpha characters and underscores. No spaces, numbers or uppercase characters.'),
-  );
-
-  $form['left']['settings']['category'] = array(
-    '#type' => 'textfield',
-    '#size' => 24,
-    '#default_value' => $panel_mini->category,
-    '#title' => t('Mini panel category'),
-    '#description' => t("The category that this mini-panel will be grouped into on the Add Content form. Only upper and lower-case alphanumeric characters are allowed. If left blank, defaults to 'Mini panels'."),
-  );
-
-  ctools_include('context');
-  $panel_mini->context = $panel_mini->display->context = ctools_context_load_contexts($panel_mini);
-
-  $form['right']['layout'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Layout'),
-  );
-
-  $layout = panels_get_layout($panel_mini->display->layout);
-
-  $form['right']['layout']['layout-icon'] = array(
-    '#value' => panels_print_layout_icon($panel_mini->display->layout, $layout),
-  );
-
-  $form['right']['layout']['layout-display'] = array(
-    '#value' => check_plain($layout['title']),
-  );
-  $form['right']['layout']['layout-content'] = array(
-    '#value' => theme('panels_common_content_list', $panel_mini->display),
-  );
-
-  $contexts = theme('panels_common_context_list', $panel_mini);
-  if ($contexts) {
-    $form['right']['context'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Contexts'),
-    );
-
-    $form['right']['context']['context'] = array(
-      '#value' => $contexts,
-    );
-  }
-
-  $label = (!empty($panel_mini->pid) && $panel_mini->pid == 'new') ? t('Save and proceed') : t('Save');
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => $label,
-  );
-
-  return $form;
-}
-
-/**
- * Validate submission of the mini panel edit form.
- */
-function panels_mini_edit_form_validate($form, &$form_state) {
-  // Test uniqueness of name:
-  if (!$form_state['values']['name']) {
-    form_error($form['left']['settings']['name'], t('Panel mini name is required.'));
-  }
-  else if (preg_match("/[^A-Za-z0-9_]/", $form_state['values']['name'])) {
-    form_error($form['left']['settings']['name'], t('Name must be alphanumeric or underscores only.'));
-  }
-  else if (preg_match("/[^A-Za-z0-9 ]/", $form_state['values']['category'])) {
-    form_error($form['left']['settings']['category'], t('Categories may contain only alphanumerics or spaces.'));
-  }
-  else {
-    $query = "SELECT pid FROM {panels_mini} WHERE name = '%s'";
-    if (!empty($form_state['values']['pid']) && is_numeric($form_state['values']['pid'])) {
-      $query .= ' AND pid != ' . $form_state['values']['pid'];
-    }
-    if (db_result(db_query($query, $form_state['values']['name']))) {
-      form_error($form['left']['settings']['name'], t('Panel name must be unique.'));
-    }
-  }
-}
-
-/**
- * Process submission of the mini panel edit form.
- */
-function panels_mini_edit_form_submit($form, &$form_state) {
-  $panel_mini = $form_state['values']['panel_mini'];
-  if (isset($panel_mini->pid) && $panel_mini->pid != 'new' && $panel_mini->name != $form_state['values']['name']) {
-    // update all existing mini panels to point to this one appropriately.
-    db_query("UPDATE {blocks} b SET delta = '%s' WHERE b.module = 'panels_mini' AND b.delta = '%s'", $form_state['values']['name'], $panel_mini->name);
-
-    // Above was blocks; these are actual panel panes.
-    $result = db_query("SELECT * FROM {panels_pane} WHERE type = 'panels_mini' and subtype = '%s'", $panel_mini->name);
-    while ($pane = db_fetch_object($result)) {
-      $conf = unserialize($pane->configuration);
-      $conf['name'] = $form_state['values']['name'];
-      db_query("UPDATE {panels_pane} SET configuration = '%s', subtype = '%s' WHERE pid = %d", serialize($conf), $conf['name'], $pane->pid);
-    }
-  }
-
-  $panel_mini->title = $form_state['values']['title'];
-  $panel_mini->name = $form_state['values']['name'];
-  $panel_mini->category = empty($form_state['values']['category']) ? '' : $form_state['values']['category'];
-
-  if (isset ($panel_mini->pid) && $panel_mini->pid == 'new') {
-    unset($_SESSION['pm_import']);
-    drupal_set_message(t('Your new mini panel %title has been saved.', array('%title' => $panel_mini->title)));
-    panels_mini_save($panel_mini);
-    $form_state['values']['pid'] = $panel_mini->pid;
-
-    $layout = panels_get_layout($panel_mini->display->layout);
-
-    $form_state['redirect'] = "admin/build/panel-mini/$panel_mini->name/edit-context/next";
-  }
-  else {
-    drupal_set_message(t('Your changes have been saved.'));
-    panels_mini_save($panel_mini);
-  }
-}
-
-/**
- * Form to edit context features of a mini panel.
- */
-function panels_mini_edit_context($panel_mini, $next = NULL) {
-  ctools_include('context-admin');
-  ctools_context_admin_includes();
-
-  if (!empty($_POST)) {
-    $panel_mini = ctools_object_cache_get('context_object:panel_mini', $panel_mini->name);
-  }
-  else {
-    ctools_object_cache_set('context_object:panel_mini', $panel_mini->name, $panel_mini);
-  }
-
-  drupal_set_title(check_plain($panel_mini->title));
-  return drupal_get_form('panels_mini_context_form', $panel_mini, $next);
-}
-
-/**
- * Form to edit the context settings of a mini panel.
- */
-function panels_mini_context_form(&$form_state, $panel_mini, $next = NULL) {
-  drupal_add_css(panels_get_path('css/panels_admin.css'));
-  panels_load_include('plugins');
-  $layout = panels_get_layout($panel_mini->display->layout);
-
-  $form['panel_mini'] = array(
-    '#type' => 'value',
-    '#value' => $panel_mini,
-  );
-
-  $form_state['panel_mini'] = $panel_mini;
-
-  $form['right'] = array(
-    '#prefix' => '<div class="right-container">',
-    '#suffix' => '</div>',
-  );
-
-  $form['left'] = array(
-    '#prefix' => '<div class="left-container">',
-    '#suffix' => '</div>',
-  );
-
-  ctools_context_add_context_form('panel_mini', $form, $form_state, $form['right']['contexts_table'], $panel_mini);
-  ctools_context_add_required_context_form('panel_mini', $form, $form_state, $form['left']['required_contexts_table'], $panel_mini);
-  ctools_context_add_relationship_form('panel_mini', $form, $form_state, $form['right']['relationships_table'], $panel_mini);
-
-  $label = $next ? t('Save and proceed') : t('Save');
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => $label,
-  );
-
-  return $form;
-}
-
-/**
- * Process submission of the mini panel edit form.
- */
-function panels_mini_context_form_submit($form, &$form_state) {
-  $panel_mini = $form_state['panel_mini'];
-
-  drupal_set_message(t('Your changes have been saved.'));
-  panels_mini_save($panel_mini);
-  ctools_object_cache_clear('context_object:panel_mini', $panel_mini->name);
-  if ($form_state['values']['submit'] == t('Save and proceed')) {
-    $form_state['redirect'] = "admin/build/panel-mini/$panel_mini->name/edit-content";
-  }
-}
-
-/**
- * Enable a default mini panel.
- */
-function panels_mini_enable_page($name = NULL) {
-  ctools_include('export');
-  ctools_export_set_status('panels_mini', $name, FALSE);
-  drupal_goto();
-}
-
-/**
- * Disable a default mini panel.
- */
-function panels_mini_disable_page($name = NULL) {
-  ctools_include('export');
-  ctools_export_set_status('panels_mini', $name, TRUE);
-  drupal_goto();
-}
-
-/**
- * Pass through to the panels content editor.
- */
-function panels_mini_edit_content($panel_mini) {
-  if (!is_object($panel_mini)) {
-    $panel_mini = panels_mini_load($panel_mini);
-  }
-  ctools_include('context');
-  // Collect a list of contexts required by the arguments on this page.
-  $panel_mini->display->context = $contexts = ctools_context_load_contexts($panel_mini);
-
-  panels_load_include('common');
-  $content_types = panels_common_get_allowed_types('panels_mini', $contexts);
-
-  $output = panels_edit($panel_mini->display, NULL, $content_types);
-  if (is_object($output)) {
-    $panel_mini->display = $output;
-    $panel_mini->did = $output->did;
-    panels_mini_save($panel_mini);
-    drupal_goto("admin/build/panel-mini/$panel_mini->name/edit-content");
-  }
-  // Print this with theme('page') so that blocks are disabled while editing a display.
-  // This is important because negative margins in common block layouts (i.e, Garland)
-  // messes up the drag & drop.
-  drupal_set_title(check_plain($panel_mini->title));
-  print theme('page', $output, FALSE);
-}
-
-/**
- * Pass through to the panels layout editor.
- */
-function panels_mini_edit_layout($panel_mini) {
-  if (!is_object($panel_mini)) {
-    $panel_mini = panels_mini_load($panel_mini);
-  }
-
-  $output = panels_edit_layout($panel_mini->display, t('Save'), NULL, 'panels_mini');
-  if (is_object($output)) {
-    $panel_mini->display = $output;
-    $panel_mini->did = $output->did;
-    panels_mini_save($panel_mini);
-    drupal_goto("admin/build/panel-mini/$panel_mini->name/edit-layout");
-  }
-
-  drupal_set_title(check_plain($panel_mini->title));
-  return $output;
-}
index 31b9c01..d26c9b6 100644 (file)
@@ -19,6 +19,10 @@ function panels_mini_schema_1() {
   $schema['panels_mini'] = array(
     'export' => array(
       'identifier' => 'mini',
+      'load callback' => 'panels_mini_load',
+      'load all callback' => 'panels_mini_load_all',
+      'save callback' => 'panels_mini_save',
+      'delete callback' => 'panels_mini_delete',
       'export callback' => 'panels_mini_export',
       'api' => array(
         'owner' => 'panels_mini',
@@ -32,40 +36,54 @@ function panels_mini_schema_1() {
         'type' => 'serial',
         'not null' => TRUE,
         'no export' => TRUE,
+        'description' => 'The primary key for uniqueness.',
       ),
       'name' => array(
         'type' => 'varchar',
         'length' => '255',
+        'description' => 'The unique name of the mini panel.',
       ),
       'category' => array(
         'type' => 'varchar',
         'length' => '64',
+        'description' => 'The category this mini panel appears in on the add content pane.',
       ),
       'did' => array(
         'type' => 'int',
         'no export' => TRUE,
+        'description' => 'The display ID of the panel.',
       ),
-      'title' => array(
+      'admin_title' => array(
         'type' => 'varchar',
         'length' => '128',
+        'description' => 'The administrative title of the mini panel.',
+      ),
+      'admin_description' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'description' => 'Administrative title of this mini panel.',
+        'object default' => '',
       ),
       'requiredcontexts' => array(
         'type' => 'text',
         'size' => 'big',
         'serialize' => TRUE,
         'object default' => array(),
+        'description' => 'An array of required contexts.',
       ),
       'contexts' => array(
         'type' => 'text',
         'size' => 'big',
         'serialize' => TRUE,
         'object default' => array(),
+        'description' => 'An array of contexts embedded into the panel.',
       ),
       'relationships' => array(
         'type' => 'text',
         'size' => 'big',
         'serialize' => TRUE,
         'object default' => array(),
+        'description' => 'An array of relationships embedded into the panel.',
       ),
     ),
     'primary key' => array('pid'),
@@ -88,7 +106,7 @@ function panels_mini_install() {
  * Implementation of hook_uninstall().
  */
 function panels_mini_uninstall() {
-  $result = db_query("SELECT * FROM {panels_mini} ORDER BY title");
+  $result = db_query("SELECT * FROM {panels_mini}");
   $panels_exists = db_table_exists('panels_display');
   while ($panel_mini = db_fetch_object($result)) {
     // Delete all associated displays.
@@ -118,3 +136,51 @@ function panels_mini_update_6300() {
   }
   return $ret;
 }
+
+/**
+ * Update all panel mini blocks to not use block caching.
+ */
+function panels_mini_update_6301() {
+  $ret = array();
+  $ret[] = update_sql("UPDATE {blocks} SET cache = -1 WHERE module = 'panels_mini'");
+  return $ret;
+}
+
+/**
+ * Add the admin description field.
+ */
+function panels_mini_update_6302() {
+  $ret = array();
+  $field = array(
+    'type' => 'text',
+    'size' => 'big',
+    'description' => 'Administrative description of this mini panel.',
+    'object default' => '',
+  );
+
+  db_add_field($ret, 'panels_mini', 'admin_description', $field);
+  return $ret;
+}
+
+/**
+ * Add the admin description field.
+ */
+function panels_mini_update_6303() {
+  $ret = array();
+  $field = array(
+    'type' => 'varchar',
+    'length' => '128',
+    'description' => 'The administrative title of the mini panel.',
+  );
+
+  db_add_field($ret, 'panels_mini', 'admin_title', $field);
+
+  $result = db_query("SELECT pid, did, title FROM {panels_mini}");
+  while ($mini = db_fetch_object($result)) {
+    db_query("UPDATE {panels_mini} SET admin_title = '%s' WHERE pid = %d", $mini->title, $mini->pid);
+    db_query("UPDATE {panels_display} SET title = '%s' WHERE did = %d", $mini->title, $mini->pid);
+  }
+
+  db_drop_field($ret, 'panels_mini', 'title');
+  return $ret;
+}
index a039b23..7ffaf88 100644 (file)
@@ -48,7 +48,8 @@ function panels_mini_block($op = 'list', $delta = 0, $edit = array()) {
     foreach ($minis as $panel_mini) {
       if (empty($mini->disabled) && empty($mini->requiredcontext)) {
         $blocks[$panel_mini->name] = array(
-          'info' => t('Mini panel: "@title"', array('@title' => $panel_mini->title)),
+          'info' => t('Mini panel: "@title"', array('@title' => $panel_mini->admin_title)),
+          'cache' => BLOCK_NO_CACHE,
         );
       }
     }
@@ -61,25 +62,33 @@ function panels_mini_block($op = 'list', $delta = 0, $edit = array()) {
     if (!empty($viewing[$delta])) {
       return;
     }
-    ctools_include('context');
-
     $viewing[$delta] = TRUE;
+
     $panel_mini = panels_mini_load($delta);
+    if (empty($panel_mini)) {
+      // Bail out early if the specified mini panel doesn't exist.
+      return;
+    }
+
+    ctools_include('context');
     $panel_mini->context = $panel_mini->display->context = ctools_context_load_contexts($panel_mini);
     $panel_mini->display->css_id = panels_mini_get_id($panel_mini->name);
 
     $block = array();
 
     $block['content'] = panels_render_display($panel_mini->display);
-    $block['subject'] = panels_display_get_title($panel_mini->display);
-    // If NULL, fall back to this title:
-    if (!isset($block['subject'])) {
-      $block['subject'] = $panel_mini->title;
-    }
+    $block['subject'] = $panel_mini->display->get_title();
 
     unset($viewing[$delta]);
     return $block;
   }
+  elseif ($op = 'configure') {
+    return array(
+      'admin-shortcut' => array(
+        '#value' => l(t('Manage this mini-panel'), 'admin/build/panel-mini/' . $delta . '/edit-general')
+      ),
+    );
+  }
 }
 
 /**
@@ -105,44 +114,92 @@ function panels_mini_get_id($name) {
 /**
  * Create a new page with defaults appropriately set from schema.
  */
-function panels_mini_new() {
+function panels_mini_new($set_defaults = TRUE) {
   ctools_include('export');
-  return ctools_export_new_object('panels_mini');
+  return ctools_export_new_object('panels_mini', $set_defaults);
 }
 
 /**
  * Load a single mini panel.
  */
 function panels_mini_load($name) {
-  ctools_include('export');
-  $result = ctools_export_load_object('panels_mini', 'names', array($name));
-  if (isset($result[$name])) {
-    if (empty($result[$name]->display)) {
-      $result[$name]->display = panels_load_display($result[$name]->did);
+  $cache = &ctools_static('panels_mini_load_all', array());
+
+  // We use array_key_exists because failed loads will be NULL and
+  // isset() will try to load it again.
+  if (!array_key_exists($name, $cache)) {
+    ctools_include('export');
+    $result = ctools_export_load_object('panels_mini', 'names', array($name));
+    if (isset($result[$name])) {
+      if (empty($result[$name]->display)) {
+        $result[$name]->display = panels_load_display($result[$name]->did);
+        if (!empty($result[$name]->title) && empty($result[$name]->display->title)) {
+          $result[$name]->display->title = $result[$name]->title;
+        }
+      }
+      $cache[$name] = $result[$name];
+      if (!empty($result[$name]->title) && empty($result[$name]->admin_title)) {
+        $cache[$name]->admin_title = $result[$name]->title;
+      }
     }
-    return $result[$name];
+    else {
+      $cache[$name] = NULL;
+    }
+  }
+
+  if (isset($cache[$name])) {
+    return $cache[$name];
   }
 }
 
 /**
  * Load all mini panels.
  */
-function panels_mini_load_all() {
-  ctools_include('export');
-  $minis = ctools_export_load_object('panels_mini');
-  $dids = array();
-  foreach ($minis as $mini) {
-    if (!empty($mini->did)) {
-      $dids[$mini->did] = $mini->name;
+function panels_mini_load_all($reset = FALSE) {
+  $cache = &ctools_static('panels_mini_load_all', array());
+  static $all_loaded = FALSE;
+
+  // We check our own private static because individual minis could have
+  // been loaded prior to load all and we need to know that.
+  if (!$all_loaded || $reset) {
+    $all_loaded = TRUE;
+    if ($reset) {
+      $cache = array();
+    }
+
+    ctools_include('export');
+    $minis = ctools_export_load_object('panels_mini');
+    $dids = array();
+    foreach ($minis as $mini) {
+      if (empty($cache[$mini->name])) {
+        if (!empty($mini->did)) {
+          $dids[$mini->did] = $mini->name;
+        }
+        else {
+        // Translate old style titles into new titles.
+          if (!empty($mini->title) && empty($mini->display->title)) {
+            $mini->display->title = $mini->title;
+          }
+        }
+        // Translate old style titles into new titles.
+        if (isset($mini->title) && empty($mini->admin_title)) {
+          $mini->admin_title = $mini->title;
+        }
+        $cache[$mini->name] = $mini;
+      }
     }
-  }
 
-  $displays = panels_load_displays(array_keys($dids));
-  foreach ($displays as $did => $display) {
-    $minis[$dids[$did]]->display = $display;
+    $displays = panels_load_displays(array_keys($dids));
+    foreach ($displays as $did => $display) {
+      if (!empty($cache[$dids[$did]]->title) && empty($display->title)) {
+        $display->title = $cache[$dids[$did]]->title;
+      }
+      $cache[$dids[$did]]->display = $display;
+    }
+    return $result[$name];
   }
 
-  return $minis;
+  return $cache;
 }
 
 /**
@@ -192,14 +249,153 @@ function panels_mini_ctools_block_info($module, $delta, &$info) {
 }
 
 /**
- * Menu callback to check to see if a mini panel is valid as part
- * of a path, and if it is, return the mini.
+ * Implementation of hook_ctools_plugin_directory() to let the system know
+ * we implement task and task_handler plugins.
+ */
+function panels_mini_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'ctools' && ($plugin == 'content_types' || $plugin == 'export_ui')) {
+    return 'plugins/' . $plugin;
+  }
+}
+
+/**
+ * Get the display cache for the panels_mini plugin.
  */
-function panels_mini_admin_load($name) {
-  $mini = panels_mini_load($name);
-  if ($mini && empty($mini->disabled)) {
-    return $mini;
+function _panels_mini_panels_cache_get($key) {
+  ctools_include('export-ui');
+  $plugin = ctools_get_export_ui('panels_mini');
+  $handler = ctools_export_ui_get_handler($plugin);
+  if (!$handler) {
+    return;
+  }
+
+  $item = $handler->edit_cache_get($key);
+  if (!$item) {
+    $item = ctools_export_crud_load($handler->plugin['schema'], $key);
   }
+
+  return array($handler, $item);
+}
+
+/**
+ * Get display edit cache for the panels mini export UI
+ *
+ * The key is the second half of the key in this form:
+ * panels_mini:TASK_NAME:HANDLER_NAME;
+ */
+function panels_mini_panels_cache_get($key) {
+  ctools_include('common', 'panels');
+  list($handler, $item) = _panels_mini_panels_cache_get($key);
+  if (isset($item->mini_panels_display_cache)) {
+    return $item->mini_panels_display_cache;
+  }
+
+  $cache = new stdClass();
+  $cache->display = $item->display;
+  $cache->display->context = ctools_context_load_contexts($item);
+  $cache->display->cache_key = 'panels_mini:' . $key;
+  $cache->content_types = panels_common_get_allowed_types('panels_mini', $cache->display->context);
+  $cache->display_title = TRUE;
+
+  // @TODO support locking
+  $cache->locked = FALSE;
+
+  return $cache;
+}
+
+/**
+ * Store a display edit in progress in the page cache.
+ */
+function panels_mini_panels_cache_set($key, $cache) {
+  list($handler, $item) = _panels_mini_panels_cache_get($key);
+  $item->mini_panels_display_cache = $cache;
+  $handler->edit_cache_set_key($item, $key);
+}
+
+/**
+ * Save all changes made to a display using the panels mini UI cache.
+ */
+function panels_mini_panels_cache_clear($key, $cache) {
+  list($handler, $item) = _panels_mini_panels_cache_get($key);
+  $handler->edit_cache_clear($item);
+}
+
+/**
+ * Save all changes made to a display using the panels mini UI cache.
+ */
+function panels_mini_panels_cache_save($key, $cache) {
+  list($handler, $item) = _panels_mini_panels_cache_get($key);
+  $item->display = $cache->display;
+  panels_mini_save($item);
+
+  $handler->edit_cache_clear($item);
+}
+
+/**
+ * Break the lock on a panels mini page.
+ */
+function panels_mini_panels_cache_break_lock($key, $cache) {
+}
+
+/**
+ * Alter default mini panels to ensure they have new fields and avoid warnings.
+ */
+function panels_mini_default_panels_mini_alter(&$minis) {
+  foreach ($minis as $name => $mini) {
+    if (!isset($mini->admin_description)) {
+      $minis[$name]->admin_description = '';
+    }
+  }
+}
+
+/**
+ * Implementation of hook_panels_dashboard_blocks().
+ *
+ * Adds mini panels information to the Panels dashboard.
+ */
+function panels_mini_panels_dashboard_blocks(&$vars) {
+  $vars['links']['panels_mini'] = array(
+    'title' => l(t('Mini panel'), 'admin/build/mini-panels/add'),
+    'description' => t('Mini panels are small content areas exposed as blocks, for when you need to have complex block layouts or layouts within layouts.'),
+    'weight' => -1,
+  );
+
+  // Load all mini panels and their displays.
+  $panel_minis = panels_mini_load_all();
+  $count = 0;
+  $rows = array();
+
+  foreach ($panel_minis as $panel_mini) {
+    $rows[] = array(
+      check_plain($panel_mini->admin_title),
+      array(
+        'data' => l(t('Edit'), "admin/build/mini-panels/list/$panel_mini->name/edit"),
+        'class' => 'links',
+      ),
+    );
+
+    // Only show 10.
+    if (++$count >= 10) {
+      break;
+    }
+  }
+
+  if ($rows) {
+    $content = theme('table', array(), $rows, array('class' => 'panels-manage'));
+  }
+  else {
+    $content = '<p>' . t('There are no mini panels.') . '</p>';
+  }
+
+  $vars['blocks']['panels_mini'] = array(
+    'weight' => -100,
+    'title' => t('Manage mini panels'),
+    'link' => l(t('Go to list'), 'admin/build/mini-panels'),
+    'content' => $content,
+    'class' => 'dashboard-mini-panels',
+    'section' => 'left',
+  );
+
 }
 
 /**
@@ -220,4 +416,4 @@ function panels_mini_panels_mini_list() {
     $list[$name] = check_plain($name) . ' (' . check_plain($panel->title) . ')';
   }
   return $list;
-}
\ No newline at end of file
+}
index ee47f68..6f57822 100644 (file)
@@ -7,7 +7,7 @@
  * need to be broken out into a .inc file, it's convenient that we do so
  * that we don't load code unneccessarily. Plus it demonstrates plugins
  * in modules other than Panels itself.
- * 
+ *
  */
 
 /**
 function panels_mini_panels_mini_ctools_content_types() {
   return array(
     'title' => t('Mini panels'),
+    'content type' => 'panels_mini_panels_mini_content_type_content_type',
   );
 }
 
 /**
  * Return each available mini panel available as a subtype.
  */
+function panels_mini_panels_mini_content_type_content_type($subtype_id, $plugin) {
+  $mini = panels_mini_load($subtype_id);
+  return _panels_mini_panels_mini_content_type_content_type($mini);
+}
+
+/**
+ * Return each available mini panel available as a subtype.
+ */
 function panels_mini_panels_mini_content_type_content_types($plugin) {
   $types = array();
   foreach (panels_mini_load_all() as $mini) {
-    if (!empty($mini->disabled)) {
-      continue;
-    }
-
-    $types[$mini->name] = array(
-      'title' => filter_xss_admin($mini->title),
-      // For now mini panels will just use the contrib block icon.
-      'icon' => 'icon_mini_panel.png',
-      'description' => filter_xss_admin($mini->title),
-      'category' => !empty($mini->category) ? filter_xss_admin($mini->category) : t('Mini panel'),
-    );
-    if (!empty($mini->requiredcontexts)) {
-      $types[$mini->name]['required context'] = array();
-      foreach ($mini->requiredcontexts as $context) {
-        $info = ctools_get_context($context['name']);
-        // TODO: allow an optional setting
-        $types[$mini->name]['required context'][] = new ctools_context_required($context['identifier'], $info['context name']);
-      }
+    $type = _panels_mini_panels_mini_content_type_content_type($mini);
+    if ($type) {
+      $types[$mini->name] = $type;
     }
   }
   return $types;
 }
 
+/**
+ * Return an info array describing a single mini panel.
+ */
+function _panels_mini_panels_mini_content_type_content_type($mini) {
+  if (!empty($mini->disabled)) {
+    return;
+  }
+
+  $title = filter_xss_admin($mini->admin_title);
+  $type = array(
+    'title' => $title,
+    // For now mini panels will just use the contrib block icon.
+    'icon' => 'icon_mini_panel.png',
+    'description' => $title,
+    'category' => !empty($mini->category) ? $mini->category : t('Mini panel'),
+  );
+  if (!empty($mini->requiredcontexts)) {
+    $type['required context'] = array();
+    foreach ($mini->requiredcontexts as $context) {
+      $info = ctools_get_context($context['name']);
+      // TODO: allow an optional setting
+      $type['required context'][] = new ctools_context_required($context['identifier'], $info['context name']);
+    }
+  }
+  return $type;
+}
 
 /**
  * Render a mini panel called from a panels display.
@@ -82,8