summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt4
-rw-r--r--CHANGELOG.txt102
-rw-r--r--D6UPDATE.txt4
-rw-r--r--TODO.txt132
-rw-r--r--css/panels-dashboard.css37
-rw-r--r--css/panels_admin.css12
-rw-r--r--css/panels_dnd.css16
-rw-r--r--docs/docs.inc116
-rw-r--r--docs/doxygen.css485
-rw-r--r--docs/sample_plugin_ct.inc902
-rw-r--r--help/api.html44
-rw-r--r--help/panels.help.ini72
-rw-r--r--help/plugins-layout.html78
-rw-r--r--help/plugins-style.html (renamed from help/plugin-style.html)0
-rw-r--r--includes/callbacks.inc201
-rw-r--r--includes/common.inc104
-rw-r--r--includes/display-edit.inc1788
-rw-r--r--includes/display-layout.inc82
-rw-r--r--includes/display-render.inc260
-rw-r--r--includes/legacy.inc71
-rw-r--r--includes/page-wizard.inc68
-rw-r--r--includes/panels.pipelines.inc34
-rw-r--r--includes/plugins.inc217
-rw-r--r--js/display_editor.js6
-rw-r--r--panels.install227
-rw-r--r--panels.module1121
-rw-r--r--panels2.doxy292
-rw-r--r--panels_ipe/css/panels_ipe.css209
-rw-r--r--panels_ipe/images/dragger.pngbin0 -> 946 bytes
-rw-r--r--panels_ipe/includes/panels_ipe.pipelines.inc45
-rw-r--r--panels_ipe/js/panels_ipe.js253
-rw-r--r--panels_ipe/panels_ipe.info7
-rw-r--r--panels_ipe/panels_ipe.module147
-rw-r--r--panels_ipe/plugins/display_renderers/ipe.inc8
-rw-r--r--panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php253
-rw-r--r--panels_mini/panels_mini.admin.inc640
-rw-r--r--panels_mini/panels_mini.install70
-rw-r--r--panels_mini/panels_mini.module266
-rw-r--r--panels_mini/plugins/content_types/panels_mini.inc62
-rw-r--r--panels_mini/plugins/export_ui/panels_mini.inc42
-rw-r--r--panels_mini/plugins/export_ui/panels_mini_ui.class.php292
-rw-r--r--panels_node/panels_node.install18
-rw-r--r--panels_node/panels_node.module106
-rw-r--r--plugins/cache/simple.inc4
-rw-r--r--plugins/display_renderers/editor.inc8
-rw-r--r--plugins/display_renderers/legacy.inc7
-rw-r--r--plugins/display_renderers/panels_renderer_editor.class.php1893
-rw-r--r--plugins/display_renderers/panels_renderer_legacy.class.php231
-rw-r--r--plugins/display_renderers/panels_renderer_simple.class.php32
-rw-r--r--plugins/display_renderers/panels_renderer_single_pane.class.php41
-rw-r--r--plugins/display_renderers/panels_renderer_standard.class.php605
-rw-r--r--plugins/display_renderers/simple.inc11
-rw-r--r--plugins/display_renderers/single_pane.inc8
-rw-r--r--plugins/display_renderers/standard.inc7
-rw-r--r--plugins/export_ui/panels_layouts.inc25
-rw-r--r--plugins/export_ui/panels_layouts_ui.class.php231
-rw-r--r--plugins/layouts/flexible/flexible-admin.css9
-rw-r--r--plugins/layouts/flexible/flexible-admin.js3
-rw-r--r--plugins/layouts/flexible/flexible.css59
-rw-r--r--plugins/layouts/flexible/flexible.inc878
-rw-r--r--plugins/layouts/onecol/onecol.inc1
-rw-r--r--plugins/layouts/threecol_25_50_25/threecol_25_50_25.inc1
-rw-r--r--plugins/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.tpl.php18
-rw-r--r--plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.inc1
-rw-r--r--plugins/layouts/threecol_33_34_33/threecol_33_34_33.inc1
-rw-r--r--plugins/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.tpl.php18
-rw-r--r--plugins/layouts/twocol/twocol.inc1
-rw-r--r--plugins/layouts/twocol_bricks/twocol_bricks.inc1
-rw-r--r--plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php19
-rw-r--r--plugins/layouts/twocol_stacked/twocol_stacked.inc1
-rw-r--r--plugins/page_wizards/landing_page.inc272
-rw-r--r--plugins/page_wizards/node_override.inc290
-rw-r--r--plugins/style_bases/pane/pane_plain_box/icon.pngbin0 -> 3022 bytes
-rw-r--r--plugins/style_bases/pane/pane_plain_box/pane-plain-box.css11
-rw-r--r--plugins/style_bases/pane/pane_plain_box/pane-plain-box.tpl.php17
-rw-r--r--plugins/style_bases/pane/pane_plain_box/pane_plain_box.inc94
-rw-r--r--plugins/style_bases/pane/pane_rounded_shadow/box-color.pngbin0 -> 4772 bytes
-rw-r--r--plugins/style_bases/pane/pane_rounded_shadow/box-shadow.pngbin0 -> 2528 bytes
-rw-r--r--plugins/style_bases/pane/pane_rounded_shadow/icon.pngbin0 -> 3566 bytes
-rw-r--r--plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.css97
-rw-r--r--plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.tpl.php32
-rw-r--r--plugins/style_bases/pane/pane_rounded_shadow/pane_rounded_shadow.inc100
-rw-r--r--plugins/style_bases/region/region_plain_box/icon.pngbin0 -> 3022 bytes
-rw-r--r--plugins/style_bases/region/region_plain_box/region-plain-box.css6
-rw-r--r--plugins/style_bases/region/region_plain_box/region-plain-box.tpl.php29
-rw-r--r--plugins/style_bases/region/region_plain_box/region_plain_box.inc66
-rw-r--r--plugins/style_bases/region/region_rounded_shadow/box-color.pngbin0 -> 4772 bytes
-rw-r--r--plugins/style_bases/region/region_rounded_shadow/box-shadow.pngbin0 -> 2528 bytes
-rw-r--r--plugins/style_bases/region/region_rounded_shadow/icon.pngbin0 -> 3566 bytes
-rw-r--r--plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.css89
-rw-r--r--plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.tpl.php29
-rw-r--r--plugins/style_bases/region/region_rounded_shadow/region_rounded_shadow.inc72
-rw-r--r--plugins/styles/block.inc4
-rw-r--r--plugins/styles/naked.inc1
-rw-r--r--plugins/styles/stylizer.inc346
-rw-r--r--plugins/task_handlers/panel_context.inc110
-rw-r--r--plugins/views/panels.views.inc31
-rw-r--r--plugins/views/panels_views_plugin_row_fields.inc193
-rw-r--r--templates/panels-dashboard-block.tpl.php14
-rw-r--r--templates/panels-dashboard-link.tpl.php13
-rw-r--r--templates/panels-dashboard.tpl.php63
-rw-r--r--translations/panels.fr.po2
-rw-r--r--translations/panels.hu.po1070
103 files changed, 10537 insertions, 5521 deletions
diff --git a/API.txt b/API.txt
deleted file mode 100644
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/.
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index dfee568..6722594 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -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.
diff --git a/D6UPDATE.txt b/D6UPDATE.txt
index 8bda859..d340127 100644
--- a/D6UPDATE.txt
+++ b/D6UPDATE.txt
@@ -18,7 +18,7 @@ administrative view of a layout can now have its own theme function.
CONTENT TYPES
=============
-panels_node_legacy module renamed to panels_node_content.module
+panels_node_legacy module renamed to panels_node_content.module
-- NEED UPDATE TO RENAME IN SYSTEM TABLE.
'title callback' now has $subtype as the first argument.
@@ -49,4 +49,4 @@ Moved to CTOOLS
When argument plugins fail to load a context at runtime, they must now return
error codes instead of FALSE or NULL (previously the practice). The error codes,
-and their respective documentation, can be found at the top of panels.module. \ No newline at end of file
+and their respective documentation, can be found at the top of panels.module.
diff --git a/TODO.txt b/TODO.txt
deleted file mode 100644
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
diff --git a/css/panels-dashboard.css b/css/panels-dashboard.css
index 002c234..500bc9f 100644
--- a/css/panels-dashboard.css
+++ b/css/panels-dashboard.css
@@ -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;
}
@@ -23,14 +14,6 @@
margin-bottom: .5em;
}
-.dashboard-entry {
- padding-left: 1em;
-}
-
-.dashboard-entry .dashboard-link form {
- margin: 0 !important;
-}
-
.dashboard-link form input {
margin: 0;
}
@@ -56,15 +39,25 @@
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;
+}
diff --git a/css/panels_admin.css b/css/panels_admin.css
index de3ea22..c969ebd 100644
--- a/css/panels_admin.css
+++ b/css/panels_admin.css
@@ -1,7 +1,7 @@
/* $Id$ */
.layout-link {
- float: left;
+ float: left;
padding: 1em;
width: 125px;
height: 160px;
@@ -77,14 +77,14 @@
.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,
@@ -101,6 +101,12 @@
margin-right: 20px;
}
+.panels-layouts-checkboxes .panels-layouts-category {
+ font-weight: bold;
+ width: 100%;
+ float: left;
+}
+
.panels-layouts-checkboxes .description {
clear: left;
}
diff --git a/css/panels_dnd.css b/css/panels_dnd.css
index b7b82e7..ba14a5f 100644
--- a/css/panels_dnd.css
+++ b/css/panels_dnd.css
@@ -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
index 6cde2f0..0000000
--- a/docs/docs.inc
+++ /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
index a8fba01..0000000
--- a/docs/doxygen.css
+++ /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
index 5b5eb02..0000000
--- a/docs/sample_plugin_ct.inc
+++ /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
diff --git a/help/api.html b/help/api.html
index e69de29..e0d2d4a 100644
--- a/help/api.html
+++ b/help/api.html
@@ -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
diff --git a/help/panels.help.ini b/help/panels.help.ini
index 8ed4f6c..fb49c03 100644
--- a/help/panels.help.ini
+++ b/help/panels.help.ini
@@ -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
index 0000000..45051b2
--- /dev/null
+++ b/help/plugins-layout.html
@@ -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>
diff --git a/help/plugin-style.html b/help/plugins-style.html
index e69de29..e69de29 100644
--- a/help/plugin-style.html
+++ b/help/plugins-style.html
diff --git a/includes/callbacks.inc b/includes/callbacks.inc
index a42e339..318eadc 100644
--- a/includes/callbacks.inc
+++ b/includes/callbacks.inc
@@ -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');
}
+
diff --git a/includes/common.inc b/includes/common.inc
index 53de582..867a884 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -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;
+}
diff --git a/includes/display-edit.inc b/includes/display-edit.inc
index 0dff83d..bf9bf69 100644
--- a/includes/display-edit.inc
+++ b/includes/display-edit.inc
@@ -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,
- );
+ // Get a renderer.
+ $renderer = panels_get_renderer_handler('editor', $display);
+ $renderer->cache = $cache;
- $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.'));
- }
-
- 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;
-}
diff --git a/includes/display-layout.inc b/includes/display-layout.inc
index 2ce9712..5ce7fbe 100644
--- a/includes/display-layout.inc
+++ b/includes/display-layout.inc
@@ -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>',
diff --git a/includes/display-render.inc b/includes/display-render.inc
index e528536..7b24461 100644
--- a/includes/display-render.inc
+++ b/includes/display-render.inc
@@ -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
index 0000000..5ba90b5
--- /dev/null
+++ b/includes/legacy.inc
@@ -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
index 0000000..e6f7012
--- /dev/null
+++ b/includes/page-wizard.inc
@@ -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
index 0000000..7e86d6c
--- /dev/null
+++ b/includes/panels.pipelines.inc
@@ -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;
+}
diff --git a/includes/plugins.inc b/includes/plugins.inc
index b06f945..85fa282 100644
--- a/includes/plugins.inc
+++ b/includes/plugins.inc
@@ -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 {};
diff --git a/js/display_editor.js b/js/display_editor.js
index ce94921..025aeb6 100644
--- a/js/display_editor.js
+++ b/js/display_editor.js
@@ -411,9 +411,9 @@
}
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);
@@ -453,7 +453,7 @@
// 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);
diff --git a/panels.install b/panels.install
index e7a0cef..7d350c9 100644
--- a/panels.install
+++ b/panels.install
@@ -5,11 +5,19 @@
* Test requirements for installation and running.
*/
function panels_requirements($phase) {
+ $function = "panels_requirements_$phase";
+ return function_exists($function) ? $function() : array();
+}
+
+/**
+ * Check install-time requirements.
+ */
+function panels_requirements_install() {
$requirements = array();
$t = get_t();
// Assume that if the user is running an installation profile that both
// Panels and CTools are the same release.
- if ($phase == 'install' && !(defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install')) {
+ if (!(defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install')) {
// apparently the install process doesn't include .module files,
// so we need to force the issue in order for our versioning
// check to work.
@@ -35,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;
+}
diff --git a/panels.module b/panels.module
index e5777f5..bd74c68 100644
--- a/panels.module
+++ b/panels.module
@@ -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
index c8a6bbd..0000000
--- a/panels2.doxy
+++ /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
index 0000000..77edc15
--- /dev/null
+++ b/panels_ipe/css/panels_ipe.css
@@ -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
index 0000000..bb3b57b
--- /dev/null
+++ b/panels_ipe/images/dragger.png
Binary files differ
diff --git a/panels_ipe/includes/panels_ipe.pipelines.inc b/panels_ipe/includes/panels_ipe.pipelines.inc
new file mode 100644
index 0000000..680f196
--- /dev/null
+++ b/panels_ipe/includes/panels_ipe.pipelines.inc
@@ -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
index 0000000..0ed435b
--- /dev/null
+++ b/panels_ipe/js/panels_ipe.js
@@ -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
index 0000000..c27a2a9
--- /dev/null
+++ b/panels_ipe/panels_ipe.info
@@ -0,0 +1,7 @@
+; $Id$
+name = Panels In-Place Editor
+description = Provide a UI for managing some Panels directly on the frontend, instead of having to use the backend.
+package = "Panels"
+dependencies[] = panels
+dependencies[] = jquery_ui
+core = 6.x
diff --git a/panels_ipe/panels_ipe.module b/panels_ipe/panels_ipe.module
new file mode 100644
index 0000000..c0520a2
--- /dev/null
+++ b/panels_ipe/panels_ipe.module
@@ -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
index 0000000..afe1e44
--- /dev/null
+++ b/panels_ipe/plugins/display_renderers/ipe.inc
@@ -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
index 0000000..fad426c
--- /dev/null
+++ b/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php
@@ -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;
+}
diff --git a/panels_mini/panels_mini.admin.inc b/panels_mini/panels_mini.admin.inc
index c85d823..4adc7ca 100644
--- a/panels_mini/panels_mini.admin.inc
+++ b/panels_mini/panels_mini.admin.inc
@@ -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;
-}
diff --git a/panels_mini/panels_mini.install b/panels_mini/panels_mini.install
index 31b9c01..d26c9b6 100644
--- a/panels_mini/panels_mini.install
+++ b/panels_mini/panels_mini.install
@@ -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;
+}
diff --git a/panels_mini/panels_mini.module b/panels_mini/panels_mini.module
index a039b23..7ffaf88 100644
--- a/panels_mini/panels_mini.module
+++ b/panels_mini/panels_mini.module
@@ -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
+}
diff --git a/panels_mini/plugins/content_types/panels_mini.inc b/panels_mini/plugins/content_types/panels_mini.inc
index ee47f68..6f57822 100644
--- a/panels_mini/plugins/content_types/panels_mini.inc
+++ b/panels_mini/plugins/content_types/panels_mini.inc
@@ -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.
- *
+ *
*/
/**
@@ -18,38 +18,58 @@
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 +102,8 @@ function panels_mini_panels_mini_content_type_render($subtype, $conf, $panel_arg
$block = new stdClass();
$block->module = 'panels_mini';
$block->delta = $subtype;
- $block->title = filter_xss_admin($mini->title);
$block->content = panels_render_display($mini->display);
+ $block->title = $mini->display->get_title();
unset($viewing[$mini->name]);
return $block;
@@ -105,7 +125,7 @@ function panels_mini_panels_mini_content_type_admin_title($subtype, $conf) {
return t('Deleted/missing mini panel @name', array('@name' => $subtype));
}
- $title = filter_xss_admin($mini->title);
+ $title = filter_xss_admin($mini->admin_title);
if (empty($title)) {
$title = t('Untitled mini panel');
}
diff --git a/panels_mini/plugins/export_ui/panels_mini.inc b/panels_mini/plugins/export_ui/panels_mini.inc
new file mode 100644
index 0000000..7644e1f
--- /dev/null
+++ b/panels_mini/plugins/export_ui/panels_mini.inc
@@ -0,0 +1,42 @@
+<?php
+// $Id$
+
+$plugin = array(
+ 'schema' => 'panels_mini',
+ 'access' => 'administer mini panels',
+ 'create access' => 'create mini panels',
+
+ 'menu' => array(
+ 'menu item' => 'mini-panels',
+ 'menu title' => 'Mini panels',
+ 'menu description' => 'Add, edit or delete mini panels, which can be used as blocks or content panes in other panels.',
+ ),
+
+ 'title singular' => t('mini panel'),
+ 'title singular proper' => t('Mini panel'),
+ 'title plural' => t('mini panels'),
+ 'title plural proper' => t('Mini panels'),
+
+ 'handler' => array(
+ 'class' => 'panels_mini_ui',
+ 'parent' => 'ctools_export_ui',
+ ),
+
+ 'use wizard' => TRUE,
+ 'form info' => array(
+ 'order' => array(
+ 'basic' => t('Settings'),
+ 'context' => t('Context'),
+ 'layout' => t('Layout'),
+ 'content' => t('Content'),
+ ),
+ // We have to add this form specially because it's invisible.
+ 'forms' => array(
+ 'move' => array(
+ 'form id' => 'ctools_export_ui_edit_item_wizard_form',
+ ),
+ ),
+ ),
+
+);
+
diff --git a/panels_mini/plugins/export_ui/panels_mini_ui.class.php b/panels_mini/plugins/export_ui/panels_mini_ui.class.php
new file mode 100644
index 0000000..3c9828a
--- /dev/null
+++ b/panels_mini/plugins/export_ui/panels_mini_ui.class.php
@@ -0,0 +1,292 @@
+<?php
+// $Id$
+
+class panels_mini_ui extends ctools_export_ui {
+ function init($plugin) {
+ parent::init($plugin);
+ ctools_include('context');
+ }
+
+ function list_form(&$form, &$form_state) {
+ ctools_include('plugins', 'panels');
+ $this->layouts = panels_get_layouts();
+
+ parent::list_form($form, $form_state);
+
+ $categories = $layouts = array('all' => t('- All -'));
+ foreach ($this->items as $item) {
+ $categories[$item->category] = $item->category ? $item->category : t('Mini panels');
+ }
+
+ $form['top row']['category'] = array(
+ '#type' => 'select',
+ '#title' => t('Category'),
+ '#options' => $categories,
+ '#default_value' => 'all',
+ '#weight' => -10,
+ );
+
+ foreach ($this->layouts as $name => $plugin) {
+ $layouts[$name] = $plugin['title'];
+ }
+
+ $form['top row']['layout'] = array(
+ '#type' => 'select',
+ '#title' => t('Layout'),
+ '#options' => $layouts,
+ '#default_value' => 'all',
+ '#weight' => -9,
+ );
+ }
+
+ function list_filter($form_state, $item) {
+ if ($form_state['values']['category'] != 'all' && $form_state['values']['category'] != $item->category) {
+ return TRUE;
+ }
+
+ if ($form_state['values']['layout'] != 'all' && $form_state['values']['layout'] != $item->display->layout) {
+ return TRUE;
+ }
+
+ return parent::list_filter($form_state, $item);
+ }
+
+ function list_sort_options() {
+ return array(
+ 'disabled' => t('Enabled, title'),
+ 'title' => t('Title'),
+ 'name' => t('Name'),
+ 'category' => t('Category'),
+ 'storage' => t('Storage'),
+ 'layout' => t('Layout'),
+ );
+ }
+
+ function list_build_row($item, &$form_state, $operations) {
+ // Set up sorting
+ switch ($form_state['values']['order']) {
+ case 'disabled':
+ $this->sorts[$item->name] = empty($item->disabled) . $item->admin_title;
+ break;
+ case 'title':
+ $this->sorts[$item->name] = $item->admin_title;
+ break;
+ case 'name':
+ $this->sorts[$item->name] = $item->name;
+ break;
+ case 'category':
+ $this->sorts[$item->name] = ($item->category ? $item->category : t('Mini panels')) . $item->admin_title;
+ break;
+ case 'layout':
+ $this->sorts[$item->name] = $item->display->layout . $item->admin_title;
+ break;
+ case 'storage':
+ $this->sorts[$item->name] = $item->type . $item->admin_title;
+ break;
+ }
+
+ $layout = !empty($this->layouts[$item->display->layout]) ? $this->layouts[$item->display->layout]['title'] : t('Missing layout');
+ $category = $item->category ? check_plain($item->category) : t('Mini panels');
+
+ $this->rows[$item->name] = array(
+ 'data' => array(
+ array('data' => check_plain($item->admin_title), 'class' => 'ctools-export-ui-title'),
+ array('data' => check_plain($item->name), 'class' => 'ctools-export-ui-name'),
+ array('data' => $category, 'class' => 'ctools-export-ui-category'),
+ array('data' => $layout, 'class' => 'ctools-export-ui-layout'),
+ array('data' => $item->type, 'class' => 'ctools-export-ui-storage'),
+ array('data' => theme('links', $operations), 'class' => 'ctools-export-ui-operations'),
+ ),
+ 'title' => !empty($item->admin_description) ? check_plain($item->admin_description) : '',
+ 'class' => !empty($item->disabled) ? 'ctools-export-ui-disabled' : 'ctools-export-ui-enabled',
+ );
+ }
+
+ function list_table_header() {
+ return array(
+ array('data' => t('Title'), 'class' => 'ctools-export-ui-title'),
+ array('data' => t('Name'), 'class' => 'ctools-export-ui-name'),
+ array('data' => t('Category'), 'class' => 'ctools-export-ui-category'),
+ array('data' => t('Layout'), 'class' => 'ctools-export-ui-layout'),
+ array('data' => t('Storage'), 'class' => 'ctools-export-ui-storage'),
+ array('data' => t('Operations'), 'class' => 'ctools-export-ui-operations'),
+ );
+ }
+
+ function edit_form(&$form, &$form_state) {
+ // Get the basic edit form
+ parent::edit_form($form, $form_state);
+
+ $form['category'] = array(
+ '#type' => 'textfield',
+ '#size' => 24,
+ '#default_value' => $form_state['item']->category,
+ '#title' => t('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'."),
+ );
+
+ $form['title']['#title'] = t('Title');
+ $form['title']['#description'] = t('The title for this mini panel. It can be overridden in the block configuration.');
+ }
+
+ /**
+ * Validate submission of the mini panel edit form.
+ */
+ function edit_form_basic_validate($form, &$form_state) {
+ parent::edit_form_validate($form, $form_state);
+ if (preg_match("/[^A-Za-z0-9 ]/", $form_state['values']['category'])) {
+ form_error($form['category'], t('Categories may contain only alphanumerics or spaces.'));
+ }
+ }
+
+ function edit_form_submit(&$form, &$form_state) {
+ parent::edit_form_submit($form, $form_state);
+ $form_state['item']->category = $form_state['values']['category'];
+ }
+
+ function edit_form_context(&$form, &$form_state) {
+ ctools_include('context-admin');
+ ctools_context_admin_includes();
+ ctools_add_css('ruleset');
+
+ $form['right'] = array(
+ '#prefix' => '<div class="ctools-right-container">',
+ '#suffix' => '</div>',
+ );
+
+ $form['left'] = array(
+ '#prefix' => '<div class="ctools-left-container clear-block">',
+ '#suffix' => '</div>',
+ );
+
+ // Set this up and we can use CTools' Export UI's built in wizard caching,
+ // which already has callbacks for the context cache under this name.
+ $module = 'ctools_export_ui-' . $this->plugin['name'];
+ $name = $this->edit_cache_get_key($form_state['item'], $form_state['form type']);
+
+ ctools_context_add_context_form($module, $form, $form_state, $form['right']['contexts_table'], $form_state['item'], $name);
+ ctools_context_add_required_context_form($module, $form, $form_state, $form['left']['required_contexts_table'], $form_state['item'], $name);
+ ctools_context_add_relationship_form($module, $form, $form_state, $form['right']['relationships_table'], $form_state['item'], $name);
+ }
+
+ function edit_form_context_submit(&$form, &$form_state) {
+ // Prevent this from going to edit_form_submit();
+ }
+
+ function edit_form_layout(&$form, &$form_state) {
+ ctools_include('common', 'panels');
+ ctools_include('display-layout', 'panels');
+ ctools_include('plugins', 'panels');
+
+ // @todo -- figure out where/how to deal with this.
+ $form_state['allowed_layouts'] = 'panels_mini';
+
+ if ($form_state['op'] == 'add' && empty($form_state['item']->display)) {
+ $form_state['item']->display = panels_new_display();
+ }
+
+ $form_state['display'] = &$form_state['item']->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 = array_merge($form, panels_choose_layout($form_state));
+
+ if ($form_state['op'] == 'edit') {
+ $form['buttons']['next']['#value'] = t('Change');
+ }
+ }
+
+ /**
+ * Validate that a layout was chosen.
+ */
+ function edit_form_layout_validate(&$form, &$form_state) {
+ $display = &$form_state['display'];
+ if (empty($form_state['values']['layout'])) {
+ form_error($form['layout'], t('You must select a layout.'));
+ }
+ if ($form_state['op'] == 'edit') {
+ if ($form_state['values']['layout'] == $display->layout) {
+ form_error($form['layout'], t('You must select a different layout if you wish to change layouts.'));
+ }
+ }
+ }
+
+ /**
+ * A layout has been selected, set it up.
+ */
+ function edit_form_layout_submit(&$form, &$form_state) {
+ $display = &$form_state['display'];
+ if ($form_state['op'] == 'edit') {
+ if ($form_state['values']['layout'] != $display->layout) {
+ $form_state['item']->temp_layout = $form_state['values']['layout'];
+ $form_state['clicked_button']['#next'] = 'move';
+ }
+ }
+ else {
+ $form_state['item']->display->layout = $form_state['values']['layout'];
+ }
+ }
+
+ /**
+ * When a layout is changed, the user is given the opportunity to move content.
+ */
+ function edit_form_move(&$form, &$form_state) {
+ $form_state['display'] = &$form_state['item']->display;
+ $form_state['layout'] = $form_state['item']->temp_layout;
+
+ ctools_include('common', 'panels');
+ ctools_include('display-layout', 'panels');
+ ctools_include('plugins', 'panels');
+
+ // 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 = array_merge($form, panels_change_layout($form_state));
+
+ // This form is outside the normal wizard list, so we need to specify the
+ // previous/next forms.
+ $form['buttons']['previous']['#next'] = 'layout';
+ $form['buttons']['next']['#next'] = 'content';
+ }
+
+ function edit_form_move_submit(&$form, &$form_state) {
+ panels_change_layout_submit($form, $form_state);
+ }
+
+ function edit_form_content(&$form, &$form_state) {
+ ctools_include('ajax');
+ ctools_include('plugins', 'panels');
+ ctools_include('display-edit', 'panels');
+ ctools_include('context');
+
+ // If we are cloning an item, we MUST have this cached for this to work,
+ // so make sure:
+ if ($form_state['form type'] == 'clone' && empty($form_state['item']->export_ui_item_is_cached)) {
+ $this->edit_cache_set($form_state['item'], 'clone');
+ }
+
+ $cache = panels_edit_cache_get('panels_mini:' . $this->edit_cache_get_key($form_state['item'], $form_state['form type']));
+
+ $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 edit_form_content_submit(&$form, &$form_state) {
+ panels_edit_display_form_submit($form, $form_state);
+ $form_state['item']->display = $form_state['display'];
+ }
+}
diff --git a/panels_node/panels_node.install b/panels_node/panels_node.install
index 4e327a5..a93610a 100644
--- a/panels_node/panels_node.install
+++ b/panels_node/panels_node.install
@@ -31,6 +31,10 @@ function panels_node_schema_1() {
'type' => 'int',
'not null' => TRUE,
),
+ 'pipeline' => array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ ),
),
'primary key' => array('did'),
);
@@ -54,3 +58,17 @@ function panels_node_uninstall() {
db_query("DELETE FROM {node} WHERE type = 'panel'");
drupal_uninstall_schema('panels_node');
}
+
+/**
+ * Implementation of hook_update to handle adding a pipeline
+ */
+function panels_node_update_6001() {
+ $ret = array();
+ $field = array(
+ 'type' => 'varchar',
+ 'length' => '255',
+ );
+
+ db_add_field($ret, 'panels_node', 'pipeline', $field);
+ return $ret;
+}
diff --git a/panels_node/panels_node.module b/panels_node/panels_node.module
index d80b0cb..53eec85 100644
--- a/panels_node/panels_node.module
+++ b/panels_node/panels_node.module
@@ -17,7 +17,7 @@
* Implementation of hook_perm().
*/
function panels_node_perm() {
- return array('create panel-nodes', 'edit any panel-nodes', 'edit own panel-nodes', 'administer panel-nodes');
+ return array('create panel-nodes', 'edit any panel-nodes', 'edit own panel-nodes', 'administer panel-nodes', 'delete any panel-nodes', 'delete own panel-nodes');
}
/**
@@ -46,7 +46,6 @@ function panels_node_menu() {
);
$items['node/%node/panel_layout'] = array(
- 'path' => $base . 'layout',
'title' => 'Panel layout',
'page callback' => 'panels_node_edit_layout',
'weight' => 2,
@@ -102,15 +101,20 @@ function panels_node_node_info() {
* Implementation of hook_access().
*/
function panels_node_access($op, $node, $account) {
- if (user_access('administer panel-nodes')) {
+ if (user_access('administer panel-nodes', $account)) {
return TRUE;
}
- if ($op == 'create' && user_access('create panel-nodes')) {
+ if ($op == 'create' && user_access('create panel-nodes', $account)) {
return TRUE;
}
- if ($op == 'update' && (user_access('edit any panel-nodes') || $node->uid == $account->uid && user_access('edit own panel-nodes'))) {
+ if ($op == 'update' && (user_access('edit any panel-nodes', $account) || $node->uid == $account->uid && user_access('edit own panel-nodes', $account))) {
+ return TRUE;
+ }
+
+
+ if ($op == 'delete' && (user_access('delete any panel-nodes') || $node->uid == $account->uid && user_access('delete own panel-nodes'))) {
return TRUE;
}
}
@@ -118,24 +122,21 @@ function panels_node_access($op, $node, $account) {
function panels_node_add() {
$output = '';
- panels_load_include('plugins');
- panels_load_include('common');
+ ctools_include('plugins', 'panels');
+ ctools_include('common', 'panels');
$layouts = panels_common_get_allowed_layouts('panels_node');
-
- // If no layout selected, present a list of choices.
- foreach ($layouts as $id => $layout) {
- $output .= panels_print_layout_link($id, $layout, 'node/add/panel/' . $id, array('query' => $_GET));
- }
- return $output;
+ return panels_common_print_layout_links($layouts, 'node/add/panel', array('query' => $_GET));
}
/**
* Implementation of hook_form().
*/
function panels_node_form(&$node, &$param) {
+ ctools_include('plugins', 'panels');
+
$form['panels_node']['#tree'] = TRUE;
- if (empty($node->nid)) {
+ if (empty($node->nid) && arg(0) == 'node' && arg(1) == 'add') {
// Grab our selected layout from the $node, If it doesn't exist, try arg(3)
// and if that doesn't work present them with a list to pick from.
$panel_layout = isset($node->panel_layout) ? $node->panel_layout : arg(3);
@@ -145,7 +146,6 @@ function panels_node_form(&$node, &$param) {
return drupal_goto('node/add/panel/choose-layout', $opts);
}
- panels_load_include('plugins');
$layout = panels_get_layout($panel_layout);
if (empty($layout)) {
return drupal_not_found();
@@ -166,7 +166,9 @@ function panels_node_form(&$node, &$param) {
);
if (!empty($type->body_label)) {
- $form['body'] = array(
+ $form['body_field']['#prefix'] = '<div class="body-field-wrapper">';
+ $form['body_field']['#suffix'] = '</div>';
+ $form['body_field']['body'] = array(
'#type' => 'textarea',
'#title' => check_plain($type->body_label),
'#rows' => 10,
@@ -174,7 +176,7 @@ function panels_node_form(&$node, &$param) {
'#description' => t('The teaser is a piece of text to describe when the panel is listed (such as when promoted to front page); the actual content will only be displayed on the full node view.'),
'#default_value' => $node->body,
);
- $form['format'] = filter_form($node->format); // Now we can set the format!
+ $form['body_field']['format'] = filter_form($node->format); // Now we can set the format!
}
// drupal_set_message('<pre>' . check_plain(var_export($node, true)) . '</pre>');
@@ -190,6 +192,35 @@ function panels_node_form(&$node, &$param) {
'#description' => t('An ID that can be used by CSS to style the panel.'),
'#default_value' => $css_id,
);
+
+ // Support for different rendering pipelines
+ // Mostly borrowed from panel_context.inc
+ $pipelines = panels_get_renderer_pipelines();
+
+ // If there are no pipelines, that probably means we're operating in
+ // legacy mode.
+ if (empty($pipelines)) {
+ // We retain the original pipeline so we don't wreck things by installing
+ // old modules.
+ $form['panels_node']['pipeline'] = array(
+ '#type' => 'value',
+ '#value' => $node->panels_node['pipeline'],
+ );
+ }
+ else {
+ $options = array();
+ foreach ($pipelines as $name => $pipeline) {
+ $options[$name] = check_plain($pipeline->admin_title) . '<div class="description">' . check_plain($pipeline->admin_description) . '</div>';
+ }
+
+ $form['panels_node']['pipeline'] = array(
+ '#type' => 'radios',
+ '#options' => $options,
+ '#title' => t('Renderer'),
+ '#default_value' => $node->panels_node['pipeline'] ? $node->panels_node['pipeline'] : 'standard',
+ );
+ }
+
return $form;
}
@@ -234,7 +265,7 @@ function panels_node_insert(&$node) {
panels_save_display($display);
$css_id = $node->panels_node['css_id'];
- db_query("INSERT INTO {panels_node} (nid, did, css_id) VALUES (%d, %d, '%s')", $node->nid, $display->did, $node->panels_node['css_id']);
+ db_query("INSERT INTO {panels_node} (nid, did, css_id, pipeline) VALUES (%d, %d, '%s', '%s')", $node->nid, $display->did, $node->panels_node['css_id'], $node->panels_node['pipeline']);
$node->panels_node['did'] = $display->did;
}
@@ -253,7 +284,7 @@ function panels_node_delete(&$node) {
* Implementation of hook_update().
*/
function panels_node_update($node) {
- db_query("UPDATE {panels_node} SET css_id = '%s' WHERE nid = %d", $node->panels_node['css_id'], $node->nid);
+ db_query("UPDATE {panels_node} SET css_id = '%s', pipeline = '%s' WHERE nid = %d", $node->panels_node['css_id'], $node->panels_node['pipeline'], $node->nid);
}
/**
@@ -268,7 +299,7 @@ function panels_node_view($node, $teaser = FALSE, $page = FALSE) {
}
$rendering[$node->nid] = TRUE;
- panels_load_include('plugins');
+ ctools_include('plugins', 'panels');
if ($teaser) {
// Do the standard view for teaser.
$node = node_prepare($node, $teaser);
@@ -282,9 +313,9 @@ function panels_node_view($node, $teaser = FALSE, $page = FALSE) {
$display->css_id = $node->panels_node['css_id'];
// TODO: Find a way to make sure this can't node_view.
$display->context = panels_node_get_context($node);
-
+ $renderer = panels_get_renderer($node->panels_node['pipeline'], $display);
$node->content['body'] = array(
- '#value' => panels_render_display($display),
+ '#value' => panels_render_display($display, $renderer),
'#weight' => 0,
);
}
@@ -301,7 +332,7 @@ function panels_node_view($node, $teaser = FALSE, $page = FALSE) {
* Settings for panel nodes.
*/
function panels_node_settings() {
- panels_load_include('common');
+ ctools_include('common', 'panels');
return drupal_get_form('panels_common_settings', 'panels_node');
}
@@ -312,7 +343,7 @@ function panels_node_settings() {
* Pass through to the panels layout editor.
*/
function panels_node_edit_layout($node) {
-// panels_load_include('plugins');
+// ctools_include('plugins', 'panels');
ctools_include('context');
$display = panels_load_display($node->panels_node['did']);
$display->context = panels_node_get_context($node);
@@ -323,11 +354,11 @@ function panels_node_edit_layout($node) {
* Pass through to the panels content editor.
*/
function panels_node_edit_content($node) {
-// panels_load_include('plugins');
+// ctools_include('plugins', 'panels');
ctools_include('context');
$display = panels_load_display($node->panels_node['did']);
$display->context = panels_node_get_context($node);
- panels_load_include('common');
+ ctools_include('common', 'panels');
$content_types = panels_common_get_allowed_types('panels_node', $display->context);
// Print this with theme('page') so that blocks are disabled while editing a display.
@@ -348,14 +379,31 @@ function panels_node_get_context(&$node) {
}
/**
- * hook_export_node_alter() - integegrate with export.module for saving
- * panel_nodes into code.
+ * Implementation of hook_export_node_alter()
+ *
+ * Integrate with export.module for saving panel_nodes into code.
*/
function panels_node_export_node_alter(&$node, $original_node, $method) {
if ($method == 'export') {
- drupal_set_message(t("NOTE: in order to import panel_nodes you must first set the export.module settings to \"Save as a new node then edit\", otherwise it won't work."));
+ $node_export_omitted = variable_get('node_export_omitted', array());
+ if (variable_get('node_export_method', '') != 'save-edit' && (array_key_exists('panel', $node_export_omitted) && !$node_export_omitted['panel'])) {
+ drupal_set_message(t("NOTE: in order to import panel_nodes you must first set the export.module settings to \"Save as a new node then edit\", otherwise it won't work."));
+ }
$display = panels_load_display($node->panels_node['did']);
$export = panels_export_display($display);
$node->export_display = $export;
}
}
+
+/**
+ * Implementation of hook_panels_dashboard_blocks().
+ *
+ * Adds panel nodes information to the Panels dashboard.
+ */
+function panels_node_panels_dashboard_blocks(&$vars) {
+ $vars['links']['panels_node'] = array(
+ 'title' => l(t('Panel node'), 'node/add/panel'),
+ 'description' => t('Panel nodes are node content and appear in your searches, but are more limited than panel pages.'),
+ 'weight' => -1,
+ );
+}
diff --git a/plugins/cache/simple.inc b/plugins/cache/simple.inc
index 443cf4e..cddfaa7 100644
--- a/plugins/cache/simple.inc
+++ b/plugins/cache/simple.inc
@@ -100,6 +100,10 @@ function panels_simple_cache_get_id($conf, $display, $args, $contexts, $pane) {
}
}
}
+ if (module_exists('locale')) {
+ global $language;
+ $id .= ':' . $language->language;
+ }
return $id;
}
diff --git a/plugins/display_renderers/editor.inc b/plugins/display_renderers/editor.inc
new file mode 100644
index 0000000..501b7b8
--- /dev/null
+++ b/plugins/display_renderers/editor.inc
@@ -0,0 +1,8 @@
+<?php
+
+$plugin = array(
+ 'handler' => array(
+ 'class' => 'panels_renderer_editor',
+ 'parent' => 'standard',
+ ),
+); \ No newline at end of file
diff --git a/plugins/display_renderers/legacy.inc b/plugins/display_renderers/legacy.inc
new file mode 100644
index 0000000..2082105
--- /dev/null
+++ b/plugins/display_renderers/legacy.inc
@@ -0,0 +1,7 @@
+<?php
+
+$plugin = array(
+ 'handler' => array(
+ 'class' => 'panels_renderer_legacy',
+ ),
+); \ No newline at end of file
diff --git a/plugins/display_renderers/panels_renderer_editor.class.php b/plugins/display_renderers/panels_renderer_editor.class.php
new file mode 100644
index 0000000..8b4b9f4
--- /dev/null
+++ b/plugins/display_renderers/panels_renderer_editor.class.php
@@ -0,0 +1,1893 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Class file to control the main Panels editor.
+ */
+
+class panels_renderer_editor extends panels_renderer_standard {
+
+ /**
+ * An array of AJAX commands to return. If populated it will automatically
+ * be used by the AJAX router.
+ */
+ var $commands = array();
+ var $admin = TRUE;
+ // -------------------------------------------------------------------------
+ // Display edit rendering.
+
+ function edit() {
+ ctools_include('form');
+ $form_state = array(
+ 'display' => &$this->display,
+ 'renderer' => &$this,
+ 'content_types' => $this->cache->content_types,
+ 're_render' => FALSE,
+ 'no_redirect' => TRUE,
+ 'display_title' => !empty($this->cache->display_title),
+ 'cache key' => $this->display->cache_key,
+ );
+
+ $output = ctools_build_form('panels_edit_display_form', $form_state);
+ if ($output) {
+ return $output;
+ }
+
+ // 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($this->display);
+ }
+ else {
+ drupal_set_message(t('Your changes have been discarded.'));
+ }
+
+ panels_cache_clear('display', $this->display->did);
+ return $this->display;
+ }
+ }
+
+ function add_meta() {
+ parent::add_meta();
+ if ($this->admin) {
+ ctools_include('ajax');
+ ctools_include('modal');
+ ctools_modal_add_js();
+
+ ctools_add_js('panels-base', 'panels');
+ ctools_add_js('display_editor', 'panels');
+ ctools_add_css('panels_dnd', 'panels');
+ ctools_add_css('panels_admin', 'panels');
+ }
+ }
+
+ function render() {
+ // Pass through to normal rendering if not in admin mode.
+ if (!$this->admin) {
+ return parent::render();
+ }
+
+ $this->add_meta();
+
+ $output = '<div class="panels-dnd" id="panels-dnd-main">';
+ $output .= $this->render_layout();
+ $output .= '</div>';
+
+ return $output;
+ }
+
+ function render_region($region_id, $panes) {
+ // Pass through to normal rendering if not in admin mode.
+ if (!$this->admin) {
+ return parent::render_region($region_id, $panes);
+ }
+
+ $content = implode('', $panes);
+
+ $panel_buttons = $this->get_region_links($region_id);
+
+ // @todo this should be panel-region not panels-display -- but CSS and .js has to be updated.
+ $output = "<div class='panels-display' id='panel-pane-$region_id'>";
+ $output .= $panel_buttons;
+ $output .= "<h2 class='label'>" . $this->plugins['layout']['panels'][$region_id] . "</h2>";
+ $output .= $content;
+ $output .= "</div>";
+
+ return $output;
+ }
+
+ function render_pane($pane) {
+ // Pass through to normal rendering if not in admin mode.
+ if (!$this->admin) {
+ return parent::render_pane($pane);
+ }
+
+ 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, $this->display->context);
+ if (!$title) {
+ $title = t('Deleted/missing content type @type', array('@type' => $pane->type));
+ }
+
+ $buttons = $this->get_pane_links($pane, $content_type);
+
+ // 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, $this->display->context);
+ }
+
+ $output = '';
+ $class = 'panel-pane';
+
+ if (empty($pane->shown)) {
+ $class .= ' hidden-pane';
+ }
+
+ if (isset($this->display->title_pane) && $this->display->title_pane == $pane->pid) {
+ $class .= ' panel-pane-is-title';
+ }
+
+ $output = '<div class="' . $class . '" id="panel-pane-' . $pane->pid . '">';
+
+ if (!$block->title) {
+ $block->title = t('No title');
+ }
+
+ $output .= '<div class="grabber">';
+ if ($buttons) {
+ $output .= '<span class="buttons">' . $buttons . '</span>';
+ }
+ $output .= '<span class="text">' . $title . '</span>';
+ $output .= '</div>'; // grabber
+
+ $output .= '<div class="panel-pane-collapsible">';
+ $output .= '<div class="pane-title">' . $block->title . '</div>';
+ $output .= '<div class="pane-content">' . filter_xss_admin($block->content) . '</div>';
+ $output .= '</div>'; // panel-pane-collapsible
+
+ $output .= '</div>'; // panel-pane
+
+ return $output;
+ }
+
+ /**
+ * Get the style links.
+ *
+ * This is abstracted out since we have styles on both panes and regions.
+ */
+ function get_style_links($type, $id = NULL) {
+ $info = $this->get_style($type, $id);
+ $style = $info[0];
+ $conf = $info[1];
+
+ $style_title = isset($style['title']) ? $style['title'] : t('Default');
+
+ $style_links[] = array(
+ 'title' => $style_title,
+ 'attributes' => array('class' => 'panels-text'),
+ );
+
+ $style_links[] = array(
+ 'title' => t('Change'),
+ 'href' => $this->get_url('style-type', $type, $id),
+ 'attributes' => array('class' => 'ctools-use-modal'),
+ );
+
+ $function = $type != 'pane' ? 'settings form' : 'pane settings form';
+ if (panels_plugin_get_function('styles', $style, $function)) {
+ $style_links[] = array(
+ 'title' => t('Settings'),
+ 'href' => $this->get_url('style-settings', $type, $id),
+ 'attributes' => array('class' => 'ctools-use-modal'),
+ );
+ }
+
+ return $style_links;
+ }
+
+ /**
+ * Get the links for a panel display.
+ *
+ * This is abstracted out for easy ajax replacement.
+ */
+ function get_display_links() {
+ $links = array();
+ $style_links = $this->get_style_links('display');
+
+ $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('use panels caching features')) {
+ $links[] = array(
+ 'title' => '<hr />',
+ 'html' => TRUE,
+ );
+
+ $method = isset($this->display->cache['method']) ? $this->display->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' => $this->get_url('cache-method', 'display'),
+ 'attributes' => array('class' => 'ctools-use-modal'),
+ );
+ if (panels_plugin_get_function('cache', $info, 'settings form')) {
+ $cache_links[] = array(
+ 'title' => t('Settings'),
+ 'href' => $this->get_url('cache-settings', 'display'),
+ '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'),
+ );
+ }
+
+ return theme('ctools_dropdown', t('Display settings'), $links, FALSE, 'panels-display-links');
+ }
+
+ /**
+ * Render the links to display when editing a region.
+ */
+ function get_region_links($region_id) {
+ $links = array();
+ $links[] = array(
+ 'title' => t('Add content'),
+ 'href' => $this->get_url('select-content', $region_id),
+ 'attributes' => array(
+ 'class' => 'ctools-use-modal',
+ ),
+ );
+
+ $links[] = array(
+ 'title' => '<hr />',
+ 'html' => TRUE,
+ );
+
+ $style_links = $this->get_style_links('region', $region_id);
+
+ $links[] = array(
+ 'title' => '<span class="dropdown-header">' . t('Style') . '</span>' . theme_links($style_links),
+ 'html' => TRUE,
+ 'attributes' => array('class' => 'panels-sub-menu'),
+ );
+
+ return theme('ctools_dropdown', theme('image', ctools_image_path('icon-addcontent.png', 'panels')), $links, TRUE, 'pane-add-link panels-region-links-' . $region_id);
+ }
+
+ /**
+ * Render the links to display when editing a pane.
+ */
+ function get_pane_links($pane, $content_type) {
+ $links = array();
+
+ if (!empty($pane->shown)) {
+ $links[] = array(
+ 'title' => t('Disable this pane'),
+ 'href' => $this->get_url('hide', $pane->pid),
+ 'attributes' => array('class' => 'ctools-use-ajax'),
+ );
+ }
+ else {
+ $links[] = array(
+ 'title' => t('Enable this pane'),
+ 'href' => $this->get_url('show', $pane->pid),
+ 'attributes' => array('class' => 'ctools-use-ajax'),
+ );
+ }
+
+ if (isset($this->display->title_pane) && $this->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' => $this->get_url('panel-title', $pane->pid),
+ 'attributes' => array('class' => 'ctools-use-ajax'),
+ );
+ }
+
+ $subtype = ctools_content_get_subtype($content_type, $pane->subtype);
+
+ if (ctools_content_editable($content_type, $subtype, $pane->configuration)) {
+ $links[] = array(
+ 'title' => isset($content_type['edit text']) ? $content_type['edit text'] : t('Settings'),
+ 'href' => $this->get_url('edit-pane', $pane->pid),
+ 'attributes' => array('class' => 'ctools-use-modal'),
+ );
+ }
+
+ if (user_access('administer advanced pane settings')) {
+ $links[] = array(
+ 'title' => t('CSS properties'),
+ 'href' => $this->get_url('pane-css', $pane->pid),
+ 'attributes' => array('class' => 'ctools-use-modal'),
+ );
+ }
+
+ $links[] = array(
+ 'title' => '<hr />',
+ 'html' => TRUE,
+ );
+
+ $style_links = $this->get_style_links('pane', $pane->pid);
+
+ $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 = $this->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' => $this->get_url('access-configure-test', $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' => $this->get_url('access-add-test', $pane->pid),
+ 'attributes' => array('class' => 'ctools-use-modal'),
+ );
+
+ $visibility_links[] = array(
+ 'title' => t('Settings'),
+ 'href' => $this->get_url('access-settings', $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' => $this->get_url('cache-method', $pane->pid),
+ 'attributes' => array('class' => 'ctools-use-modal'),
+ );
+ if (panels_plugin_get_function('cache', $info, 'settings form')) {
+ $cache_links[] = array(
+ 'title' => t('Settings'),
+ 'href' => $this->get_url('cache-settings', $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",
+ ),
+ );
+
+ return theme('ctools_dropdown', theme('image', ctools_image_path('icon-configure.png', 'panels')), $links, TRUE);
+ }
+
+ // -----------------------------------------------------------------------
+ // Display edit AJAX callbacks and helpers.
+
+ /**
+ * Generate a URL path for the AJAX editor.
+ */
+ function get_url() {
+ $args = func_get_args();
+ $command = array_shift($args);
+ $url = 'panels/ajax/' . $this->plugin['name'] . '/' . $command . '/' . $this->display->cache_key;
+ if ($args) {
+ $url .= '/' . implode('/', $args);
+ }
+
+ return $url;
+ }
+
+ /**
+ * AJAX command to show a pane.
+ */
+ function ajax_show($pid = NULL) {
+ if (empty($this->display->content[$pid])) {
+ ctools_ajax_render_error(t('Invalid pane id.'));
+ }
+
+ $this->display->content[$pid]->shown = TRUE;
+ panels_edit_cache_set($this->cache);
+
+ $this->command_update_pane($pid);
+ }
+
+ /**
+ * AJAX command to show a pane.
+ */
+ function ajax_hide($pid = NULL) {
+ if (empty($this->display->content[$pid])) {
+ ctools_ajax_render_error(t('Invalid pane id.'));
+ }
+
+ $this->display->content[$pid]->shown = FALSE;
+ panels_edit_cache_set($this->cache);
+
+ $this->command_update_pane($pid);
+ }
+
+ /**
+ * AJAX command to present a dialog with a list of available content.
+ */
+ function ajax_select_content($region = NULL, $category = NULL) {
+ if (!array_key_exists($region, $this->plugins['layout']['panels'])) {
+ ctools_modal_render(t('Error'), t('Invalid input'));
+ }
+
+ $title = t('Add content to !s', array('!s' => $this->plugins['layout']['panels'][$region]));
+
+ $categories = $this->get_categories($this->cache->content_types);
+
+ if (empty($categories)) {
+ $output = t('There are no content types you may add to this display.');
+ }
+ else {
+ $output = '<div class="panels-add-content-modal">';
+ $selector = $this->render_category_selector($categories, $category, $region);
+
+ $content = !empty($categories[$category]['content']) ? $categories[$category]['content'] : array();
+ $center = $this->render_category($content, $category, $region);
+
+ $output .= '<div class="panels-section-column panels-section-column-categories">'
+ . '<div class="inside">' . $selector . '</div></div>';
+ $output .= $center;
+ $output .= '</div>'; // panels-add-content-modal
+ }
+
+ $this->commands[] = ctools_modal_command_display($title, $output);
+ }
+
+ /**
+ * Return the category name and the category key of a given content
+ * type.
+ *
+ * @todo -- this should be in CTools.
+ */
+ function get_category($content_type) {
+ if (isset($content_type['top level'])) {
+ $category = 'root';
+ }
+ else if (isset($content_type['category'])) {
+ if (is_array($content_type['category'])) {
+ list($category, $weight) = $content_type['category'];
+ }
+ else {
+ $category = $content_type['category'];
+ }
+ }
+ else {
+ $category = t('Uncategorized');
+ }
+
+ return array(preg_replace('/[^a-z0-9]/', '-', strtolower($category)), $category);
+ }
+
+
+ /**
+ * Create a list of categories from all of the content type.
+ *
+ * @return array
+ * An array of categories. Each entry in the array will also be an array
+ * with 'title' as the printable title of the category, and 'content'
+ * being an array of all content in the category. Each item in the 'content'
+ * array contain the array plugin definition so that it can be later
+ * found in the content array. They will be keyed by the title so that they
+ * can be sorted.
+ */
+ function get_categories($content_types) {
+ $categories = array();
+ $category_names = array();
+
+ foreach ($content_types as $type_name => $subtypes) {
+ foreach ($subtypes as $subtype_name => $content_type) {
+ list($category_key, $category) = $this->get_category($content_type);
+
+ if (empty($categories[$category_key])) {
+ $categories[$category_key] = array(
+ 'title' => $category,
+ 'content' => array(),
+ );
+ $category_names[$category_key] = $category;
+ }
+
+ $content_title = filter_xss_admin($content_type['title']);
+
+ // Ensure content with the same title doesn't overwrite each other.
+ while (isset($categories['content'][$content_title])) {
+ $content_title .= '-';
+ }
+
+ $categories[$category_key]['content'][$content_title] = $content_type;
+ $categories[$category_key]['content'][$content_title]['type_name'] = $type_name;
+ $categories[$category_key]['content'][$content_title]['subtype_name'] = $subtype_name;
+ }
+ }
+
+ // Now sort
+ natcasesort($category_names);
+ foreach ($category_names as $category => $name) {
+ $output[$category] = $categories[$category];
+ }
+
+ return $output;
+ }
+
+ /**
+ * Render a single link to add a content type.
+ */
+ function render_add_content_link($region, $content_type) {
+ $title = filter_xss_admin($content_type['title']);
+ $description = isset($content_type['description']) ? $content_type['description'] : $title;
+ $icon = ctools_content_admin_icon($content_type);
+ $url = $this->get_url('add-pane', $region, $content_type['type_name'], $content_type['subtype_name']);
+
+ $output = '<div class="content-type-button clear-block">';
+ $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>';
+
+ return $output;
+ }
+
+ /**
+ * Render the selector widget in the add content modal to select categories.
+ */
+ function render_category_selector($categories, $category, $region) {
+ $output = '<div class="panels-categories-box">';
+
+ // Render our list of categories in column 0.
+ foreach ($categories as $key => $category_info) {
+ if ($key == 'root') {
+ continue;
+ }
+
+ $class = 'panels-modal-add-category';
+ if ($key == $category) {
+ $class .= ' active';
+ }
+
+ $url = $this->get_url('select-content', $region, $key);
+ $output .= ctools_ajax_text_button($category_info['title'], $url, '', $class);
+ }
+
+ $output .= '</div>'; // panels-categories-box
+
+ if (!empty($categories['root'])) {
+ foreach ($categories['root']['content'] as $content_type) {
+ $output .= $this->render_add_content_link($region, $content_type);
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Render all of the content links in a category.
+ */
+ function render_category($content, $category, $region) {
+ if (empty($category) || empty($content) || $category == 'root') {
+ $output = '<div class="panels-categories-description">';
+ $output .= t('Content options are divided by category. Please select a category from the left to proceed.');
+ $output .= '</div>';
+ return $output;
+ }
+
+ $titles = array_keys($content);
+ natcasesort($titles);
+
+ // Fill out the info for our current category.
+ $columns = 2;
+ $col[1] = '';
+ $col[2] = '';
+
+ $col_size = count($titles) / $columns;
+ $count = 0;
+ foreach ($titles as $title) {
+ $which = floor($count++ / $col_size) + 1; // we leave 0 for the categories.
+ $col[$which] .= $this->render_add_content_link($region, $content[$title]);
+ }
+
+ $output = '<div class="panels-section-columns">';
+ foreach ($col as $id => $column) {
+ $output .= '<div class="panels-section-column panels-section-column-' . $id . '">'
+ . '<div class="inside">' . $column . '</div></div>';
+ }
+ $output .= '</div>'; // columns
+
+ return $output;
+ }
+
+ /**
+ * AJAX entry point to add a new pane.
+ */
+ function ajax_add_pane($region = NULL, $type_name = NULL, $subtype_name = NULL, $step = NULL) {
+ $content_type = ctools_get_content_type($type_name);
+ $subtype = ctools_content_get_subtype($content_type, $subtype_name);
+
+ if (!isset($step) || !isset($this->cache->new_pane)) {
+ $pane = panels_new_pane($type_name, $subtype_name, TRUE);
+ $this->cache->new_pane = &$pane;
+ }
+ else {
+ $pane = &$this->cache->new_pane;
+ }
+
+ $form_state = array(
+ 'display' => &$this->cache->display,
+ 'contexts' => $this->cache->display->context,
+ 'pane' => &$pane,
+ 'cache_key' => $this->display->cache_key,
+ 'cache' => &$this->cache,
+ 'ajax' => TRUE,
+ 'modal' => TRUE,
+ // This will force the system to not automatically render.
+ 'modal return' => TRUE,
+ 'commands' => array(),
+ );
+
+ $form_info = array(
+ 'path' => $this->get_url('add-pane', $region, $type_name, $subtype_name, '%step'),
+ 'show cancel' => TRUE,
+ 'next callback' => 'panels_ajax_edit_pane_next',
+ 'finish callback' => 'panels_ajax_edit_pane_finish',
+ 'cancel callback' => 'panels_ajax_edit_pane_cancel',
+ );
+
+ $output = ctools_content_form('add', $form_info, $form_state, $content_type, $pane->subtype, $subtype, $pane->configuration, $step);
+
+ // If $rc is FALSE, there was no actual form.
+ if ($output === FALSE || !empty($form_state['complete'])) {
+ $pane = $this->cache->new_pane;
+ unset($this->cache->new_pane);
+
+ // Add the pane to the display
+ $this->display->add_pane($pane, $region);
+ panels_edit_cache_set($this->cache);
+
+ // Tell the client to draw the pane
+ $this->command_add_pane($pane);
+
+ // Dismiss the modal.
+ $this->commands[] = ctools_modal_command_dismiss();
+ }
+ else if (!empty($form_state['cancel'])) {
+ // If cancelling, return to the activity.
+ list($category_key, $category) = $this->get_category($subtype);
+ $this->ajax_select_content($region, $category_key);
+ }
+ else {
+ // This overwrites any previous commands.
+ $this->commands = ctools_modal_form_render($form_state, $output);
+ }
+ }
+
+ /**
+ * AJAX entry point to edit a pane.
+ */
+ function ajax_edit_pane($pid = NULL, $step = NULL) {
+ if (empty($this->cache->display->content[$pid])) {
+ ctools_modal_render(t('Error'), t('Invalid pane id.'));
+ }
+
+ $pane = &$this->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' => &$this->cache->display,
+ 'contexts' => $this->cache->display->context,
+ 'pane' => &$pane,
+ 'cache' => &$this->cache,
+ 'ajax' => TRUE,
+ 'modal' => TRUE,
+ 'modal return' => TRUE,
+ 'commands' => array(),
+ );
+
+ $form_info = array(
+ 'path' => $this->get_url('edit-pane', $pid, '%step'),
+ 'show cancel' => TRUE,
+ 'next callback' => 'panels_ajax_edit_pane_next',
+ 'finish callback' => 'panels_ajax_edit_pane_finish',
+ 'cancel callback' => 'panels_ajax_edit_pane_cancel',
+ );
+
+ $output = ctools_content_form('edit', $form_info, $form_state, $content_type, $pane->subtype, $subtype, $pane->configuration, $step);
+
+ // If $rc is FALSE, there was no actual form.
+ if ($output === FALSE || !empty($form_state['cancel'])) {
+ // Dismiss the modal.
+ $this->commands[] = ctools_modal_command_dismiss();
+ }
+ else if (!empty($form_state['complete'])) {
+ panels_edit_cache_set($this->cache);
+ $this->command_update_pane($pid);
+ $this->commands[] = ctools_modal_command_dismiss();
+ }
+ else {
+ // This overwrites any previous commands.
+ $this->commands = ctools_modal_form_render($form_state, $output);
+ }
+ }
+
+ /**
+ * AJAX entry point to select which pane is currently the title.
+ *
+ * @param string $pid
+ * The pane id for the pane object whose title state we're setting.
+ */
+ function ajax_panel_title($pid = NULL) {
+ if (empty($this->display->content[$pid])) {
+ ctools_ajax_render_error(t('Invalid pane id.'));
+ }
+
+ $pane = &$this->display->content[$pid];
+
+ $old_title = !empty($this->display->title_pane) ? $this->display->title_pane : NULL;
+ $this->display->title_pane = $pid;
+
+ panels_edit_cache_set($this->cache);
+
+ $this->command_update_pane($pane);
+
+ if ($old_title && !empty($this->cache->display->content[$old_title])) {
+ $this->command_update_pane($this->cache->display->content[$old_title]);
+ }
+ }
+
+ /**
+ * AJAX entry point to configure the cache method for a pane or the display.
+ *
+ * @param string $pid
+ * Either a pane id for a pane in the display, or 'display' to edit the
+ * display cache settings.
+ */
+ function ajax_cache_method($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 = &$this->display->cache;
+ $title = t('Cache method for this display');
+ }
+ else if (!empty($this->display->content[$pid])) {
+ $pane = &$this->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' => &$this->display,
+ 'conf' => &$conf,
+ 'title' => $title,
+ 'ajax' => TRUE,
+ );
+
+ $output = ctools_modal_form_wrapper('panels_edit_cache_method_form', $form_state);
+ if (!empty($output)) {
+ $this->commands = $output;
+ return;
+ }
+
+ // 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($this->cache);
+
+ $this->commands[] = ctools_modal_command_dismiss();
+
+ if ($pid != 'display') {
+ $this->command_update_pane($pane);
+ }
+ else {
+ $this->command_update_display_links();
+ }
+ }
+ else {
+ $this->cache->method = $form_state['method'];
+ panels_edit_cache_set($this->cache);
+ // send them to next form.
+ return $this->ajax_cache_settings($pid);
+ }
+ }
+
+ /**
+ * AJAX entry point to configure the cache settings for a pane or the display.
+ *
+ * @param string $pid
+ * Either a pane id for a pane in the display, or 'display' to edit the
+ * display cache settings.
+ */
+ function ajax_cache_settings($pid = 0) {
+ ctools_include('content');
+
+ // This lets us choose whether we're doing the display's cache or
+ // a pane's.
+ if ($pid == 'display') {
+ $conf = &$this->display->cache;
+ $title = t('Cache settings for this display');
+ }
+ else if (!empty($this->display->content[$pid])) {
+ $pane = &$this->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($this->cache->method) && (empty($conf['method']) || $conf['method'] != $this->cache->method)) {
+ $conf['method'] = $this->cache->method;
+ $info = panels_get_cache($conf['method']);
+ $conf['settings'] = isset($info['defaults']) ? $info['defaults'] : array();
+ }
+
+ $form_state = array(
+ 'display' => &$this->display,
+ 'pid' => $pid,
+ 'conf' => &$conf,
+ 'ajax' => TRUE,
+ 'title' => $title,
+ 'url' => url($this->get_url('cache-settings', $pid), array('absolute' => TRUE)),
+ );
+
+ $output = ctools_modal_form_wrapper('panels_edit_cache_settings_form', $form_state);
+ if (!empty($output)) {
+ $this->commands = $output;
+ return;
+ }
+
+ panels_edit_cache_set($this->cache);
+
+ $this->commands[] = ctools_modal_command_dismiss();
+
+ if ($pid != 'display') {
+ $this->command_update_pane($pane);
+ }
+ else {
+ $this->command_update_display_links();
+ }
+ }
+
+ /**
+ * AJAX entry point to select the style for a display, region or pane.
+ *
+ * @param string $type
+ * Either display, region or pane
+ * @param $pid
+ * The pane id, if a pane. The region id, if a region.
+ */
+ function ajax_style_type($type, $pid = NULL) {
+ // This lets us choose whether we're doing the display's cache or
+ // a pane's.
+ switch ($type) {
+ case 'display':
+ $style = isset($this->display->panel_settings['style']) ? $this->display->panel_settings['style'] : 'default';
+ $title = t('Default style for this display');
+ break;
+
+ case 'region':
+ $style = isset($this->display->panel_settings[$pid]['style']) ? $this->display->panel_settings[$pid]['style'] : '-1'; // -1 signifies to use the default setting.
+ $title = t('Panel style for region "!region"', array('!region' => $this->plugins['layout']['panels'][$pid]));
+ break;
+
+ case 'pane':
+ ctools_include('content');
+ $pane = &$this->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.'));
+ }
+ $info = $this->get_style($type, $pid);
+ $style_plugin = $info[0];
+ $style_settings = $info[1];
+
+ // Backward compatibility: Translate old-style stylizer to new style
+ // stylizer.
+ if ($style == 'stylizer' && !empty($style_settings['style']) && $style_settings['style'] != '$') {
+ $style = 'stylizer:' . $style_settings['style'];
+ }
+
+ $form_state = array(
+ 'display' => &$this->display,
+ 'style' => $style,
+ 'title' => $title,
+ 'ajax' => TRUE,
+ 'type' => $type,
+ );
+
+ $output = ctools_modal_form_wrapper('panels_edit_style_type_form', $form_state);
+ if (!empty($output)) {
+ $this->commands = $output;
+ return;
+ }
+
+ // 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('styles', $style, ($type == 'pane') ? 'pane settings form' : 'settings form');
+ if (!$function) {
+ if (isset($this->cache->style)) {
+ unset($this->cache->style);
+ }
+
+ // If there's no settings form, just change the style and exit.
+ switch($type) {
+ case 'display':
+ $this->display->panel_settings['style'] = $form_state['style'];
+ if (isset($this->display->panel_settings['style_settings']['default'])) {
+ unset($this->display->panel_settings['style_settings']['default']);
+ }
+ break;
+
+ case 'region':
+ $this->display->panel_settings[$pid]['style'] = $form_state['style'];
+ if (isset($this->display->panel_settings['style_settings'][$pid])) {
+ unset($this->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($this->cache);
+
+ $this->commands[] = ctools_modal_command_dismiss();
+
+ if ($type == 'pane') {
+ $this->command_update_pane($pane);
+ }
+ else if ($type == 'region') {
+ $this->command_update_region_links($pid);
+ }
+ else {
+ $this->command_update_display_links();
+ }
+ }
+ else {
+ if ($form_state['style'] != $form_state['old_style']) {
+ $this->cache->style = $form_state['style'];
+ panels_edit_cache_set($this->cache);
+ }
+
+ // send them to next form.
+ return $this->ajax_style_settings($type, $pid);
+ }
+ }
+
+ /**
+ * Get the appropriate style from the panel in the cache.
+ *
+ * Since we have styles for regions, panes and the display itself, and
+ * they are stored differently, we use this method to simplify getting
+ * style information into a way that's easy to cope with.
+ */
+ function get_style($type, $pid = '') {
+ if (isset($this->cache->style)) {
+ $style = panels_get_style($this->cache->style);
+ $defaults = isset($style['defaults']) ? $style['defaults'] : array();
+ // Get the &$conf variable based upon whose style we're editing.
+ switch ($type) {
+ case 'display':
+ $this->display->panel_settings['style'] = $this->cache->style;
+ $this->display->panel_settings['style_settings']['default'] = $defaults;
+ break;
+
+ case 'region':
+ $this->display->panel_settings[$pid]['style'] = $this->cache->style;
+ $this->display->panel_settings['style_settings'][$pid] = $defaults;
+ break;
+
+ case 'pane':
+ $pane = &$this->display->content[$pid];
+ $pane->style['style'] = $this->cache->style;
+ $pane->style['settings'] = $defaults;
+ $conf = &$pane->style['settings'];
+ break;
+ }
+ }
+ else {
+ switch ($type) {
+ case 'display':
+ $style = panels_get_style((!empty($this->display->panel_settings['style'])) ? $this->display->panel_settings['style'] : 'default');
+ break;
+
+ case 'region':
+ $style = panels_get_style((!empty($this->display->panel_settings[$pid]['style'])) ? $this->display->panel_settings[$pid]['style'] : '-1');
+ break;
+
+ case 'pane':
+ $pane = &$this->display->content[$pid];
+ $style = panels_get_style(!empty($pane->style['style']) ? $pane->style['style'] : 'default');
+ break;
+ }
+ }
+
+ // Set up our $conf reference.
+ switch ($type) {
+ case 'display':
+ $conf = &$this->display->panel_settings['style_settings']['default'];
+ break;
+
+ case 'region':
+ $conf = &$this->display->panel_settings['style_settings'][$pid];
+ break;
+
+ case 'pane':
+ ctools_include('content');
+ $pane = &$this->display->content[$pid];
+ $conf = &$pane->style['settings'];
+ break;
+ }
+
+ // Backward compatibility: Translate old-style stylizer to new style
+ // stylizer.
+ if ($style['name'] == 'stylizer' && !empty($conf['style']) && $conf['style'] != '$') {
+ $style = panels_get_style('stylizer:' . $conf['style']);
+ }
+
+ return array($style, &$conf);
+ }
+
+ /**
+ * AJAX entry point to configure the style for a display, region or pane.
+ *
+ * @param string $type
+ * Either display, region or pane
+ * @param $pid
+ * The pane id, if a pane. The region id, if a region.
+ */
+ function ajax_style_settings($type, $pid = '') {
+ $info = $this->get_style($type, $pid);
+ $style = $info[0];
+ $conf = &$info[1];
+
+ switch ($type) {
+ case 'display':
+ $title = t('Style settings for @style (display)', array('@style' => $style['title']));
+ break;
+
+ case 'region':
+ $title = t('Style settings for style @style (Region "!region")', array('@style' => $style['title'], '!region' => $this->plugins['layout']['panels'][$pid]));
+ break;
+
+ case 'pane':
+ ctools_include('content');
+ $pane = &$this->display->content[$pid];
+ $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' => &$this->display,
+ 'type' => $type,
+ 'pid' => $pid,
+ 'conf' => &$conf,
+ 'style' => $style,
+ 'ajax' => TRUE,
+ 'title' => $title,
+ 'url' => url($this->get_url('style-settings', $type, $pid), array('absolute' => TRUE)),
+ 'renderer' => &$this,
+ );
+
+ $output = ctools_modal_form_wrapper('panels_edit_style_settings_form', $form_state);
+ if (!empty($output)) {
+ $this->commands = $output;
+ return;
+ }
+
+ if (isset($this->cache->style)) {
+ unset($this->cache->style);
+ }
+
+ // $conf was a reference so it should just modify.
+ panels_edit_cache_set($this->cache);
+
+ $this->commands[] = ctools_modal_command_dismiss();
+
+ if ($type == 'pane') {
+ $this->command_update_pane($pane);
+ }
+ else if ($type == 'region') {
+ $this->command_update_region_links($pid);
+ }
+ else {
+ $this->command_update_display_links();
+ }
+ }
+
+ /**
+ * AJAX entry point to configure CSS for a pane.
+ *
+ * @param $pid
+ * The pane id to edit.
+ */
+ function ajax_pane_css($pid = NULL) {
+ if (empty($this->display->content[$pid])) {
+ ctools_modal_render(t('Error'), t('Invalid pane id.'));
+ }
+
+ $pane = &$this->display->content[$pid];
+ $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
+
+ $form_state = array(
+ 'display' => &$this->display,
+ 'pane' => &$pane,
+ 'ajax' => TRUE,
+ 'title' => t('Configure CSS on !subtype_title', array('!subtype_title' => $subtype['title'])),
+ );
+
+ $output = ctools_modal_form_wrapper('panels_edit_configure_pane_css_form', $form_state);
+ if (!empty($output)) {
+ $this->commands = $output;
+ return;
+ }
+
+ panels_edit_cache_set($this->cache);
+ $this->command_update_pane($pid);
+ $this->commands[] = ctools_modal_command_dismiss();
+ }
+
+ /**
+ * AJAX entry point to configure access settings for a pane.
+ *
+ * @param $pid
+ * The pane id to edit.
+ */
+ function ajax_access_settings($pid = NULL) {
+ if (empty($this->display->content[$pid])) {
+ ctools_modal_render(t('Error'), t('Invalid pane id.'));
+ }
+
+ $pane = &$this->display->content[$pid];
+ $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
+
+ $form_state = array(
+ 'display' => &$this->display,
+ 'pane' => &$pane,
+ 'ajax' => TRUE,
+ 'title' => t('Access settings on !subtype_title', array('!subtype_title' => $subtype['title'])),
+ );
+
+ $output = ctools_modal_form_wrapper('panels_edit_configure_access_settings_form', $form_state);
+ if (!empty($output)) {
+ $this->commands = $output;
+ return;
+ }
+
+ panels_edit_cache_set($this->cache);
+ $this->command_update_pane($pid);
+ $this->commands[] = ctools_modal_command_dismiss();
+ }
+
+ /**
+ * AJAX entry point for to add a visibility rule.
+ */
+ function ajax_access_add_test($pid = NULL) {
+ if (empty($this->display->content[$pid])) {
+ ctools_modal_render(t('Error'), t('Invalid pane id.'));
+ }
+
+ $pane = &$this->display->content[$pid];
+ $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
+
+ $form_state = array(
+ 'display' => &$this->display,
+ 'pane' => &$pane,
+ 'ajax' => TRUE,
+ 'title' => t('Add visibility rule for !subtype_title', array('!subtype_title' => $subtype['title'])),
+ );
+
+ $output = ctools_modal_form_wrapper('panels_edit_add_access_test_form', $form_state);
+ if (empty($output)) {
+ // Set up the plugin in cache
+ $plugin = ctools_get_access_plugin($form_state['values']['type']);
+ $this->cache->new_plugin = ctools_access_new_test($plugin);
+ panels_edit_cache_set($this->cache);
+
+ // go to the next step.
+ return $this->ajax_access_configure_test($pid, 'add');
+ }
+
+ ctools_ajax_render($output);
+ }
+
+ /**
+ * AJAX entry point for to configure vsibility rule.
+ */
+ function ajax_access_configure_test($pid = NULL, $id = NULL) {
+ if (empty($this->display->content[$pid])) {
+ ctools_modal_render(t('Error'), t('Invalid pane id.'));
+ }
+
+ $pane = &$this->display->content[$pid];
+ $subtype = ctools_content_get_subtype($pane->type, $pane->subtype);
+
+ // Set this up here because $id gets changed later.
+ $url = $this->get_url('access-configure-test', $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'][] = $this->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' => &$this->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_edit_configure_access_test_form', $form_state);
+ if (!empty($output)) {
+ $this->commands = $output;
+ return;
+ }
+
+ // Unset the new plugin
+ if (isset($this->cache->new_plugin)) {
+ unset($this->cache->new_plugin);
+ }
+
+ if (!empty($form_state['remove'])) {
+ unset($pane->access['plugins'][$id]);
+ }
+
+ panels_edit_cache_set($this->cache);
+ $this->command_update_pane($pid);
+ $this->commands[] = ctools_modal_command_dismiss();
+ }
+
+ /**
+ * AJAX Router function for layout owned AJAX calls.
+ *
+ * Layouts like the flexible layout builder need callbacks of their own.
+ * This allows those layouts to simply declare their callbacks and use
+ * them with $this->get_url('layout', $command).
+ */
+ function ajax_layout() {
+ $args = func_get_args();
+ if (empty($args)) {
+ return MENU_NOT_FOUND;
+ }
+
+ $command = array_shift($args);
+ if (empty($this->plugins['layout']['ajax'][$command]) || !function_exists($this->plugins['layout']['ajax'][$command])) {
+ return MENU_NOT_FOUND;
+ }
+
+ // Make sure the this is always available to the called functions.
+ array_unshift($args, $this);
+ return call_user_func_array($this->plugins['layout']['ajax'][$command], $args);
+ }
+
+ /**
+ * AJAX Router function for style owned AJAX calls.
+ *
+ * Styles like the stylizer need AJAX callbacks of their own. This
+ * allows the system to figure out which style is being referenced,
+ * load it, and execute the callback.
+ *
+ * This allows those layouts to simply declare their callbacks and use
+ * them using $this->get_url('style', $command, $type, $pid).
+ */
+ function ajax_style() {
+ $args = func_get_args();
+ if (count($args) < 3) {
+ return MENU_NOT_FOUND;
+ }
+
+ $command = array_shift($args);
+ $type = array_shift($args);
+ $pid = array_shift($args);
+
+ $info = $this->get_style($type, $pid);
+
+ $style = $info[0];
+ $conf = &$info[1];
+
+ if (empty($style['ajax'][$command]) || !function_exists($style['ajax'][$command])) {
+ return MENU_NOT_FOUND;
+ }
+
+ // Make sure the this is always available to the called functions.
+ $args = array_merge(array(&$this, $style, &$conf, $type, $pid), $args);
+ return call_user_func_array($style['ajax'][$command], $args);
+ }
+
+ // ------------------------------------------------------------------------
+ // AJAX command generators
+ //
+ // These are used to make sure that child implementations can control their
+ // own AJAX commands as needed.
+
+ /**
+ * 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("#panel-pane-$pane->pid", $this->render_pane($pane));
+ $this->commands[] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
+ }
+
+ /**
+ * 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];
+ }
+
+ $this->commands[] = ctools_ajax_command_append("#panel-pane-$pane->panel", $this->render_pane($pane));
+ $this->commands[] = ctools_ajax_command_changed("#panel-pane-$pane->pid", "div.grabber span.text");
+ }
+
+ /**
+ * Create a command to update the links on a display after a change was made.
+ */
+ function command_update_display_links() {
+ $this->commands[] = ctools_ajax_command_replace('.panels-display-links', $this->get_display_links());
+ }
+
+ /**
+ * Create a command to update the links on a region after a change was made.
+ */
+ function command_update_region_links($id) {
+ $this->commands[] = ctools_ajax_command_replace('.panels-region-links-' . $id, $this->get_region_links($id));
+ }
+}
+
+/**
+ * Handle the 'next' click on the add/edit pane form wizard.
+ *
+ * All we need to do is store the updated pane in the cache.
+ */
+function panels_ajax_edit_pane_next(&$form_state) {
+ $form_state['cache']->new_pane = $form_state['pane'];
+ panels_edit_cache_set($form_state['cache']);
+}
+
+/**
+ * Handle the 'finish' click on teh add/edit pane form wizard.
+ *
+ * All we need to do is set a flag so the return can handle adding
+ * the pane.
+ */
+function panels_ajax_edit_pane_finish(&$form_state) {
+ $form_state['complete'] = TRUE;
+ return;
+}
+
+/**
+ * Handle the 'cancel' click on the add/edit pane form wizard.
+ */
+function panels_ajax_edit_pane_cancel(&$form_state) {
+ $form_state['cancel'] = TRUE;
+ return;
+}
+
+// --------------------------------------------------------------------------
+// Forms for the editor object
+
+/**
+ * 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'];
+}
+
+/**
+ * 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'];
+}
+
+/**
+ * Choose style 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' : (variable_get('panels_legacy_rendering_mode', TRUE) ? 'render panel' : 'render region'));
+ $options = array();
+ if ($type == 'region') {
+ $options[-1] = t('Use display default style');
+ }
+
+ uasort($styles, 'ctools_plugin_sort');
+
+ foreach ($styles as $id => $info) {
+ if (empty($info['hidden']) && (!empty($info[$function]) || $id == 'default')) {
+ $options[$id] = check_plain($info['title']);
+ }
+ }
+
+ $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_style_type_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'];
+}
+
+/**
+ * Style 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('styles', $style, ($type == 'pane') ? 'pane settings form' : 'settings form');
+
+ $form['settings'] = $function($conf, $display, $pid, $type, $form_state);
+ $form['settings']['#tree'] = TRUE;
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ );
+
+ return $form;
+}
+
+/**
+ * Validate style 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('styles', $form_state['style'], $name)) {
+ $function($form, $form_state['values']['settings'], $form_state);
+ }
+}
+
+/**
+ * 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('styles', $form_state['style'], $name)) {
+ $function($form, $form_state['values']['settings'], $form_state);
+ }
+
+ $form_state['conf'] = $form_state['values']['settings'];
+}
+
+
+/**
+ * Configure CSS on a pane form.
+ */
+function panels_edit_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_edit_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'];
+}
+
+/**
+ * Form to control basic visibility settings.
+ */
+function panels_edit_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 criterion 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_edit_configure_access_settings_form_submit($form, &$form_state) {
+ $pane = &$form_state['pane'];
+ $display = $form_state['display'];
+
+ $pane->access['logic'] = $form_state['values']['logic'];
+}
+
+/**
+ * Form to add a visibility rule.
+ */
+function panels_edit_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;
+}
+
+/**
+ * Form to configure a visibility rule.
+ */
+function panels_edit_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['not'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Reverse (NOT)'),
+ '#default_value' => !empty($test['not']),
+ );
+
+ $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_edit_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_edit_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'];
+ }
+ $form_state['test']['not'] = !empty($form_state['values']['not']);
+}
+
diff --git a/plugins/display_renderers/panels_renderer_legacy.class.php b/plugins/display_renderers/panels_renderer_legacy.class.php
new file mode 100644
index 0000000..87fae60
--- /dev/null
+++ b/plugins/display_renderers/panels_renderer_legacy.class.php
@@ -0,0 +1,231 @@
+<?php
+
+/**
+ * Legacy render pipeline for a panels display.
+ *
+ * This render pipeline mirrors the old procedural system exactly, and plugins
+ * written for the legacy system will work exactly as they did before with this
+ * renderer.
+ *
+ * Most plugins will work with the newer renderer. These are the exceptions:
+ * - Style plugins that implement panel styling no longer need to call
+ * panels_render_pane() on all contained panes; rendered pane HTML is now
+ * passed in directly.
+ * - Cache plugins are now triggered on rendered HTML, rather than on
+ * unrendered datastructures, when acting at the display level. When acting
+ * at the pane level, they still receive the unrendered datastructure.
+ *
+ * If your site relies on any of these plugin behaviors, you will need to use
+ * this renderer instead of the new panels_renderer_standard() until those
+ * plugins are updated.
+ */
+class panels_renderer_legacy {
+ var $display;
+ var $plugins = array();
+
+ function init($plugin, &$display) {
+ $this->plugin = $plugin;
+ $this->plugins['layout'] = panels_get_layout($display->layout);
+ if (empty($this->plugins['layout'])) {
+ watchdog('panels', "Layout: @layout couldn't been found, maybe the theme is disabled.", array('@layout' => $display->layout));
+ }
+ $this->display = &$display;
+ }
+
+ /**
+ * Builds inner content, then hands off to layout-specified theme function for
+ * final render step.
+ *
+ * This is the outermost method in the Panels render pipeline. It calls the
+ * inner methods, which return a content array, which is in turn passed to the
+ * theme function specified in the layout plugin.
+ *
+ * @return string
+ * Themed & rendered HTML output.
+ */
+ function render() {
+ if (!empty($this->plugins['layout']['css'])) {
+ if (file_exists(path_to_theme() . '/' . $this->plugins['layout']['css'])) {
+ drupal_add_css(path_to_theme() . '/' . $this->plugins['layout']['css']);
+ }
+ else {
+ drupal_add_css($this->plugins['layout']['path'] . '/' . $this->plugins['layout']['css']);
+ }
+ }
+ // This now comes after the CSS is added, because panels-within-panels must
+ // have their CSS added in the right order; inner content before outer content.
+
+ if (empty($this->display->cache['method']) || !empty($this->display->skip_cache)) {
+ $content = $this->render_regions();
+ }
+ else {
+ $cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context);
+ if ($cache === FALSE) {
+ $cache = new panels_cache_object();
+ $cache->set_content($this->render_regions());
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context);
+ }
+ $content = $cache->content;
+ }
+
+ $output = theme($this->plugins['layout']['theme'], check_plain($this->display->css_id), $content, $this->display->layout_settings, $this->display, $this->plugins['layout'], $this);
+
+ return $output;
+ }
+
+ /**
+ * Render all panes in the attached display into their panel regions, then
+ * render those regions.
+ *
+ * @return array $content
+ * An array of rendered panel regions, keyed on the region name.
+ */
+ function render_regions() {
+ ctools_include('content');
+
+ // First, render all the panes into little boxes. We do this here because
+ // some panes request to be rendered after other panes (primarily so they
+ // can do the leftovers of forms).
+ $panes = array();
+ $later = array();
+
+ foreach ($this->display->content as $pid => $pane) {
+ $pane->shown = !empty($pane->shown); // guarantee this field exists.
+ // If the user can't see this pane, do not render it.
+ if (!$pane->shown || !panels_pane_access($pane, $this->display)) {
+ continue;
+ }
+
+ // If this pane wants to render last, add it to the $later array.
+ $content_type = ctools_get_content_type($pane->type);
+
+ if (!empty($content_type['render last'])) {
+ $later[$pid] = $pane;
+ continue;
+ }
+
+ $panes[$pid] = $this->render_pane($pane);
+ }
+
+ foreach ($later as $pid => $pane) {
+ $panes[$pid] = $this->render_pane($pane);
+ }
+
+ // Loop through all panels, put all panes that belong to the current panel
+ // in an array, then render the panel. Primarily this ensures that the
+ // panes are in the proper order.
+ $content = array();
+ foreach ($this->display->panels as $panel_name => $pids) {
+ $panel_panes = array();
+ foreach ($pids as $pid) {
+ if (!empty($panes[$pid])) {
+ $panel_panes[$pid] = $panes[$pid];
+ }
+ }
+ $content[$panel_name] = $this->render_region($panel_name, $panel_panes);
+ }
+
+ // Prevent notices by making sure that all panels at least have an entry:
+ $panels = panels_get_regions($this->plugins['layout'], $this->display);
+ foreach ($panels as $id => $panel) {
+ if (!isset($content[$id])) {
+ $content[$id] = NULL;
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Render the contents of a single pane.
+ *
+ * This method retrieves pane content and produces a ready-to-render content
+ * object. It also manages pane-specific caching.
+ *
+ * @param stdClass $pane
+ * A Panels pane object, as loaded from the database.
+ */
+ function render_pane($pane) {
+ ctools_include('context');
+ if (!is_array($this->display->context)) {
+ $this->display->context = array();
+ }
+
+ $content = FALSE;
+ $caching = !empty($pane->cache['method']) && empty($this->display->skip_cache);
+ if ($caching && ($cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context, $pane))) {
+ $content = $cache->content;
+ }
+ else {
+ $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, array(), $this->display->args, $this->display->context);
+ foreach (module_implements('panels_pane_content_alter') as $module) {
+ $function = $module . '_panels_pane_content_alter';
+ $function($content, $pane, $this->display->args, $this->display->context);
+ }
+ if ($caching) {
+ $cache = new panels_cache_object();
+ $cache->set_content($content);
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $pane);
+ $content = $cache->content;
+ }
+ }
+
+ // Pass long the css_id that is usually available.
+ if (!empty($pane->css['css_id'])) {
+ $content->css_id = $pane->css['css_id'];
+ }
+
+ // Pass long the css_class that is usually available.
+ if (!empty($pane->css['css_class'])) {
+ $content->css_class = $pane->css['css_class'];
+ }
+
+ return $content;
+ }
+
+ /**
+ * Render a single panel region.
+ *
+ * Primarily just a passthrough to the panel region rendering callback
+ * specified by the style plugin that is attached to the current panel region.
+ *
+ * @param $region_name
+ * The ID of the panel region being rendered
+ * @param $panes
+ * An array of panes that are assigned to the panel that's being rendered.
+ *
+ * @return
+ * The rendered HTML for the passed-in panel region.
+ */
+ function render_region($region_name, $panes) {
+ list($style, $style_settings) = panels_get_panel_style_and_settings($this->display->panel_settings, $region_name);
+ $callback = 'render panel';
+
+ // Retrieve the pid (can be a panel page id, a mini panel id, etc.), this
+ // might be used (or even necessary) for some panel display styles.
+ $owner_id = 0;
+ if (isset($this->display->owner) && is_object($this->display->owner) && isset($this->display->owner->id)) {
+ $owner_id = $this->display->owner->id;
+ }
+
+ // Check to see if we're actually running a current style plugin even though
+ // we're in the legacy renderer
+ if (version_compare($style['version'], 2.0, '>=')) {
+ // We are, so pre-render the content as the current version expects
+ foreach($panes as $pane_id => $pane) {
+ $content = panels_render_pane($pane, $this->display->content[$pane_id], $this->display);
+ if ($content) {
+ $panes[$pane_id] = $content;
+ }
+ else {
+ unset($panes[$pane_id]);
+ }
+ }
+ // And set the callback to the new key
+ $callback = 'render region';
+
+ }
+
+ return theme($style[$callback], $this->display, $owner_id, $panes, $style_settings, $region_name, $style);
+ }
+}
diff --git a/plugins/display_renderers/panels_renderer_simple.class.php b/plugins/display_renderers/panels_renderer_simple.class.php
new file mode 100644
index 0000000..9a813ac
--- /dev/null
+++ b/plugins/display_renderers/panels_renderer_simple.class.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * @file
+ * Contains the simple display renderer.
+ */
+
+/**
+ * The simple display renderer renders a display normally, except each pane
+ * is already rendered content, rather than a pane containing CTools content
+ * to be rendered. Styles are not supported.
+ */
+class panels_renderer_simple extends panels_renderer_standard {
+ function render_regions() {
+ $this->rendered['regions'] = array();
+ foreach ($this->display->content as $region_id => $content) {
+ if (is_array($content)) {
+ $content = implode('', $content);
+ }
+
+ $this->rendered['regions'][$region_id] = $content;
+ }
+ return $this->rendered['regions'];
+ }
+
+ function render_panes() {
+ // NOP
+ }
+
+ function prepare() {
+ $this->prep_run = TRUE;
+ }
+}
diff --git a/plugins/display_renderers/panels_renderer_single_pane.class.php b/plugins/display_renderers/panels_renderer_single_pane.class.php
new file mode 100644
index 0000000..ccdebaf
--- /dev/null
+++ b/plugins/display_renderers/panels_renderer_single_pane.class.php
@@ -0,0 +1,41 @@
+<?php
+
+class panels_renderer_single_pane extends panels_renderer_standard {
+ /**
+ * The pane id of the pane that will be rendered by a call to the render()
+ * method. Numeric int or string (typically if a new-# id has been used).
+ * @var mixed
+ */
+ var $render_pid;
+
+ /**
+ * Modified build method (vs. panels_renderer_standard::build()); takes just
+ * the display, no layout is necessary.
+ *
+ * @param array $plugin
+ * The definition of the renderer plugin.
+ *
+ * @param panels_display $display
+ * The panels display object to be rendered.
+ */
+ function init($plugin, &$display) {
+ $this->plugin = $plugin;
+ $this->display = &$display;
+ }
+
+ function prepare($external_settings = NULL) {
+ $this->render_pid = $external_settings;
+ }
+
+ function render() {
+ // If no requested pid, or requested pid does not exist,
+ if (empty($this->render_pid) || empty($this->display->content[$this->render_pid])) {
+ return NULL;
+ }
+ return $this->render_pane($this->display->content[$this->render_pid]);
+ }
+
+ function render_single($pid) {
+ return $this->render_pane($this->display->content[$pid]);
+ }
+} \ No newline at end of file
diff --git a/plugins/display_renderers/panels_renderer_standard.class.php b/plugins/display_renderers/panels_renderer_standard.class.php
new file mode 100644
index 0000000..729a1f5
--- /dev/null
+++ b/plugins/display_renderers/panels_renderer_standard.class.php
@@ -0,0 +1,605 @@
+<?php
+// $Id$
+
+/**
+ * The standard render pipeline for a Panels display object.
+ *
+ * Given a fully-loaded panels_display object, this class will turn its
+ * combination of layout, panes, and styles into HTML, invoking caching
+ * appropriately along the way. Interacting with the renderer externally is
+ * very simple - just pass it the display object and call the render() method:
+ *
+ * @code
+ * // given that $display is a fully loaded Panels display object
+ * $renderer = panels_get_renderer_handler('standard', $display)
+ * $html_output = $renderer->render();
+ * @endcode
+ *
+ * Internally, the render pipeline is divided into two phases, prepare and
+ * render:
+ * - The prepare phase transforms the skeletal data on the provided
+ * display object into a structure that is expected by the render phase.
+ * It is divided into a series of discrete sub-methods and operates
+ * primarily by passing parameters, all with the intention of making
+ * subclassing easier.
+ * - The render phase relies primarily on data stored in the renderer object's
+ * properties, presumably set in the prepare phase. It iterates through the
+ * rendering of each pane, pane styling, placement in panel regions, region
+ * styling, and finally the arrangement of rendered regions in the layout.
+ * Caching, if in use, is triggered per pane, or on the entire display.
+ *
+ * In short: prepare builds conf, render renders conf. Subclasses should respect
+ * this separation of responsibilities by adhering to these loose guidelines,
+ * given a loaded display object:
+ * - If your renderer needs to modify the datastructure representing what is
+ * to be rendered (panes and their conf, styles, caching, etc.), it should
+ * use the prepare phase.
+ * - If your renderer needs to modify the manner in which that renderable
+ * datastructure data is rendered, it should use the render phase.
+ *
+ * In the vast majority of use cases, this standard renderer will be sufficient
+ * and need not be switched out/subclassed; style and/or layout plugins can
+ * accommodate nearly every use case. If you think you might need a custom
+ * renderer, consider the following criteria/examples:
+ * - Some additional markup needs to be added to EVERY SINGLE panel.
+ * - Given a full display object, just render one pane.
+ * - Show a Panels admin interface.
+ *
+ * The system is almost functionally identical to the old procedural approach,
+ * with some exceptions (@see panels_renderer_legacy for details). The approach
+ * here differs primarily in its friendliness to tweaking in subclasses.
+ */
+class panels_renderer_standard {
+ /**
+ * The fully-loaded Panels display object that is to be rendered. "Fully
+ * loaded" is defined as:
+ * 1. Having been produced by panels_load_displays(), whether or this page
+ * request or at some time in the past and the object was exported.
+ * 2. Having had some external code attach context data ($display->context),
+ * in the exact form expected by panes. Context matching is delicate,
+ * typically relying on exact string matches, so special attention must
+ * be taken.
+ *
+ * @var panels_display
+ */
+ var $display;
+
+ /**
+ * An associative array of loaded plugins. Used primarily as a central
+ * location for storing plugins that require additional loading beyond
+ * reading the plugin definition, which is already statically cached by
+ * ctools_get_plugins(). An example is layout plugins, which can optionally
+ * have a callback that determines the set of panel regions available at
+ * runtime.
+ *
+ * @var array
+ */
+ var $plugins = array();
+
+ /**
+ * A multilevel array of rendered data. The first level of the array
+ * indicates the type of rendered data, typically with up to three keys:
+ * 'layout', 'regions', and 'panes'. The relevant rendered data is stored as
+ * the value for each of these keys as it is generated:
+ * - 'panes' are an associative array of rendered output, keyed on pane id.
+ * - 'regions' are an associative array of rendered output, keyed on region
+ * name.
+ * - 'layout' is the whole of the rendered output.
+ *
+ * @var array
+ */
+ var $rendered = array();
+
+ /**
+ * A multilevel array of data prepared for rendering. The first level of the
+ * array indicates the type of prepared data. The standard renderer populates
+ * and uses two top-level keys, 'panes' and 'regions':
+ * - 'panes' are an associative array of pane objects to be rendered, keyed
+ * on pane id and sorted into proper rendering order.
+ * - 'regions' are an associative array of regions, keyed on region name,
+ * each of which is itself an indexed array of pane ids in the order in
+ * which those panes appear in that region.
+ *
+ * @var array
+ */
+ var $prepared = array();
+
+ /**
+ * Boolean state variable, indicating whether or not the prepare() method has
+ * been run.
+ *
+ * This state is checked in panels_renderer_standard::render_layout() to
+ * determine whether the prepare method should be automatically triggered.
+ *
+ * @var bool
+ */
+ var $prep_run = FALSE;
+
+ /**
+ * The plugin that defines this handler.
+ */
+ var $plugin = FALSE;
+
+ /**
+ * TRUE if this renderer is rendering in administrative mode
+ * which will allow layouts to have extra functionality.
+ *
+ * @var bool
+ */
+ var $admin = FALSE;
+
+ /**
+ * Where to add standard meta information. There are three possibilities:
+ * - standard: Put the meta information in the normal location. Default.
+ * - inline: Put the meta information directly inline. This will
+ * not work for javascript.
+ *
+ * @var string
+ */
+ var $meta_location = 'standard';
+
+ /**
+ * Include rendered HTML prior to the layout.
+ *
+ * @var string
+ */
+ var $prefix = '';
+
+ /**
+ * Include rendered HTML after the layout.
+ *
+ * @var string
+ */
+ var $suffix = '';
+
+ /**
+ * Receive and store the display object to be rendered.
+ *
+ * This is a psuedo-constructor that should typically be called immediately
+ * after object construction.
+ *
+ * @param array $plugin
+ * The definition of the renderer plugin.
+ * @param panels_display $display
+ * The panels display object to be rendered.
+ */
+ function init($plugin, &$display) {
+ $this->plugin = $plugin;
+ $layout = panels_get_layout($display->layout);
+ $this->display = &$display;
+ $this->plugins['layout'] = $layout;
+ if (!isset($layout['panels'])) {
+ $this->plugins['layout']['panels'] = panels_get_regions($layout, $display);
+ }
+
+ if (empty($this->plugins['layout'])) {
+ watchdog('panels', "Layout: @layout couldn't been found, maybe the theme is disabled.", array('@layout' => $display->layout));
+ }
+ }
+
+ /**
+ * Prepare the attached display for rendering.
+ *
+ * This is the outermost prepare method. It calls several sub-methods as part
+ * of the overall preparation process. This compartmentalization is intended
+ * to ease the task of modifying renderer behavior in child classes.
+ *
+ * If you override this method, it is important that you either call this
+ * method via parent::prepare(), or manually set $this->prep_run = TRUE.
+ *
+ * @param mixed $external_settings
+ * An optional parameter allowing external code to pass in additional
+ * settings for use in the preparation process. Not used in the default
+ * renderer, but included for interface consistency.
+ */
+ function prepare($external_settings = NULL) {
+ $this->prepare_panes($this->display->content);
+ $this->prepare_regions($this->display->panels, $this->display->panel_settings);
+ $this->prep_run = TRUE;
+ }
+
+ /**
+ * Prepare the list of panes to be rendered, accounting for visibility/access
+ * settings and rendering order.
+ *
+ * This method represents the standard approach for determining the list of
+ * panes to be rendered that is compatible with all parts of the Panels
+ * architecture. It first applies visibility & access checks, then sorts panes
+ * into their proper rendering order, and returns the result as an array.
+ *
+ * Inheriting classes should override this method if that renderer needs to
+ * regularly make additions to the set of panes that will be rendered.
+ *
+ * @param array $panes
+ * An associative array of pane data (stdClass objects), keyed on pane id.
+ * @return array
+ * An associative array of panes to be rendered, keyed on pane id and sorted
+ * into proper rendering order.
+ */
+ function prepare_panes($panes) {
+ ctools_include('content');
+ // Use local variables as writing to them is very slightly faster
+ $normal = $last = array();
+
+ // Prepare the list of panes to be rendered
+ foreach ($panes as $pid => $pane) {
+ if (empty($this->admin)) {
+ // TODO remove in 7.x and ensure the upgrade path weeds out any stragglers; it's been long enough
+ $pane->shown = !empty($pane->shown); // guarantee this field exists.
+ // If this pane is not visible to the user, skip out and do the next one
+ if (!$pane->shown || !panels_pane_access($pane, $this->display)) {
+ continue;
+ }
+ }
+
+ $ct_plugin_def = ctools_get_content_type($pane->type);
+
+ // If this pane wants to render last, add it to the $last array. We allow
+ // this because some panes need to be rendered after other panes,
+ // primarily so they can do things like the leftovers of forms.
+ if (!empty($ct_plugin_def['render last'])) {
+ $last[$pid] = $pane;
+ }
+ // Otherwise, render it in the normal order.
+ else {
+ $normal[$pid] = $pane;
+ }
+ }
+ $this->prepared['panes'] = $normal + $last;
+ return $this->prepared['panes'];
+ }
+
+ /**
+ * Prepare the list of regions to be rendered.
+ *
+ * This method is primarily about properly initializing the style plugin that
+ * will be used to render the region. This is crucial as regions cannot be
+ * rendered without a style plugin (in keeping with Panels' philosophy of
+ * hardcoding none of its output), but for most regions no style has been
+ * explicitly set. The logic here is what accommodates that situation:
+ * - If a region has had its style explicitly set, then we fetch that plugin
+ * and continue.
+ * - If the region has no explicit style, but a style was set at the display
+ * level, then inherit the style from the display.
+ * - If neither the region nor the dispay have explicitly set styles, then
+ * fall back to the hardcoded 'default' style, a very minimal style.
+ *
+ * The other important task accomplished by this method is ensuring that even
+ * regions without any panes are still properly prepared for the rendering
+ * process. This is essential because the way Panels loads display objects
+ * (@see panels_load_displays) results only in a list of regions that
+ * contain panes - not necessarily all the regions defined by the layout
+ * plugin, which can only be determined by asking the plugin at runtime. This
+ * method consults that retrieved list of regions and prepares all of those,
+ * ensuring none are inadvertently skipped.
+ *
+ * @param array $region_pane_list
+ * An associative array of pane ids, keyed on the region to which those pids
+ * are assigned. In the default case, this is $display->panels.
+ * @param array $settings
+ * All known region style settings, including both the top-level display's
+ * settings (if any) and all region-specific settings (if any).
+ * @return array
+ * An array of regions prepared for rendering.
+ */
+ function prepare_regions($region_pane_list, $settings) {
+ // Initialize defaults to be used for regions without their own explicit
+ // settings. Use display settings if they exist, else hardcoded defaults.
+ $default = array(
+ 'style' => panels_get_style(!empty($settings['style']) ? $settings['style'] : 'default'),
+ 'style settings' => isset($settings['style_settings']['default']) ? $settings['style_settings']['default'] : array(),
+ );
+
+ $regions = array();
+ if (empty($settings)) {
+ // No display/panel region settings exist, init all with the defaults.
+ foreach ($this->plugins['layout']['panels'] as $region_id => $title) {
+ // Ensure this region has at least an empty panes array.
+ $panes = !empty($region_pane_list[$region_id]) ? $region_pane_list[$region_id] : array();
+
+ $regions[$region_id] = $default;
+ $regions[$region_id]['pids'] = $panes;
+ }
+ }
+ else {
+ // Some settings exist; iterate through each region and set individually.
+ foreach ($this->plugins['layout']['panels'] as $region_id => $title) {
+ // Ensure this region has at least an empty panes array.
+ $panes = !empty($region_pane_list[$region_id]) ? $region_pane_list[$region_id] : array();
+
+ if (empty($settings[$region_id]['style']) || $settings[$region_id]['style'] == -1) {
+ $regions[$region_id] = $default;
+ }
+ else {
+ $regions[$region_id]['style'] = panels_get_style($settings[$region_id]['style']);
+ $regions[$region_id]['style settings'] = isset($settings['style_settings'][$region_id]) ? $settings['style_settings'][$region_id] : array();
+ }
+ $regions[$region_id]['pids'] = $panes;
+ }
+ }
+
+ $this->prepared['regions'] = $regions;
+ return $this->prepared['regions'];
+ }
+
+ /**
+ * Build inner content, then hand off to layout-specified theme function for
+ * final render step.
+ *
+ * This is the outermost method in the Panels render pipeline. It calls the
+ * inner methods, which return a content array, which is in turn passed to the
+ * theme function specified in the layout plugin.
+ *
+ * @return string
+ * Themed & rendered HTML output.
+ */
+ function render() {
+ // Attach out-of-band data first.
+ $this->add_meta();
+
+ if (empty($this->display->cache['method']) || !empty($this->display->skip_cache)) {
+ return $this->render_layout();
+ }
+ else {
+ $cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context);
+ if ($cache === FALSE) {
+ $cache = new panels_cache_object();
+ $cache->set_content($this->render_layout());
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context);
+ }
+ return $cache->content;
+ }
+ }
+
+ /**
+ * Perform display/layout-level render operations.
+ *
+ * This method triggers all the inner pane/region rendering processes, passes
+ * that to the layout plugin's theme callback, and returns the rendered HTML.
+ *
+ * If display-level caching is enabled and that cache is warm, this method
+ * will not be called.
+ *
+ * @return string
+ * The HTML string representing the entire rendered, themed panel.
+ */
+ function render_layout() {
+ if (empty($this->prep_run)) {
+ $this->prepare();
+ }
+ $this->render_panes();
+ $this->render_regions();
+
+ if ($this->admin && !empty($this->plugins['layout']['admin theme'])) {
+ $theme = $this->plugins['layout']['admin theme'];
+ }
+ else {
+ $theme = $this->plugins['layout']['theme'];
+ }
+ $this->rendered['layout'] = theme($theme, check_plain($this->display->css_id), $this->rendered['regions'], $this->display->layout_settings, $this->display, $this->plugins['layout'], $this);
+ return $this->prefix . $this->rendered['layout'] . $this->suffix;
+ }
+
+ /**
+ * Attach out-of-band page metadata (e.g., CSS and JS).
+ *
+ * This must be done before render, because panels-within-panels must have
+ * their CSS added in the right order: inner content before outer content.
+ */
+ function add_meta() {
+ if (!empty($this->plugins['layout']['css'])) {
+ if (file_exists(path_to_theme() . '/' . $this->plugins['layout']['css'])) {
+ $this->add_css(path_to_theme() . '/' . $this->plugins['layout']['css']);
+ }
+ else {
+ $this->add_css($this->plugins['layout']['path'] . '/' . $this->plugins['layout']['css']);
+ }
+ }
+
+ if ($this->admin && isset($this->plugins['layout']['admin css'])) {
+ $this->add_css($this->plugins['layout']['path'] . '/' . $this->plugins['layout']['admin css']);
+ }
+ }
+
+ function add_css($filename, $type = 'module', $media = 'all', $preprocess = TRUE) {
+ $path = file_create_path($filename);
+ switch ($this->meta_location) {
+ case 'standard':
+ if ($path) {
+ // Use CTools CSS add because it can handle temporary CSS in private
+ // filesystem.
+ ctools_include('css');
+ ctools_css_add_css($filename, $type, $media, $preprocess);
+ }
+ else {
+ drupal_add_css($filename, $type, $media, $preprocess);
+ }
+ break;
+ case 'inline':
+ if ($path) {
+ $url = file_create_url($filename);
+ }
+ else {
+ $url = base_path() . $filename;
+ }
+
+ $this->prefix .= '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . $url . '" />'."\n";
+ break;
+ }
+ }
+
+ /**
+ * Render all prepared panes, first by dispatching to their plugin's render
+ * callback, then handing that output off to the pane's style plugin.
+ *
+ * @return array
+ * The array of rendered panes, keyed on pane pid.
+ */
+ function render_panes() {
+ ctools_include('content');
+
+ // First, render all the panes into little boxes.
+ $this->rendered['panes'] = array();
+ foreach ($this->prepared['panes'] as $pid => $pane) {
+ $content = $this->render_pane($pane);
+ if ($content) {
+ $this->rendered['panes'][$pid] = $content;
+ }
+ }
+ return $this->rendered['panes'];
+ }
+
+ /**
+ * Render a pane using its designated style.
+ *
+ * This method also manages 'title pane' functionality, where the title from
+ * an individual pane can be bubbled up to take over the title for the entire
+ * display.
+ *
+ * @param stdClass $pane
+ * A Panels pane object, as loaded from the database.
+ */
+ function render_pane(&$pane) {
+ $content = $this->render_pane_content($pane);
+ if ($this->display->hide_title == PANELS_TITLE_PANE && !empty($this->display->title_pane) && $this->display->title_pane == $pane->pid) {
+
+ // If the user selected to override the title with nothing, and selected
+ // this as the title pane, assume the user actually wanted the original
+ // title to bubble up to the top but not actually be used on the pane.
+ if (empty($content->title) && !empty($content->original_title)) {
+ $this->display->stored_pane_title = $content->original_title;
+ }
+ else {
+ $this->display->stored_pane_title = !empty($content->title) ? $content->title : '';
+ }
+ }
+
+ if (!empty($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, $this->display, $style);
+
+ // This could be null if no theme function existed.
+ if (isset($output)) {
+ return $output;
+ }
+ }
+ }
+
+ // fallback
+ return theme('panels_pane', $content, $pane, $this->display);
+ }
+ }
+
+ /**
+ * Render the interior contents of a single pane.
+ *
+ * This method retrieves pane content and produces a ready-to-render content
+ * object. It also manages pane-specific caching.
+ *
+ * @param stdClass $pane
+ * A Panels pane object, as loaded from the database.
+ * @return stdClass $content
+ * A renderable object, containing a subject, content, etc. Based on the
+ * renderable objects used by the block system.
+ */
+ function render_pane_content(&$pane) {
+ ctools_include('context');
+ // TODO finally safe to remove this check?
+ if (!is_array($this->display->context)) {
+ watchdog('panels', 'renderer::render_pane_content() hit with a non-array for the context', $this->display, WATCHDOG_DEBUG);
+ $this->display->context = array();
+ }
+
+ $content = FALSE;
+ $caching = !empty($pane->cache['method']) && empty($this->display->skip_cache);
+ if ($caching && ($cache = panels_get_cached_content($this->display, $this->display->args, $this->display->context, $pane))) {
+ $content = $cache->content;
+ }
+ else {
+ $content = ctools_content_render($pane->type, $pane->subtype, $pane->configuration, array(), $this->display->args, $this->display->context);
+ foreach (module_implements('panels_pane_content_alter') as $module) {
+ $function = $module . '_panels_pane_content_alter';
+ $function($content, $pane, $this->display->args, $this->display->context);
+ }
+ if ($caching) {
+ $cache = new panels_cache_object();
+ $cache->set_content($content);
+ panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $pane);
+ $content = $cache->content;
+ }
+ }
+
+ // Pass long the css_id that is usually available.
+ if (!empty($pane->css['css_id'])) {
+ $content->css_id = $pane->css['css_id'];
+ }
+
+ // Pass long the css_class that is usually available.
+ if (!empty($pane->css['css_class'])) {
+ $content->css_class = $pane->css['css_class'];
+ }
+
+ return $content;
+ }
+
+ /**
+ * Render all prepared regions, placing already-rendered panes into their
+ * appropriate positions therein.
+ *
+ * @return array
+ * An array of rendered panel regions, keyed on the region name.
+ */
+ function render_regions() {
+ $this->rendered['regions'] = array();
+
+ // Loop through all panel regions, put all panes that belong to the current
+ // region in an array, then render the region. Primarily this ensures that
+ // the panes are arranged in the proper order.
+ $content = array();
+ foreach ($this->prepared['regions'] as $region_id => $conf) {
+ $region_panes = array();
+ foreach ($conf['pids'] as $pid) {
+ // Only include panes for region rendering if they had some output.
+ if (!empty($this->rendered['panes'][$pid])) {
+ $region_panes[$pid] = $this->rendered['panes'][$pid];
+ }
+ }
+ $this->rendered['regions'][$region_id] = $this->render_region($region_id, $region_panes);
+ }
+
+ return $this->rendered['regions'];
+ }
+
+ /**
+ * Render a single panel region.
+ *
+ * Primarily just a passthrough to the panel region rendering callback
+ * specified by the style plugin that is attached to the current panel region.
+ *
+ * @param $region_id
+ * The ID of the panel region being rendered
+ * @param $panes
+ * An array of panes that are assigned to the panel that's being rendered.
+ *
+ * @return string
+ * The rendered, HTML string output of the passed-in panel region.
+ */
+ function render_region($region_id, $panes) {
+ $style = $this->prepared['regions'][$region_id]['style'];
+ $style_settings = $this->prepared['regions'][$region_id]['style settings'];
+
+ // Retrieve the pid (can be a panel page id, a mini panel id, etc.), this
+ // might be used (or even necessary) for some panel display styles.
+ // TODO: Got to fix this to use panel page name instead of pid, since pid is
+ // no longer guaranteed. This needs an API to be able to set the final id.
+ $owner_id = 0;
+ if (isset($this->display->owner) && is_object($this->display->owner) && isset($this->display->owner->id)) {
+ $owner_id = $this->display->owner->id;
+ }
+
+ return theme($style['render region'], $this->display, $owner_id, $panes, $style_settings, $region_id, $style);
+ }
+}
diff --git a/plugins/display_renderers/simple.inc b/plugins/display_renderers/simple.inc
new file mode 100644
index 0000000..725c28d
--- /dev/null
+++ b/plugins/display_renderers/simple.inc
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Create a simple renderer plugin that renders a layout but the content is
+ * already rendered, not in panes.
+ */
+$plugin = array(
+ 'handler' => array(
+ 'class' => 'panels_renderer_simple',
+ 'parent' => 'standard',
+ ),
+); \ No newline at end of file
diff --git a/plugins/display_renderers/single_pane.inc b/plugins/display_renderers/single_pane.inc
new file mode 100644
index 0000000..67ef61d
--- /dev/null
+++ b/plugins/display_renderers/single_pane.inc
@@ -0,0 +1,8 @@
+<?php
+
+$plugin = array(
+ 'handler' => array(
+ 'class' => 'panels_renderer_single_pane',
+ 'parent' => 'standard',
+ ),
+); \ No newline at end of file
diff --git a/plugins/display_renderers/standard.inc b/plugins/display_renderers/standard.inc
new file mode 100644
index 0000000..39b235d
--- /dev/null
+++ b/plugins/display_renderers/standard.inc
@@ -0,0 +1,7 @@
+<?php
+
+$plugin = array(
+ 'handler' => array(
+ 'class' => 'panels_renderer_standard',
+ ),
+); \ No newline at end of file
diff --git a/plugins/export_ui/panels_layouts.inc b/plugins/export_ui/panels_layouts.inc
new file mode 100644
index 0000000..0a05efe
--- /dev/null
+++ b/plugins/export_ui/panels_layouts.inc
@@ -0,0 +1,25 @@
+<?php
+// $Id$
+
+$plugin = array(
+ 'schema' => 'panels_layout',
+ 'access' => 'administer panels layouts',
+
+ 'menu' => array(
+ 'menu prefix' => 'admin/build/panels',
+ 'menu item' => 'layouts',
+ 'menu title' => 'Layouts',
+ 'menu description' => 'Add, edit or delete custom content layouts.',
+ ),
+
+ 'title singular' => t('layout'),
+ 'title singular proper' => t('Layout'),
+ 'title plural' => t('layouts'),
+ 'title plural proper' => t('Layouts'),
+
+ 'handler' => array(
+ 'class' => 'panels_layouts_ui',
+ 'parent' => 'ctools_export_ui',
+ ),
+);
+
diff --git a/plugins/export_ui/panels_layouts_ui.class.php b/plugins/export_ui/panels_layouts_ui.class.php
new file mode 100644
index 0000000..b8f5788
--- /dev/null
+++ b/plugins/export_ui/panels_layouts_ui.class.php
@@ -0,0 +1,231 @@
+<?php
+// $Id$
+
+class panels_layouts_ui extends ctools_export_ui {
+ var $lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam egestas congue nibh, vel dictum ante posuere vitae. Cras gravida massa tempor metus eleifend sed elementum tortor scelerisque. Vivamus egestas, tortor quis luctus tristique, sem velit adipiscing risus, et tempus enim felis in massa. Morbi viverra, nisl quis rhoncus imperdiet, turpis massa vestibulum turpis, egestas faucibus nibh metus vel nunc. In hac habitasse platea dictumst. Nunc sit amet nisi quis ipsum tincidunt semper. Donec ac urna enim, et placerat arcu. Morbi eu laoreet justo. Nullam nec velit eu neque mattis pulvinar sed non libero. Sed sed vulputate erat. Fusce sit amet dui nibh.";
+
+ function hook_menu(&$items) {
+ // During updates, this can run before our schema is set up, so our
+ // plugin can be empty.
+ if (empty($this->plugin['menu']['items']['add'])) {
+ return;
+ }
+
+ // Change the item to a tab on the Panels page.
+ $this->plugin['menu']['items']['list callback']['type'] = MENU_LOCAL_TASK;
+
+ // Establish a base for adding plugins
+ $base = $this->plugin['menu']['items']['add'];
+ // Remove the default 'add' menu item.
+ unset($this->plugin['menu']['items']['add']);
+
+ ctools_include('plugins', 'panels');
+ $this->builders = panels_get_layout_builders();
+ asort($this->builders);
+ foreach ($this->builders as $name => $builder) {
+ // Create a new menu item for the builder
+ $item = $base;
+ $item['title'] = !empty($builder['builder tab title']) ? $builder['builder tab title'] : 'Add ' . $builder['title'];
+ $item['page arguments'][] = $name;
+ $item['path'] = 'add-' . $name;
+ $this->plugin['menu']['items']['add ' . $name] = $item;
+ }
+
+ parent::hook_menu($items);
+ }
+
+ function edit_form(&$form, &$form_state) {
+ ctools_include('plugins', 'panels');
+ // If the plugin is not set, then it should be provided as an argument:
+ if (!isset($form_state['item']->plugin)) {
+ $form_state['item']->plugin = $form_state['function args'][2];
+ }
+
+ parent::edit_form($form, $form_state);
+
+ $form['category'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Category'),
+ '#description' => t('What category this layout should appear in. If left blank the category will be "Miscellaneous".'),
+ '#default_value' => $form_state['item']->category,
+ );
+
+ ctools_include('context');
+ ctools_include('display-edit', 'panels');
+ ctools_include('content');
+
+ // Provide actual layout admin UI here.
+ // Create a display for editing:
+ $cache_key = 'builder-' . $form_state['item']->name;
+
+ // Load the display being edited from cache, if possible.
+ if (!empty($_POST) && is_object($cache = panels_edit_cache_get($cache_key))) {
+ $display = &$cache->display;
+ }
+ else {
+ $content_types = ctools_content_get_available_types();
+
+ $display->cache_key = $cache_key;
+ panels_cache_clear('display', $cache_key);
+ $cache = new stdClass();
+
+ $display = panels_new_display();
+ $display->did = $form_state['item']->name;
+ $display->layout = $form_state['item']->plugin;
+ $display->layout_settings = $form_state['item']->settings;
+ $display->cache_key = $cache_key;
+ $display->editing_layout = TRUE;
+
+ $cache->display = $display;
+ $cache->content_types = $content_types;
+ $cache->display_title = FALSE;
+ panels_edit_cache_set($cache);
+ }
+
+ // Set up lipsum content in all of the existing panel regions:
+ $display->content = array();
+ $display->panels = array();
+ $custom = ctools_get_content_type('custom');
+ $layout = panels_get_layout($display->layout);
+
+ $regions = panels_get_regions($layout, $display);
+ foreach ($regions as $id => $title) {
+ $pane = panels_new_pane('custom', 'custom');
+ $pane->pid = $id;
+ $pane->panel = $id;
+ $pane->configuration = ctools_content_get_defaults($custom, 'custom');
+ $pane->configuration['title'] = 'Lorem Ipsum';
+ $pane->configuration['body'] = $this->lipsum;
+ $display->content[$id] = $pane;
+ $display->panels[$id] = array($id);
+ }
+
+ $form_state['display'] = &$display;
+ // Tell the Panels form not to display buttons.
+ $form_state['no buttons'] = TRUE;
+ $form_state['no display settings'] = TRUE;
+
+ $form_state['cache_key'] = $cache_key;
+ $form_state['content_types'] = $cache->content_types;
+ $form_state['display_title'] = FALSE;
+
+ $form_state['renderer'] = panels_get_renderer_handler('editor', $cache->display);
+ $form_state['renderer']->cache = &$cache;
+
+ $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';
+
+ // If we leave the standard submit handler, it'll try to reconcile
+ // content from the input, but we've not exposed that to the user. This
+ // makes previews work with the content we forced in.
+ $form['preview']['button']['#submit'] = array('panels_edit_display_form_preview');
+ }
+
+ function edit_form_submit(&$form, &$form_state) {
+ parent::edit_form_submit($form, $form_state);
+ $form_state['item']->settings = $form_state['display']->layout_settings;
+ }
+
+ function list_form(&$form, &$form_state) {
+ ctools_include('plugins', 'panels');
+ $this->builders = panels_get_layout_builders();
+ parent::list_form($form, $form_state);
+
+ $categories = $plugins = array('all' => t('- All -'));
+ foreach ($this->items as $item) {
+ $categories[$item->category] = $item->category ? $item->category : t('Miscellaneous');
+ }
+
+ $form['top row']['category'] = array(
+ '#type' => 'select',
+ '#title' => t('Category'),
+ '#options' => $categories,
+ '#default_value' => 'all',
+ '#weight' => -10,
+ );
+
+ foreach ($this->builders as $name => $plugin) {
+ $plugins[$name] = $plugin['title'];
+ }
+
+ $form['top row']['plugin'] = array(
+ '#type' => 'select',
+ '#title' => t('Type'),
+ '#options' => $plugins,
+ '#default_value' => 'all',
+ '#weight' => -9,
+ );
+ }
+
+ function list_filter($form_state, $item) {
+ if ($form_state['values']['category'] != 'all' && $form_state['values']['category'] != $item->category) {
+ return TRUE;
+ }
+
+ if ($form_state['values']['plugin'] != 'all' && $form_state['values']['plugin'] != $item->plugin) {
+ return TRUE;
+ }
+
+ return parent::list_filter($form_state, $item);
+ }
+
+ function list_sort_options() {
+ return array(
+ 'disabled' => t('Enabled, title'),
+ 'title' => t('Title'),
+ 'name' => t('Name'),
+ 'category' => t('Category'),
+ 'storage' => t('Storage'),
+ 'plugin' => t('Type'),
+ );
+ }
+
+ function list_build_row($item, &$form_state, $operations) {
+ // Set up sorting
+ switch ($form_state['values']['order']) {
+ case 'disabled':
+ $this->sorts[$item->name] = empty($item->disabled) . $item->admin_title;
+ break;
+ case 'title':
+ $this->sorts[$item->name] = $item->admin_title;
+ break;
+ case 'name':
+ $this->sorts[$item->name] = $item->name;
+ break;
+ case 'category':
+ $this->sorts[$item->name] = ($item->category ? $item->category : t('Miscellaneous')) . $item->admin_title;
+ break;
+ case 'plugin':
+ $this->sorts[$item->name] = $item->plugin;
+ break;
+ case 'storage':
+ $this->sorts[$item->name] = $item->type . $item->admin_title;
+ break;
+ }
+
+ $type = !empty($this->builders[$item->plugin]) ? $this->builders[$item->plugin]['title'] : t('Broken/missing plugin');
+ $category = $item->category ? check_plain($item->category) : t('Miscellaneous');
+ $this->rows[$item->name] = array(
+ 'data' => array(
+ array('data' => check_plain($type), 'class' => 'ctools-export-ui-type'),
+ array('data' => check_plain($item->name), 'class' => 'ctools-export-ui-name'),
+ array('data' => check_plain($item->admin_title), 'class' => 'ctools-export-ui-title'),
+ array('data' => $category, 'class' => 'ctools-export-ui-category'),
+ array('data' => theme('links', $operations), 'class' => 'ctools-export-ui-operations'),
+ ),
+ 'title' => check_plain($item->admin_description),
+ 'class' => !empty($item->disabled) ? 'ctools-export-ui-disabled' : 'ctools-export-ui-enabled',
+ );
+ }
+
+ function list_table_header() {
+ return array(
+ array('data' => t('Type'), 'class' => 'ctools-export-ui-type'),
+ array('data' => t('Name'), 'class' => 'ctools-export-ui-name'),
+ array('data' => t('Title'), 'class' => 'ctools-export-ui-title'),
+ array('data' => t('Category'), 'class' => 'ctools-export-ui-category'),
+ array('data' => t('Operations'), 'class' => 'ctools-export-ui-operations'),
+ );
+ }
+}
diff --git a/plugins/layouts/flexible/flexible-admin.css b/plugins/layouts/flexible/flexible-admin.css
index 97058c2..d0842a0 100644
--- a/plugins/layouts/flexible/flexible-admin.css
+++ b/plugins/layouts/flexible/flexible-admin.css
@@ -10,7 +10,7 @@
}
.panel-flexible-edit-layout .panels-flexible-column-inside {
- margin: 5px;
+/* margin: 5px; */
border: 1px dotted green;
}
@@ -73,6 +73,7 @@
position: absolute;
z-index: 1000;
background: white;
+ color: black;
border: 1px solid black;
width: 60px;
height: 2em;
@@ -80,4 +81,8 @@
line-height: 2em;
}
-} \ No newline at end of file
+#panels-edit-display .panel-pane,
+#panels-edit-display .helperclass {
+ margin: .5em;
+}
+
diff --git a/plugins/layouts/flexible/flexible-admin.js b/plugins/layouts/flexible/flexible-admin.js
index 5cae831..d3c4808 100644
--- a/plugins/layouts/flexible/flexible-admin.js
+++ b/plugins/layouts/flexible/flexible-admin.js
@@ -49,6 +49,8 @@ Drupal.behaviors.flexibleAdmin = function(context) {
Drupal.flexible.splitters.push(new Drupal.flexible.splitter($(this)));
});
}
+
+ Drupal.flexible.fixHeight();
};
Drupal.flexible.splitter = function($splitter) {
@@ -404,3 +406,4 @@ Drupal.CTools.AJAX.commands.flexible_fix_firstlast = function(data) {
$(data.selector + ' > div > .' + data.base + ':last')
.addClass(data.base + '-last');
};
+
diff --git a/plugins/layouts/flexible/flexible.css b/plugins/layouts/flexible/flexible.css
index 3543aa9..2050235 100644
--- a/plugins/layouts/flexible/flexible.css
+++ b/plugins/layouts/flexible/flexible.css
@@ -1,64 +1,5 @@
/* $Id$ */
-.panel-flexible {
-/* overflow: hidden; */
-}
-
-.panel-flexible {
- width: 99.9%;
-}
-
-#panels-edit-display .panel-pane,
-#panels-edit-display .helperclass {
- margin: .5em;
-}
-
-.panel-flexible .panel-separator {
- margin: 0 0 1em 0;
-}
-
-/* ---- */
-
-.panel-flexible {
- width: 99.9%;
-}
-
-.panels-flexible-region {
- float: left;
- padding: 0;
- /* margin: 1px 0 .5em 0; */
-/* overflow: hidden; */
-}
-
-.panels-flexible-row {
- padding: 0;
- margin: 0;
- padding-bottom: .5em;
- width: 99.9%;
-}
-
-.panels-flexible-row-last {
- padding-bottom: 0;
-}
-
-.panels-flexible-region-inside {
- padding-right: .5em;
- padding-left: .5em;
-}
-
-.panesl-flexible-region-inside-first {
- padding-left: 0;
-}
-
-.panels-flexible-region-inside-last {
- padding-right: 0;
-}
-
-#panels-edit-display .panel-pane,
-#panels-edit-display .helperclass {
- margin: .5em;
-}
-
.panel-flexible .panel-separator {
margin: 0 0 1em 0;
}
diff --git a/plugins/layouts/flexible/flexible.inc b/plugins/layouts/flexible/flexible.inc
index 83c9eeb..9fb53cb 100644
--- a/plugins/layouts/flexible/flexible.inc
+++ b/plugins/layouts/flexible/flexible.inc
@@ -7,6 +7,7 @@
// Plugin definition
$plugin = array(
'title' => t('Flexible'),
+ 'category' => t('Builders'),
'icon' => 'flexible.png',
'theme' => 'panels_flexible',
'admin theme' => 'panels_flexible_admin',
@@ -17,32 +18,65 @@ $plugin = array(
'settings validate' => 'panels_flexible_settings_validate',
'panels function' => 'panels_flexible_panels',
'hook menu' => 'panels_flexible_menu',
+
+ // Reuisable layout Builder specific directives
+ 'builder' => TRUE,
+ 'builder tab title' => 'Add flexible layout', // menu so translated elsewhere
+
+ 'get child' => 'panels_flexible_get_sublayout',
+ 'get children' => 'panels_flexible_get_sublayouts',
+
+ // Define ajax callbacks
+ 'ajax' => array(
+ 'settings' => 'panels_ajax_flexible_edit_settings',
+ 'add' => 'panels_ajax_flexible_edit_add',
+ 'remove' => 'panels_ajax_flexible_edit_remove',
+ 'resize' => 'panels_ajax_flexible_edit_resize',
+ 'reuse' => 'panels_ajax_flexible_edit_reuse',
+ ),
);
/**
- * Delegated implementation of hook_menu().
+ * Merge the main flexible plugin with a layout to create a sub plugin.
+ *
+ * This is used for both panels_flexible_get_sublayout and
+ * panels_flexible_get_sublayouts.
*/
-function panels_flexible_menu(&$items, &$info) {
- $base = array(
- 'access arguments' => array('access content'),
- 'page arguments' => array(4),
- 'type' => MENU_CALLBACK,
- 'file' => $info['file'],
- 'file path' => $info['path'],
- );
+function panels_flexible_merge_plugin($plugin, $layout) {
+ $plugin['name'] = 'flexible:' . $layout->name;
+ $plugin['category'] = !empty($layout->category) ? check_plain($layout->category) : t('Miscellaneous');
+ $plugin['title'] = check_plain($layout->admin_title);
+ $plugin['description'] = check_plain($layout->admin_description);
+ $plugin['layout'] = $layout;
+ $plugin['builder'] = FALSE;
+ $plugin['builder tab title'] = NULL;
+ return $plugin;
+}
+
+/**
+ * Callback to provide a single stored flexible layout.
+ */
+function panels_flexible_get_sublayout($plugin, $layout_name, $sublayout_name) {
+ // Do not worry about caching; Panels is handling that for us.
+ ctools_include('export');
+ $item = ctools_export_crud_load('panels_layout', $sublayout_name);
+ if ($item) {
+ return panels_flexible_merge_plugin($plugin, $item);
+ }
+}
+
+/**
+ * Callback to provide all stored flexible layouts.
+ */
+function panels_flexible_get_sublayouts($plugin, $layout_name) {
+ $layouts[$layout_name] = $plugin;
+ ctools_include('export');
+ $items = ctools_export_load_object('panels_layout', 'conditions', array('plugin' => 'flexible'));
+ foreach ($items as $name => $item) {
+ $layouts['flexible:' . $name] = panels_flexible_merge_plugin($plugin, $item);
+ }
- $items['panels/ajax/flexible/settings/%panels_edit_cache'] = array(
- 'page callback' => 'panels_ajax_flexible_edit_settings',
- ) + $base;
- $items['panels/ajax/flexible/add/%panels_edit_cache'] = array(
- 'page callback' => 'panels_ajax_flexible_edit_add',
- ) + $base;
- $items['panels/ajax/flexible/remove/%panels_edit_cache'] = array(
- 'page callback' => 'panels_ajax_flexible_edit_remove',
- ) + $base;
- $items['panels/ajax/flexible/resize/%panels_edit_cache'] = array(
- 'page callback' => 'panels_ajax_flexible_edit_resize',
- ) + $base;
+ return $layouts;
}
/**
@@ -50,7 +84,19 @@ function panels_flexible_menu(&$items, &$info) {
* empty settings.
* @param <type> $settings
*/
-function panels_flexible_convert_settings(&$settings) {
+function panels_flexible_convert_settings(&$settings, &$layout) {
+ // This indicates that this is a layout that they used the checkbox
+ // on. The layout is still 'flexible' but it's actually pointing
+ // to another stored one and we have to load it.
+ if (!empty($settings['layout'])) {
+ $layout = panels_get_layout('flexible:' . $settings['layout']);
+ }
+
+ if (!empty($layout['layout'])) {
+ $settings = $layout['layout']->settings;
+ return $settings;
+ }
+
if (empty($settings)) {
// set up a default
$settings = array(
@@ -204,9 +250,9 @@ function panels_flexible_convert_settings(&$settings) {
/**
* Define the actual list of columns and rows for this flexible panel.
*/
-function panels_flexible_panels($display, $settings) {
+function panels_flexible_panels($display, $settings, $layout) {
$items = array();
- panels_flexible_convert_settings($settings);
+ panels_flexible_convert_settings($settings, $layout);
foreach ($settings['items'] as $id => $item) {
if ($item['type'] == 'region') {
$items[$id] = $item['title'];
@@ -217,44 +263,124 @@ function panels_flexible_panels($display, $settings) {
}
/**
- * Draw the flexible layout.
+ * Create a renderer object.
+ *
+ * The renderer object contains data that is passed around from function
+ * to function allowing us to render our CSS and HTML easily.
+ *
+ * @todo Convert the functions to methods and make this properly OO.
*/
-function theme_panels_flexible($id, $content, $settings, $display) {
- panels_flexible_convert_settings($settings);
-
+function panels_flexible_create_renderer($admin, $id, $content, $settings, &$display, $layout, $handler) {
$renderer = new stdClass;
$renderer->settings = $settings;
$renderer->content = $content;
$renderer->css_id = $id;
- $renderer->did = $display->did;
+ $renderer->did = &$display->did;
+ if ($admin) {
+ // always scale in admin mode.
+ $renderer->scale_base = 99.0;
+ }
+ else {
+ $renderer->scale_base = !empty($settings['items']['canvas']['no_scale']) ? 100.0 : 99.0;
+ }
$renderer->id_str = $id ? 'id="' . $id . '"' : '';
- $renderer->admin = FALSE;
+ $renderer->admin = $admin;
+ $renderer->handler = $handler;
+
+ // Set up basic classes for all of our components.
+ $renderer->name = !empty($layout['layout']) ? $layout['layout']->name : $display->did;
+ $renderer->base_class = $renderer->name;
+ $renderer->item_class['column'] = 'panels-flexible-column';
+ $renderer->item_class['row'] = 'panels-flexible-row';
+ $renderer->item_class['region'] = 'panels-flexible-region';
+ $renderer->base['canvas'] = 'panels-flexible-' . $renderer->base_class;
+
+ // Override these if selected from the UI and not in admin mode.
+ if (!$admin) {
+ if (!empty($settings['items']['canvas']['class'])) {
+ $renderer->base_class = $settings['items']['canvas']['class'];
+ $renderer->base['canvas'] = $renderer->base_class;
+ }
+ if (!empty($settings['items']['canvas']['column_class'])) {
+ $renderer->item_class['column'] = $settings['items']['canvas']['column_class'];
+ }
+ if (!empty($settings['items']['canvas']['row_class'])) {
+ $renderer->item_class['row'] = $settings['items']['canvas']['row_class'];
+ }
+ if (!empty($settings['items']['canvas']['region_class'])) {
+ $renderer->item_class['region'] = $settings['items']['canvas']['region_class'];
+ }
+ }
+
+ // Get the separation values out of the canvas settings.
+ $renderer->column_separation = !empty($settings['items']['canvas']['column_separation']) ? $settings['items']['canvas']['column_separation'] : '0.5em';
+
+ $renderer->region_separation = !empty($settings['items']['canvas']['region_separation']) ? $settings['items']['canvas']['region_separation'] : '0.5em';
+
+ $renderer->row_separation = !empty($settings['items']['canvas']['row_separation']) ? $settings['items']['canvas']['row_separation'] : '0.5em';
+
+ // Make some appended classes so it's easier to reference them.
+
+ $renderer->base['column'] = $renderer->item_class['column'] . '-' . $renderer->base_class;
+ $renderer->base['row'] = $renderer->item_class['row'] . '-' . $renderer->base_class;
+ $renderer->base['region'] = $renderer->item_class['region'] . '-' . $renderer->base_class;
+
+ if ($renderer->name != 'new') {
+ // Use v2 to guarantee all CSS gets regenerated to account for changes in
+ // how some divs will be rendered.
+ $renderer->css_cache_name = 'flexiblev2:' . $renderer->name;
+ if ($admin) {
+ ctools_include('css');
+ ctools_css_clear($renderer->css_cache_name);
+ }
+ }
+ return $renderer;
+}
+
+/**
+ * Draw the flexible layout.
+ */
+function theme_panels_flexible($id, $content, $settings, $display, $layout, $handler) {
+ panels_flexible_convert_settings($settings, $layout);
+
+ $renderer = panels_flexible_create_renderer(FALSE, $id, $content, $settings, $display, $layout, $handler);
// CSS must be generated because it reports back left/middle/right
// positions.
$css = panels_flexible_render_css($renderer);
- if ($display->did && $display->did != 'new') {
+ if (!empty($renderer->css_cache_name) && empty($display->editing_layout)) {
ctools_include('css');
// Generate an id based upon rows + columns:
- $css_id = 'flexible:' . $display->did;
- $filename = ctools_css_retrieve($css_id);
+ $filename = ctools_css_retrieve($renderer->css_cache_name);
if (!$filename) {
- $filename = ctools_css_store($css_id, $css, FALSE);
+ $filename = ctools_css_store($renderer->css_cache_name, $css, FALSE);
+ }
+
+ // Give the CSS to the renderer to put where it wants.
+ if ($handler) {
+ $handler->add_css($filename, 'module', 'all', FALSE);
+ }
+ else {
+ ctools_css_add_css($filename, 'module', 'all', FALSE);
}
- drupal_add_css($filename, 'module', 'all', FALSE);
}
else {
// If the id is 'new' we can't reliably cache the CSS in the filesystem
// because the display does not truly exist, so we'll stick it in the
- // head tag.
+ // head tag. We also do this if we've been told we're in the layout
+ // editor so that it always gets fresh CSS.
drupal_set_html_head("<style type=\"text/css\">\n$css</style>\n");
}
- $output = "<div class=\"panel-flexible panel-flexible-$renderer->did clear-block\" $renderer->id_str>\n";
- $output .= "<div class=\"panel-flexible-inside panel-flexible-$renderer->did-inside\">\n";
+ // Also store the CSS on the display in case the live preview or something
+ // needs it
+ $display->add_css = $css;
+
+ $output = "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clear-block\" $renderer->id_str>\n";
+ $output .= "<div class=\"panel-flexible-inside " . $renderer->base['canvas'] . "-inside\">\n";
- $output .= panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], 'panel-flexible-' . $renderer->did);
+ $output .= panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], $renderer->base['canvas']);
// Wrap the whole thing up nice and snug
$output .= "</div>\n</div>\n";
@@ -265,44 +391,44 @@ function theme_panels_flexible($id, $content, $settings, $display) {
/**
* Draw the flexible layout.
*/
-function theme_panels_flexible_admin($id, $content, $settings, $display) {
- panels_flexible_convert_settings($settings);
+function theme_panels_flexible_admin($id, $content, $settings, $display, $layout, $handler) {
+ // We never draw stored flexible layouts in admin mode; they must be edited
+ // from the stored layout UI at that point.
+ if (!empty($layout['layout'])) {
+ return theme_panels_flexible($id, $content, $settings, $display, $layout, $handler);
+ }
- $renderer = new stdClass;
- $renderer->settings = $settings;
- $renderer->content = $content;
- $renderer->css_id = $id;
- $renderer->did = $display->did;
- $renderer->cache_key = $display->cache_key;
- $renderer->id_str = $id ? 'id="' . $id . '"' : '';
- $renderer->admin = TRUE;
+ panels_flexible_convert_settings($settings, $layout);
+ $renderer = panels_flexible_create_renderer(TRUE, $id, $content, $settings, $display, $layout, $handler);
- if ($display->did && $display->did != 'new') {
- // Automatically remove any cached CSS for this display since it's
- // being edited. The next time it's viewed CSS will be auto generated
- // if necessary.
- $css_id = 'flexible:' . $display->did;
- ctools_include('css');
- ctools_css_clear($css_id);
- }
$css = panels_flexible_render_css($renderer);
// For the administrative view, add CSS directly to head.
drupal_set_html_head("<style type=\"text/css\">\n$css</style>\n");
- $output = '<input type="submit" id="panels-flexible-toggle-layout" value ="' .
- t('Show layout designer') . '">';
- $output .= "<div class=\"panel-flexible panel-flexible-$renderer->did clear-block panel-flexible-admin panel-flexible-no-edit-layout\" $renderer->id_str>\n";
- $output .= "<div class=\"panel-flexible-inside panel-flexible-$renderer->did-inside \">\n";
+ if (empty($display->editing_layout)) {
+ $output = '<input type="submit" id="panels-flexible-toggle-layout" value ="' .
+ t('Show layout designer') . '">';
+ if (user_access('administer panels layouts')) {
+ $output .= '<input type="hidden" class="panels-flexible-reuse-layout-url" value="' . url($handler->get_url('layout', 'reuse'), array('absolute' => TRUE)) . '">';
+ $output .= '<input type="submit" id="panels-flexible-reuse-layout" class="ctools-use-modal" value ="' .
+ t('Reuse layout') . '">';
+ }
+ $output .= "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clear-block panel-flexible-admin panel-flexible-no-edit-layout\" $renderer->id_str>\n";
+ }
+ else {
+ $output = "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clear-block panel-flexible-admin panel-flexible-edit-layout\" $renderer->id_str>\n";
+ }
+ $output .= "<div class=\"panel-flexible-inside " . $renderer->base['canvas'] . "-inside \">\n";
- $content = panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], 'panels-flexible-row-' . $renderer->did . '-canvas');
- $output .= panels_flexible_render_item($renderer, $settings['items']['canvas'], 'panels-flexible-row', $content, 'canvas', 0, 0, TRUE);
+ $content = panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], $renderer->base['row'] . '-canvas');
+ $output .= panels_flexible_render_item($renderer, $settings['items']['canvas'], $content, 'canvas', 0, 0, TRUE);
// Wrap the whole thing up nice and snug
$output .= "</div>\n</div>\n";
- drupal_add_js(panels_get_path('plugins/layouts/flexible/flexible-admin.js'));
- drupal_add_js(array('flexible' => array('resize' => url('panels/ajax/flexible/resize/' . $display->cache_key, array('absolute' => TRUE)))), 'setting');
+ drupal_add_js($layout['path'] . '/flexible-admin.js');
+ drupal_add_js(array('flexible' => array('resize' => url($handler->get_url('layout', 'resize'), array('absolute' => TRUE)))), 'setting');
return $output;
}
@@ -325,16 +451,16 @@ function panels_flexible_render_items($renderer, $list, $owner_id) {
switch ($item['type']) {
case 'column':
- $content = panels_flexible_render_items($renderer, $item['children'], 'panels-flexible-column-' . $renderer->did . '-' . $id);
- $groups[$location] .= panels_flexible_render_item($renderer, $item, 'panels-flexible-column', $content, $id, $position, $max);
+ $content = panels_flexible_render_items($renderer, $item['children'], $renderer->base['column'] . '-' . $id);
+ $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max);
break;
case 'row':
- $content = panels_flexible_render_items($renderer, $item['children'], 'panels-flexible-row-' . $renderer->did . '-' . $id);
- $groups[$location] .= panels_flexible_render_item($renderer, $item, 'panels-flexible-row', $content, $id, $position, $max, TRUE);
+ $content = panels_flexible_render_items($renderer, $item['children'], $renderer->base['row'] . '-' . $id);
+ $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max, TRUE);
break;
case 'region':
$content = isset($renderer->content[$id]) ? $renderer->content[$id] : "&nbsp;";
- $groups[$location] .= panels_flexible_render_item($renderer, $item, 'panels-flexible-region', $content, $id, $position, $max);
+ $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max);
break;
}
@@ -346,9 +472,18 @@ function panels_flexible_render_items($renderer, $list, $owner_id) {
$prev = $id;
}
+ $group_count = count(array_filter($groups));
+
+ // Render each group. We only render the group div if we're in admin mode
+ // or if there are multiple groups.
foreach ($groups as $position => $content) {
if (!empty($content) || $renderer->admin) {
- $output .= '<div class="' . $owner_id . '-' . $position . '">' . $content . '</div>';
+ if ($group_count > 1 || $renderer->admin) {
+ $output .= '<div class="' . $owner_id . '-' . $position . '">' . $content . '</div>';
+ }
+ else {
+ $output .= $content;
+ }
}
}
@@ -358,8 +493,32 @@ function panels_flexible_render_items($renderer, $list, $owner_id) {
/**
* Render a column in the flexible layout.
*/
-function panels_flexible_render_item($renderer, $item, $base, $content, $id, $position, $max, $clear = FALSE) {
- $output = '<div class="' . $base . ' ' . $base . '-' . $renderer->did . '-' . $id;
+function panels_flexible_render_item($renderer, $item, $content, $id, $position, $max, $clear = FALSE) {
+
+ // If we are rendering a row and there is just one row, we don't need to
+ // render the row unless there is fixed_width content inside it.
+ if (empty($renderer->admin) && $item['type'] == 'row' && $max == 0) {
+ $fixed = FALSE;
+ foreach ($item['children'] as $id) {
+ if ($renderer->settings['items'][$id]['width_type'] != '%') {
+ $fixed = TRUE;
+ break;
+ }
+ }
+
+ if (!$fixed) {
+ return $content;
+ }
+ }
+
+ // If we are rendering a column and there is just one column, we don't
+ // need to render the column unless it has a fixed_width.
+ if (empty($renderer->admin) && $item['type'] == 'column' && $max == 0 && $item['width_type'] == '%') {
+ return $content;
+ }
+
+ $base = $renderer->item_class[$item['type']];
+ $output = '<div class="' . $base . ' ' . $renderer->base[$item['type']] . '-' . $id;
if ($position == 0) {
$output .= ' ' . $base . '-first';
}
@@ -380,7 +539,7 @@ function panels_flexible_render_item($renderer, $item, $base, $content, $id, $po
$output .= panels_flexible_render_item_links($renderer, $id, $item);
}
- $output .= ' <div class="inside ' . $base . '-inside ' . $base . '-' . $renderer->did . '-' . $id . '-inside';
+ $output .= ' <div class="inside ' . $base . '-inside ' . $base . '-' . $renderer->base_class . '-' . $id . '-inside';
if ($position == 0) {
$output .= ' ' . $base . '-inside-first';
}
@@ -409,10 +568,10 @@ function panels_flexible_render_item($renderer, $item, $base, $content, $id, $po
function panels_flexible_render_splitter($renderer, $left_id, $right_id) {
$left = $renderer->settings['items'][$left_id];
- $left_class = 'panels-flexible-' . $left['type'] . '-' . $renderer->did . '-' . $left_id;
+ $left_class = $renderer->base[$left['type']] . '-' . $left_id;
if ($right_id) {
$right = $renderer->settings['items'][$right_id];
- $right_class = 'panels-flexible-' . $right['type'] . '-' . $renderer->did . '-' . $right_id;
+ $right_class = $renderer->base[$left['type']] . '-' . $right_id;
}
else {
$right = $left;
@@ -490,13 +649,14 @@ function panels_flexible_render_item_links($renderer, $id, $item) {
else if ($item['type'] == 'row') {
if ($id == 'canvas') {
$title = t('Canvas');
+ $settings = t('Canvas settings');
}
else {
$title = t('Row');
$settings = t('Row settings');
}
if (empty($item['children'])) {
- if ($item != 'canvas') {
+ if ($id != 'canvas') {
$remove = t('Remove row');
}
$add = $item['contains'] == 'region' ? t('Add region') : t('Add column');
@@ -515,28 +675,28 @@ function panels_flexible_render_item_links($renderer, $id, $item) {
if (!empty($settings)) {
$links[] = array(
'title' => $settings,
- 'href' => 'panels/ajax/flexible/settings/' . $renderer->cache_key . '/' . $id,
+ 'href' => $renderer->handler->get_url('layout', 'settings', $id),
'attributes' => array('class' => 'ctools-use-modal'),
);
}
if ($add) {
$links[] = array(
'title' => $add,
- 'href' => 'panels/ajax/flexible/add/' . $renderer->cache_key . '/' . $id,
+ 'href' => $renderer->handler->get_url('layout', 'add', $id),
'attributes' => array('class' => 'ctools-use-modal'),
);
}
if (isset($add2)) {
$links[] = array(
'title' => $add2,
- 'href' => 'panels/ajax/flexible/add/' . $renderer->cache_key . '/' . $id . '/right',
+ 'href' => $renderer->handler->get_url('layout', 'add', $id, 'right'),
'attributes' => array('class' => 'ctools-use-modal'),
);
}
if ($remove) {
$links[] = array(
'title' => $remove,
- 'href' => 'panels/ajax/flexible/remove/' . $renderer->cache_key . '/' . $id,
+ 'href' => $renderer->handler->get_url('layout', 'remove', $id),
'attributes' => array('class' => 'ctools-use-ajax'),
);
}
@@ -548,8 +708,13 @@ function panels_flexible_render_item_links($renderer, $id, $item) {
* Provide CSS for a flexible layout.
*/
function panels_flexible_render_css($renderer) {
- $parent_class = $renderer->admin ? '.panels-flexible-row-' . $renderer->did . '-canvas' : '.panel-flexible-' . $renderer->did;
- return panels_flexible_render_css_group($renderer, $renderer->settings['items']['canvas']['children'], $parent_class, 'column');
+ if ($renderer->admin) {
+ $parent_class = '.' . $renderer->base['row'] . '-canvas';
+ }
+ else {
+ $parent_class = '.' . $renderer->base['canvas'];
+ }
+ return panels_flexible_render_css_group($renderer, $renderer->settings['items']['canvas']['children'], $parent_class, 'column', 'canvas');
}
/**
@@ -559,9 +724,55 @@ function panels_flexible_render_css($renderer) {
* order to share margins and make sure that percent widths add up
* to the right total.
*/
-function panels_flexible_render_css_group($renderer, $list, $owner_id, $type) {
+function panels_flexible_render_css_group($renderer, $list, $owner_id, $type, $id) {
$css = array();
- panels_flexible_get_css_group($css, $renderer, $list, $owner_id, $type);
+
+ // Start off with some generic CSS to properly pad regions
+ $css['.' . $renderer->item_class['region']] = array(
+ 'padding' => '0',
+ );
+
+ $css['.' . $renderer->item_class['region'] . '-inside'] = array(
+ 'padding-right' => $renderer->region_separation,
+ 'padding-left' => $renderer->region_separation,
+ );
+
+ $css['.' . $renderer->item_class['region'] . '-inside-first'] = array(
+ 'padding-left' => '0',
+ );
+
+ $css['.' . $renderer->item_class['region'] . '-inside-last'] = array(
+ 'padding-right' => '0',
+ );
+
+ $css['.' . $renderer->item_class['column']] = array(
+ 'padding' => '0',
+ );
+
+ $css['.' . $renderer->item_class['column'] . '-inside'] = array(
+ 'padding-right' => $renderer->column_separation,
+ 'padding-left' => $renderer->column_separation,
+ );
+
+ $css['.' . $renderer->item_class['column'] . '-inside-first'] = array(
+ 'padding-left' => '0',
+ );
+
+ $css['.' . $renderer->item_class['column'] . '-inside-last'] = array(
+ 'padding-right' => '0',
+ );
+
+ // And properly pad rows too
+ $css['.' . $renderer->item_class['row']] = array(
+ 'padding' => '0 0 ' . $renderer->row_separation . ' 0',
+ 'margin' => '0',
+ );
+
+ $css['.' . $renderer->item_class['row'] . '-last'] = array(
+ 'padding-bottom' => '0',
+ );
+
+ panels_flexible_get_css_group($css, $renderer, $list, $owner_id, $type, $id);
ctools_include('css');
return ctools_css_assemble($css);
@@ -573,7 +784,7 @@ function panels_flexible_render_css_group($renderer, $list, $owner_id, $type) {
* This will parse down into children and produce all of the CSS needed if you
* start from the top.
*/
-function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type) {
+function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type, $item_id) {
if ($type != 'row') {
// Go through our items and break up into right/center/right groups so we
// can figure out our offsets.
@@ -614,7 +825,7 @@ function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type
// Go through our right sides and create CSS.
foreach ($left as $id) {
- $class = ".panels-flexible-$type-" . $renderer->did . "-$id";
+ $class = "." . $renderer->base[$type] . "-$id";
$css[$class] = array(
'position' => 'relative',
'float' => 'left',
@@ -627,7 +838,7 @@ function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type
$right_pixels = 0;
foreach ($right as $id) {
- $class = ".panels-flexible-$type-" . $renderer->did . "-$id";
+ $class = "." . $renderer->base[$type] . "-$id";
$css[$class] = array(
'float' => 'left',
'width' => $renderer->settings['items'][$id]['width'] . "px",
@@ -640,12 +851,12 @@ function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type
// Because we love IE so much, auto scale everything to 99%. This
// means adding up the actual widths and then providing a multiplier
// to each so that the total is 99%.
- $scale = 99.0 / $middle_total;
+ $scale = $renderer->scale_base / $middle_total;
foreach ($middle as $position => $id) {
- $class = ".panels-flexible-$type-" . $renderer->did . "-$id";
+ $class = "." . $renderer->base[$type] . "-$id";
$css[$class] = array(
'float' => 'left',
- 'width' => ($renderer->settings['items'][$id]['width'] * $scale) . "%",
+ 'width' => number_format($renderer->settings['items'][$id]['width'] * $scale, 4, '.', '') . "%",
);
// Store this so we can use it later.
@@ -658,17 +869,36 @@ function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type
// If there is any total remaining, we need to offset the splitter
// by this much too.
if ($left_total) {
- $css["$owner_id-left"]['margin-left'] = '-' . $left_total . 'px';
- // IE hack
- $css["* html $owner_id-left"]['left'] = $left_total . "px";
+ if ($renderer->admin || count($middle)) {
+ $css["$owner_id-middle"]['margin-left'] = $left_total . 'px';
+ // IE hack
+ $css["* html $owner_id-left"]['left'] = $left_total . "px";
+ }
+ else {
+ $css["$owner_id-inside"]['margin-left'] = '-' . $left_total . 'px';
+ // IE hack
+ $css["* html $owner_id-inside"]['left'] = $left_total . "px";
+ }
}
// Add this even if it's 0 so we can handle removals.
- $css["$owner_id-inside"]['padding-left'] = $left_total . 'px';
+ $css["$owner_id-inside"]['padding-left'] = '0px';
if ($right_total) {
- $css["$owner_id-right"]['margin-right'] = '-' . $right_total . 'px';
+ $css["$owner_id-middle"]['margin-right'] = $right_total . 'px';
}
- $css["$owner_id-inside"]['padding-right'] = $right_total . 'px';
+ $css["$owner_id-inside"]['padding-right'] = '0px';
+ }
+
+ // If the canvas has a fixed width set, and this is the canvas, fix the
+ // width.
+ if ($item_id == 'canvas') {
+ $item = $renderer->settings['items'][$item_id];
+ if (!empty($item['fixed_width']) && intval($item['fixed_width'])) {
+ $css['.' . $renderer->base['canvas']]['width'] = intval($item['fixed_width']) . 'px';
+ }
+ else {
+ $css['.' . $renderer->base['canvas']]['width'] = 'auto';
+ }
}
// Go through each item and process children.
@@ -686,31 +916,44 @@ function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type
$child_type = isset($item['contains']) ? $item['contains'] : 'region';
}
- $class = ".panels-flexible-$type-" . $renderer->did . "-$id";
- panels_flexible_get_css_group($css, $renderer, $item['children'], $class, $child_type);
+ $class = "." . $renderer->base[$type] . "-$id";
+ panels_flexible_get_css_group($css, $renderer, $item['children'], $class, $child_type, $id);
}
}
/**
* AJAX responder to edit flexible settings for an item.
+ *
+ * $handler object
+ * The display renderer handler object.
*/
-function panels_ajax_flexible_edit_settings($cache, $id) {
- $settings = &$cache->display->layout_settings;
- panels_flexible_convert_settings($settings);
+function panels_ajax_flexible_edit_settings($handler, $id) {
+ $settings = &$handler->display->layout_settings;
+ panels_flexible_convert_settings($settings, $handler->plugins['layout']);
if (empty($settings['items'][$id])) {
ctools_modal_render(t('Error'), t('Invalid item id.'));
}
$item = &$settings['items'][$id];
- $siblings = $settings['items'][$item['parent']]['children'];
+ $siblings = array();
+
+ if ($id != 'canvas') {
+ $siblings = $settings['items'][$item['parent']]['children'];
+ }
+
switch ($item['type']) {
case 'column':
$title = t('Configure column');
break;
case 'row':
- $title = t('Configure row');
+ if ($id == 'canvas') {
+ $title = t('Configure canvas');
+ }
+ else {
+ $title = t('Configure row');
+ }
break;
case 'region':
$title = t('Configure region');
@@ -718,7 +961,7 @@ function panels_ajax_flexible_edit_settings($cache, $id) {
}
$form_state = array(
- 'display' => &$cache->display,
+ 'display' => &$handler->display,
'item' => &$item,
'id' => $id,
'siblings' => $siblings,
@@ -732,21 +975,54 @@ function panels_ajax_flexible_edit_settings($cache, $id) {
if (empty($output)) {
// If the width type changed then other nearby items will have
// to have their widths adjusted.
- panels_load_include('display-edit');
- panels_edit_cache_set($cache);
+ panels_edit_cache_set($handler->cache);
+
+ $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
+ $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
+
$output = array();
// If the item is a region, replace the title.
- $class = 'panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $id;
+ $class = $renderer->base[$item['type']] . '-' . $id;
if ($item['type'] == 'region') {
$output[] = ctools_ajax_command_replace(".$class h2.label",
'<h2 class="label">' . check_plain($item['title']) . '</h2>');
}
- // If not a row, reset the css width if necessary.
+
+ // Rerender our links in case something changed.
+ $output[] = ctools_ajax_command_replace('.flexible-links-' . $id,
+ panels_flexible_render_item_links($renderer, $id, $item));
+
+ // If editing the canvas, reset the CSS width
+ if ($id == 'canvas') {
+ // update canvas CSS.
+ $css = array(
+ '.' . $renderer->item_class['column'] . '-inside' => array(
+ 'padding-left' => $renderer->column_separation,
+ 'padding-right' => $renderer->column_separation,
+ ),
+ '.' . $renderer->item_class['region'] . '-inside' => array(
+ 'padding-left' => $renderer->region_separation,
+ 'padding-right' => $renderer->region_separation,
+ ),
+ '.' . $renderer->item_class['row'] => array(
+ 'padding-bottom' => $renderer->row_separation,
+ ),
+ );
+ if (!empty($item['fixed_width']) && intval($item['fixed_width'])) {
+ $css['.' . $renderer->base['canvas']] = array('width' => intval($item['fixed_width']) . 'px');
+ }
+ else {
+ $css['.' . $renderer->base['canvas']] = array('width' => 'auto');
+ }
+ foreach ($css as $selector => $data) {
+ $output[] = ctools_ajax_command_css($selector, $data);
+ }
+ }
$output[] = ctools_modal_command_dismiss();
}
- ctools_ajax_render($output);
+ $handler->commands = $output;
}
/**
@@ -762,8 +1038,6 @@ function panels_flexible_config_item_form(&$form_state) {
$settings = &$form_state['settings'];
$id = &$form_state['id'];
-// $form['#action'] = $form_state['url'];
-
if ($item['type'] == 'region') {
$form['title'] = array(
'#title' => t('Region title'),
@@ -771,55 +1045,121 @@ function panels_flexible_config_item_form(&$form_state) {
'#default_value' => $item['title'],
'#required' => TRUE,
);
+ }
+ if ($id == 'canvas') {
$form['class'] = array(
- '#title' => t('Region class'),
+ '#title' => t('Canvas class'),
'#type' => 'textfield',
'#default_value' => isset($item['class']) ? $item['class'] : '',
- '#description' => t('Enter a CSS class that will be used for this region. This can be used to apply automatic styling from your theme, for example.'),
+ '#description' => t('This class will the primary class for this layout. It will also be appended to all column, row and region_classes to ensure that layouts within layouts will not inherit CSS from each other. If left blank, the name of the layout or ID of the display will be used.'),
);
- }
- if ($item['type'] != 'row') {
- // Test to see if there are fluid items to the left or the right. If there
- // are fluid items on both sides, this item cannot be set to fixed.
- $left = $right = FALSE;
- $current = 'left';
- foreach ($siblings as $sibling) {
- if ($sibling == $id) {
- $current = 'right';
- }
- else if ($settings['items'][$sibling]['width_type'] == '%') {
- $$current = TRUE; // Indirection.
- }
- }
+ $form['column_class'] = array(
+ '#title' => t('Column class'),
+ '#type' => 'textfield',
+ '#default_value' => isset($item['column_class']) ? $item['column_class'] : '',
+ '#description' => t('This class will be applied to all columns of the layout. If left blank this will be panels-flexible-column.'),
+ );
- $form['width_type'] = array(
- '#type' => 'select',
- '#title' => t('Width'),
- '#default_value' => $item['width_type'],
- '#options' => array(
- '%' => t('Fluid'),
- 'px' => t('Fixed'),
- ),
- '#disabled' => TRUE,
+ $form['row_class'] = array(
+ '#title' => t('Row class'),
+ '#type' => 'textfield',
+ '#default_value' => isset($item['row_class']) ? $item['row_class'] : '',
+ '#description' => t('This class will be applied to all rows of the layout. If left blank this will be panels-flexible-row.'),
+ );
+
+ $form['region_class'] = array(
+ '#title' => t('Region class'),
+ '#type' => 'textfield',
+ '#default_value' => isset($item['region_class']) ? $item['region_class'] : '',
+ '#description' => t('This class will be applied to all regions of the layout. If left blank this will be panels-flexible-region.'),
+ );
+
+ $form['no_scale'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Scale fluid widths for IE6'),
+ '#description' => t('IE6 does not do well with 100% widths. If checked, width will be scaled to 99% to compensate.'),
+ '#default_value' => empty($item['no_scale']),
+ );
+
+ $form['fixed_width'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Fixed width'),
+ '#description' => t('If a value is entered, the layout canvas will be fixed to the given pixel width.'),
+ '#default_value' => isset($item['fixed_width']) ? $item['fixed_width'] : '',
+ );
+
+ $form['column_separation'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Column separation'),
+ '#description' => t('How much padding to put on columns that are that are next to other columns. Note that this is put on both columns so the real amount is doubled.'),
+ '#default_value' => isset($item['column_separation']) ? $item['column_separation'] : '0.5em',
+ );
+
+ $form['region_separation'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Region separation'),
+ '#description' => t('How much padding to put on regions that are that are next to other regions. Note that this is put on both regions so the real amount is doubled.'),
+ '#default_value' => isset($item['region_separation']) ? $item['region_separation'] : '0.5em',
+ );
+
+ $form['row_separation'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Row separation'),
+ '#description' => t('How much padding to put on beneath rows to separate them from each other. Because this is placed only on the bottom, not hte top, this is NOT doubled like column/region separation.'),
+ '#default_value' => isset($item['row_separation']) ? $item['row_separation'] : '0.5em',
);
}
else {
- $form['contains'] = array(
- '#type' => 'select',
- '#title' => t('Contains'),
- '#default_value' => $item['contains'],
- '#options' => array(
- 'region' => t('Regions'),
- 'column' => t('Columns'),
- ),
+ $form['class'] = array(
+ '#title' => t('CSS class'),
+ '#type' => 'textfield',
+ '#default_value' => isset($item['class']) ? $item['class'] : '',
+ '#description' => t('Enter a CSS class that will be used. This can be used to apply automatic styling from your theme, for example.'),
);
- if (!empty($item['children'])) {
- $form['contains']['#disabled'] = TRUE;
- $form['contains']['#value'] = $item['contains'];
- $form['contains']['#description'] = t('You must remove contained items to change the row container type.');
+ if ($item['type'] != 'row') {
+ // Test to see if there are fluid items to the left or the right. If there
+ // are fluid items on both sides, this item cannot be set to fixed.
+ $left = $right = FALSE;
+ $current = 'left';
+ foreach ($siblings as $sibling) {
+ if ($sibling == $id) {
+ $current = 'right';
+ }
+ else if ($settings['items'][$sibling]['width_type'] == '%') {
+ $$current = TRUE; // Indirection.
+ }
+ }
+
+ $form['width_type'] = array(
+ '#type' => 'select',
+ '#title' => t('Width'),
+ '#default_value' => $item['width_type'],
+ '#options' => array(
+ '%' => t('Fluid'),
+ 'px' => t('Fixed'),
+ ),
+ '#disabled' => TRUE,
+ );
+ }
+ else {
+ $form['contains'] = array(
+ '#type' => 'select',
+ '#title' => t('Contains'),
+ '#default_value' => $item['contains'],
+ '#options' => array(
+ 'region' => t('Regions'),
+ 'column' => t('Columns'),
+ ),
+ );
+
+ if (!empty($item['children'])) {
+ $form['contains']['#disabled'] = TRUE;
+ $form['contains']['#value'] = $item['contains'];
+ $form['contains']['#description'] = t('You must remove contained items to change the row container type.');
+ }
}
}
@@ -838,25 +1178,39 @@ function panels_flexible_config_item_form_submit(&$form, &$form_state) {
$item = &$form_state['item'];
if ($item['type'] == 'region') {
$item['title'] = $form_state['values']['title'];
- $item['class'] = $form_state['values']['class'];
}
- if ($item['type'] != 'row') {
+ $item['class'] = $form_state['values']['class'];
+
+ if ($form_state['id'] == 'canvas') {
+ $item['column_class'] = $form_state['values']['column_class'];
+ $item['row_class'] = $form_state['values']['row_class'];
+ $item['region_class'] = $form_state['values']['region_class'];
+ // Reverse this as the checkbox is backward from how we actually store
+ // it to make it simpler to default to scaling.
+ $item['no_scale'] = !$form_state['values']['no_scale'];
+ $item['fixed_width'] = $form_state['values']['fixed_width'];
+ $item['column_separation'] = $form_state['values']['column_separation'];
+ $item['region_separation'] = $form_state['values']['region_separation'];
+ $item['row_separation'] = $form_state['values']['row_separation'];
+ }
+ else if ($item['type'] != 'row') {
$item['width_type'] = $form_state['values']['width_type'];
}
else {
$item['contains'] = $form_state['values']['contains'];
}
+
}
/**
* AJAX responder to add a new row, column or region to a flexible layout.
*/
-function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
+function panels_ajax_flexible_edit_add($handler, $id, $location = 'left') {
ctools_include('modal');
ctools_include('ajax');
- $settings = &$cache->display->layout_settings;
- panels_flexible_convert_settings($settings);
+ $settings = &$handler->display->layout_settings;
+ panels_flexible_convert_settings($settings, $handler->plugins['layout']);
if (empty($settings['items'][$id])) {
ctools_modal_render(t('Error'), t('Invalid item id.'));
@@ -906,7 +1260,7 @@ function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
}
$form_state = array(
- 'display' => &$cache->display,
+ 'display' => &$handler->display,
'parent' => &$parent,
'item' => &$item,
'id' => $id,
@@ -920,27 +1274,19 @@ function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
if (empty($output)) {
// If the width type changed then other nearby items will have
// to have their widths adjusted.
- panels_load_include('display-edit');
- panels_edit_cache_set($cache);
+ panels_edit_cache_set($handler->cache);
$output = array();
- $css_id = isset($cache->display->css_id) ? $cache->display->css_id : '';
+ $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
// Create a renderer object so we can render our new stuff.
- $renderer = new stdClass;
- $renderer->settings = $settings;
- $renderer->content = array();
- $renderer->css_id = $css_id;
- $renderer->did = $cache->display->did;
- $renderer->cache_key = $cache->display->cache_key;
- $renderer->id_str = $css_id ? 'id="' . $css_id . '"' : '';
- $renderer->admin = TRUE;
+ $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
$content = '';
if ($item['type'] == 'region') {
- panels_load_include('display-edit');
- $panel_buttons = panels_edit_panel_get_links($cache->display, $form_state['key']);
+ $handler->plugins['layout']['panels'][$form_state['key']] = $item['title'];
+
+ $content = $handler->render_region($form_state['key'], array());
- $content = panels_render_region_dnd('', $form_state['key'], $item['title'], $panel_buttons);
// Manually add the hidden field that our region uses to store pane info.
$content .= '<input type="hidden" name="panel[pane][' .
$form_state['key'] . ']" id="edit-panel-pane-' . $form_state['key'] . '" value="" />';
@@ -951,20 +1297,20 @@ function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
// so that more stuff can be added inside it as needed.
foreach (array('left', 'middle', 'right') as $position) {
if (!empty($content) || $renderer->admin) {
- $content .= '<div class="panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $form_state['key'] . '-' . $position . '"></div>';
+ $content .= '<div class="' . $renderer->base[$item['type']] . '-' . $form_state['key'] . '-' . $position . '"></div>';
}
}
}
// render the item
- $parent_class = 'panels-flexible-' . $parent['type'] . '-' . $cache->display->did . '-' . $id;
- $item_output = panels_flexible_render_item($renderer, $item, 'panels-flexible-' . $item['type'], $content, $form_state['key'], 0, 0, $item['type'] == 'row');
+ $parent_class = $renderer->base[$parent['type']] . '-' . $id;
+ $item_output = panels_flexible_render_item($renderer, $item, $content, $form_state['key'], 0, 0, $item['type'] == 'row');
// Get all the CSS necessary for the entire row (as width adjustments may
// have cascaded).
$css = array();
- panels_flexible_get_css_group($css, $renderer, $parent['children'], '.' . $parent_class, $item['type']);
+ panels_flexible_get_css_group($css, $renderer, $parent['children'], '.' . $parent_class, $item['type'], $id);
$position = isset($renderer->positions[$form_state['key']]) ? $renderer->positions[$form_state['key']] : 'middle';
// If there's a nearby item, add the splitter and rewrite the width
@@ -975,36 +1321,36 @@ function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
case 'left':
if ($location == 'left') {
$item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
- $output[] = ctools_ajax_command_prepend('.' . $parent_class . '-left', $item_output);
+ $output[] = ctools_ajax_command_prepend('#panels-dnd-main .' . $parent_class . '-left', $item_output);
}
else if ($location == 'right') {
// If we are adding to the right side of the left box, there is
// a splitter that we have to remove; then we add our box normally,
// and then add a new splitter for just our guy.
- $output[] = ctools_ajax_command_remove('panels-flexible-splitter-for-panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $form_state['key']);
+ $output[] = ctools_ajax_command_remove('panels-flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $form_state['key']);
$item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) . $item_output;
$item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], NULL);
- $output[] = ctools_ajax_command_append('.' . $parent_class . '-left', $item_output);
+ $output[] = ctools_ajax_command_append('#panels-dnd-main .' . $parent_class . '-left', $item_output);
}
break;
case 'right':
if (!empty($form_state['sibling'])) {
$item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) . $item_output;
}
- $output[] = ctools_ajax_command_append('.' . $parent_class . '-right', $item_output);
+ $output[] = ctools_ajax_command_append('#panels-dnd-main .' . $parent_class . '-right', $item_output);
break;
case 'middle':
if ($location == 'left') {
if (!empty($form_state['sibling'])) {
$item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
}
- $output[] = ctools_ajax_command_prepend('.' . $parent_class . '-middle', $item_output);
+ $output[] = ctools_ajax_command_prepend('#panels-dnd-main .' . $parent_class . '-middle', $item_output);
}
else {
if (!empty($form_state['sibling'])) {
$item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) . $item_output;
}
- $output[] = ctools_ajax_command_append('.' . $parent_class . '-middle', $item_output);
+ $output[] = ctools_ajax_command_append('#panels-dnd-main .' . $parent_class . '-middle', $item_output);
}
break;
@@ -1014,7 +1360,7 @@ function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
$output[] = array('command' => 'flexible_fix_height');
if (!empty($form_state['sibling'])) {
- $sibling_width = '.panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $form_state['sibling'] . '-width';
+ $sibling_width = '#panels-dnd-main .' . $renderer->base[$item['type']] . '-' . $form_state['sibling'] . '-width';
$output[] = ctools_ajax_command_html($sibling_width, $settings['items'][$form_state['sibling']]['width']);
}
foreach ($css as $selector => $data) {
@@ -1034,7 +1380,7 @@ function panels_ajax_flexible_edit_add($cache, $id, $location = 'left') {
$output[] = ctools_modal_command_dismiss();
}
- ctools_ajax_render($output);
+ $handler->commands = $output;
}
/**
* Form to add a row, column or region to a flexible layout.
@@ -1049,8 +1395,6 @@ function panels_flexible_add_item_form(&$form_state) {
$location = &$form_state['location'];
$id = &$form_state['id'];
-// $form['#action'] = $form_state['url'];
-
if ($item['type'] == 'region') {
$form['title'] = array(
'#title' => t('Region title'),
@@ -1060,6 +1404,13 @@ function panels_flexible_add_item_form(&$form_state) {
);
}
+ $form['class'] = array(
+ '#title' => t('CSS Class'),
+ '#type' => 'textfield',
+ '#default_value' => isset($item['class']) ? $item['class'] : '',
+ '#description' => t('Enter a CSS class that will be used. This can be used to apply automatic styling from your theme, for example.'),
+ );
+
if ($item['type'] != 'row') {
// If there is a 'fixed' type on the side we're adding to, then this
// must also be fixed. Otherwise it can be either and should default to
@@ -1125,6 +1476,8 @@ function panels_flexible_add_item_form_submit(&$form, &$form_state) {
$location = &$form_state['location'];
$settings = &$form_state['settings'];
+ $item['class'] = $form_state['values']['class'];
+
if ($item['type'] == 'region') {
$item['title'] = $form_state['values']['title'];
}
@@ -1184,20 +1537,22 @@ function panels_flexible_add_item_form_submit(&$form, &$form_state) {
* AJAX responder to remove an existing row, column or region from a flexible
* layout.
*/
-function panels_ajax_flexible_edit_remove($cache, $id) {
- $settings = &$cache->display->layout_settings;
- panels_flexible_convert_settings($settings);
+function panels_ajax_flexible_edit_remove($handler, $id) {
+ $settings = &$handler->display->layout_settings;
+ panels_flexible_convert_settings($settings, $handler->plugins['layout']);
if (empty($settings['items'][$id])) {
- ctools_ajax_render_error(print_r($settings['items'], 1));
ctools_ajax_render_error(t('Invalid item id.'));
}
$item = &$settings['items'][$id];
+ $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
+ // Create a renderer object so we can render our new stuff.
+ $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
+
$siblings = &$settings['items'][$item['parent']]['children'];
- $parent_class = '.panels-flexible-' . $settings['items'][$item['parent']]['type'] .
- '-' . $cache->display->did . '-' . $item['parent'];
+ $parent_class = '.' . $renderer->base[$settings['items'][$item['parent']]['type']] . '-' . $item['parent'];
// Find the offset of our array. This will also be the key because
// this is a simple array.
@@ -1232,30 +1587,18 @@ function panels_ajax_flexible_edit_remove($cache, $id) {
unset($settings['items'][$id]);
// Save our new state.
- panels_load_include('display-edit');
- panels_edit_cache_set($cache);
- $class = 'panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $id;
+ panels_edit_cache_set($handler->cache);
+ $class = $renderer->base[$item['type']] . '-' . $id;
$output = array();
- $output[] = ctools_ajax_command_remove('.' . $class);
-
- $css_id = isset($cache->display->css_id) ? $cache->display->css_id : '';
- // Create a renderer object so we can render our new stuff.
- $renderer = new stdClass;
- $renderer->settings = $settings;
- $renderer->content = array();
- $renderer->css_id = $css_id;
- $renderer->did = $cache->display->did;
- $renderer->cache_key = $cache->display->cache_key;
- $renderer->id_str = $css_id ? 'id="' . $css_id . '"' : '';
- $renderer->admin = TRUE;
+ $output[] = ctools_ajax_command_remove('#panels-dnd-main .' . $class);
// Regenerate the CSS for siblings.
if (!empty($siblings)) {
// Get all the CSS necessary for the entire row (as width adjustments may
// have cascaded).
$css = array();
- panels_flexible_get_css_group($css, $renderer, $siblings, $parent_class, $item['type']);
+ panels_flexible_get_css_group($css, $renderer, $siblings, $parent_class, $item['type'], $item['parent']);
foreach ($css as $selector => $data) {
$output[] = ctools_ajax_command_css($selector, $data);
}
@@ -1263,16 +1606,16 @@ function panels_ajax_flexible_edit_remove($cache, $id) {
// There are potentially two splitters linked to this item to be removed.
if (!empty($prev)) {
- $output[] = ctools_ajax_command_remove('.flexible-splitter-for-panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $prev);
+ $output[] = ctools_ajax_command_remove('.flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $prev);
}
// Try to remove the 'next' one even if there isn't a $next.
- $output[] = ctools_ajax_command_remove('.flexible-splitter-for-panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $id);
+ $output[] = ctools_ajax_command_remove('.flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $id);
if (!empty($prev) && !empty($next)) {
// Add a new splitter that links $prev and $next:
$splitter = panels_flexible_render_splitter($renderer, $prev, $next);
- $prev_class = '.panels-flexible-' . $item['type'] . '-' . $cache->display->did . '-' . $prev;
+ $prev_class = '#panels-dnd-main .' . $renderer->base[$item['type']] . '-' . $prev;
$output[] = ctools_ajax_command_after($prev_class, $splitter);
// Send our fix height command.
$output[] = array('command' => 'flexible_fix_height');
@@ -1287,17 +1630,17 @@ function panels_ajax_flexible_edit_remove($cache, $id) {
'base' => 'panels-flexible-' . $item['type'],
);
- ctools_ajax_render($output);
+ $handler->commands = $output;
}
/**
* AJAX responder to store resize information when the user adjusts the
* splitter.
*/
-function panels_ajax_flexible_edit_resize($cache) {
+function panels_ajax_flexible_edit_resize($handler) {
ctools_include('ajax');
- $settings = &$cache->display->layout_settings;
- panels_flexible_convert_settings($settings);
+ $settings = &$handler->display->layout_settings;
+ panels_flexible_convert_settings($settings, $handler->plugins['layout']);
$settings['items'][$_POST['left']]['width'] = $_POST['left_width'];
if (!empty($_POST['right']) && $_POST['right'] != $_POST['left']) {
@@ -1305,9 +1648,114 @@ function panels_ajax_flexible_edit_resize($cache) {
}
// Save our new state.
- panels_load_include('display-edit');
- panels_edit_cache_set($cache);
+ panels_edit_cache_set($handler->cache);
- ctools_ajax_render(array('ok'));
+ $handler->commands = array('ok');
}
+/**
+ * AJAX form to bring up the "reuse" modal.
+ */
+function panels_ajax_flexible_edit_reuse($handler) {
+ $settings = &$handler->display->layout_settings;
+ panels_flexible_convert_settings($settings, $handler->plugins['layout']);
+
+ $form_state = array(
+ 'display' => &$handler->display,
+ 'settings' => &$settings,
+ 'ajax' => TRUE,
+ 'title' => t('Save this layout for reuse'),
+ );
+
+ $output = ctools_modal_form_wrapper('panels_flexible_reuse_form', $form_state);
+ if (empty($output)) {
+ // Create the new layout.
+ ctools_include('export');
+ $layout = ctools_export_crud_new('panels_layout');
+ $layout->plugin = 'flexible';
+ $layout->name = $form_state['values']['name'];
+ $layout->admin_title = $form_state['values']['admin_title'];
+ $layout->admin_description = $form_state['values']['admin_description'];
+ $layout->category = $form_state['values']['category'];
+ $layout->settings = $handler->display->layout_settings;
+
+ // Save it.
+ ctools_export_crud_save('panels_layout', $layout);
+
+ if (empty($form_state['values']['keep'])) {
+ // Set the actual layout_settings to now use the newly minted layout:
+ $handler->display->layout = 'flexible:' . $layout->name;
+ $handler->display->layout_settings = array();
+
+ // Save our new state.
+ panels_edit_cache_set($handler->cache);
+ }
+
+ // Dismiss the modal.
+ $output[] = ctools_modal_command_dismiss();
+ }
+
+ $handler->commands = $output;
+}
+
+function panels_flexible_reuse_form(&$form_state) {
+ $form['markup'] = array(
+ '#prefix' => '<div class="description">',
+ '#suffix' => '</div>',
+ '#value' => t('If you save this layout for reuse it will appear in the list of reusable layouts at admin/build/panels/layouts, and you will need to go there to edit it. This layout will then become an option for all future panels you make.'),
+ );
+
+ $form['admin_title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Administrative title'),
+ '#description' => t('This will appear in the administrative interface to easily identify it.'),
+ );
+
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Machine name'),
+ '#description' => t('The machine readable name of this layout. It must be unique, and it must contain only alphanumeric characters and underscores. Once created, you will not be able to change this value!'),
+ );
+
+ $form['category'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Category'),
+ '#description' => t('What category this layout should appear in. If left blank the category will be "Miscellaneous".'),
+ );
+
+ $form['admin_description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Administrative description'),
+ '#description' => t('A description of what this layout is, does or is for, for administrative use.'),
+ );
+
+ $form['keep'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Keep current panel layout flexible'),
+ '#description' => t('If checked, this panel will continue to use a generic flexible layout and will not use the saved layout. Use this option if you wish to clone this layout.'),
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ );
+
+ return $form;
+}
+
+function panels_flexible_reuse_form_validate(&$form, &$form_state) {
+ if (empty($form_state['values']['name'])) {
+ form_error($form['name'], t('You must choose a machine name.'));
+ }
+
+ ctools_include('export');
+ $test = ctools_export_crud_load('panels_layout', $form_state['values']['name']);
+ if ($test) {
+ form_error($form['name'], t('That name is used by another layout: @layout', array('@layout' => $test->admin_title)));
+ }
+
+ // Ensure name fits the rules:
+ if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
+ form_error($form['name'], t('Name must be alphanumeric or underscores only.'));
+ }
+}
diff --git a/plugins/layouts/onecol/onecol.inc b/plugins/layouts/onecol/onecol.inc
index 58faceb..58bae76 100644
--- a/plugins/layouts/onecol/onecol.inc
+++ b/plugins/layouts/onecol/onecol.inc
@@ -7,6 +7,7 @@
// Plugin definition
$plugin = array(
'title' => t('Single column'),
+ 'category' => t('Columns: 1'),
'icon' => 'onecol.png',
'theme' => 'panels_onecol',
'css' => 'onecol.css',
diff --git a/plugins/layouts/threecol_25_50_25/threecol_25_50_25.inc b/plugins/layouts/threecol_25_50_25/threecol_25_50_25.inc
index 5b6b9d4..2d166fd 100644
--- a/plugins/layouts/threecol_25_50_25/threecol_25_50_25.inc
+++ b/plugins/layouts/threecol_25_50_25/threecol_25_50_25.inc
@@ -7,6 +7,7 @@
// Plugin definition
$plugin = array(
'title' => t('Three column 25/50/25'),
+ 'category' => t('Columns: 3'),
'icon' => 'threecol_25_50_25.png',
'theme' => 'panels_threecol_25_50_25',
'theme arguments' => array('id', 'content'),
diff --git a/plugins/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.tpl.php b/plugins/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.tpl.php
index d6d0dc2..dc106ce 100644
--- a/plugins/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.tpl.php
+++ b/plugins/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.tpl.php
@@ -19,9 +19,12 @@
*/
?>
<div class="panel-display panel-3col-stacked clear-block" <?php if (!empty($css_id)) { print "id=\"$css_id\""; } ?>>
- <div class="panel-panel panel-col-top">
- <div class="inside"><?php print $content['top']; ?></div>
- </div>
+ <?php if ($content['top']): ?>
+ <div class="panel-panel panel-col-top">
+ <div class="inside"><?php print $content['top']; ?></div>
+ </div>
+ <?php endif ?>
+
<div class="center-wrapper">
<div class="panel-panel panel-col-first">
<div class="inside"><?php print $content['left']; ?></div>
@@ -35,7 +38,10 @@
<div class="inside"><?php print $content['right']; ?></div>
</div>
</div>
- <div class="panel-panel panel-col-bottom">
- <div class="inside"><?php print $content['bottom']; ?></div>
- </div>
+
+ <?php if ($content['bottom']): ?>
+ <div class="panel-panel panel-col-bottom">
+ <div class="inside"><?php print $content['bottom']; ?></div>
+ </div>
+ <?php endif ?>
</div>
diff --git a/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.inc b/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.inc
index 673e1c9..8e24eb7 100644
--- a/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.inc
+++ b/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.inc
@@ -4,6 +4,7 @@
// Plugin definition
$plugin = array(
'title' => t('Three column 25/50/25 stacked'),
+ 'category' => t('Columns: 3'),
'icon' => 'threecol_25_50_25_stacked.png',
'theme' => 'panels_threecol_25_50_25_stacked',
'css' => 'threecol_25_50_25_stacked.css',
diff --git a/plugins/layouts/threecol_33_34_33/threecol_33_34_33.inc b/plugins/layouts/threecol_33_34_33/threecol_33_34_33.inc
index 6f05277..b78f20a 100644
--- a/plugins/layouts/threecol_33_34_33/threecol_33_34_33.inc
+++ b/plugins/layouts/threecol_33_34_33/threecol_33_34_33.inc
@@ -4,6 +4,7 @@
// Plugin definition
$plugin = array(
'title' => t('Three column 33/34/33'),
+ 'category' => t('Columns: 3'),
'icon' => 'threecol_33_34_33.png',
'theme' => 'panels_threecol_33_34_33',
'css' => 'threecol_33_34_33.css',
diff --git a/plugins/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.tpl.php b/plugins/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.tpl.php
index f7ed50a..bc9ee4f 100644
--- a/plugins/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.tpl.php
+++ b/plugins/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.tpl.php
@@ -19,9 +19,12 @@
*/
?>
<div class="panel-display panel-3col-33-stacked clear-block" <?php if (!empty($css_id)) { print "id=\"$css_id\""; } ?>>
- <div class="panel-panel panel-col-top">
- <div class="inside"><?php print $content['top']; ?></div>
- </div>
+ <?php if ($content['top']): ?>
+ <div class="panel-panel panel-col-top">
+ <div class="inside"><?php print $content['top']; ?></div>
+ </div>
+ <?php endif ?>
+
<div class="center-wrapper">
<div class="panel-panel panel-col-first">
<div class="inside"><?php print $content['left']; ?></div>
@@ -35,7 +38,10 @@
<div class="inside"><?php print $content['right']; ?></div>
</div>
</div>
- <div class="panel-panel panel-col-bottom">
- <div class="inside"><?php print $content['bottom']; ?></div>
- </div>
+
+ <?php if ($content['bottom']): ?>
+ <div class="panel-panel panel-col-bottom">
+ <div class="inside"><?php print $content['bottom']; ?></div>
+ </div>
+ <?php endif ?>
</div>
diff --git a/plugins/layouts/twocol/twocol.inc b/plugins/layouts/twocol/twocol.inc
index 5f56c0e..45735cd 100644
--- a/plugins/layouts/twocol/twocol.inc
+++ b/plugins/layouts/twocol/twocol.inc
@@ -4,6 +4,7 @@
// Plugin definition
$plugin = array(
'title' => t('Two column'),
+ 'category' => t('Columns: 2'),
'icon' => 'twocol.png',
'theme' => 'panels_twocol',
'css' => 'twocol.css',
diff --git a/plugins/layouts/twocol_bricks/twocol_bricks.inc b/plugins/layouts/twocol_bricks/twocol_bricks.inc
index 2efe270..b42b9cf 100644
--- a/plugins/layouts/twocol_bricks/twocol_bricks.inc
+++ b/plugins/layouts/twocol_bricks/twocol_bricks.inc
@@ -9,6 +9,7 @@
// Plugin definition
$plugin = array(
'title' => t('Two column bricks'),
+ 'category' => t('Columns: 2'),
'icon' => 'twocol_bricks.png',
'theme' => 'panels_twocol_bricks',
'css' => 'twocol_bricks.css',
diff --git a/plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php b/plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php
index c8979db..7e30ca8 100644
--- a/plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php
+++ b/plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php
@@ -18,19 +18,24 @@
*/
?>
<div class="panel-2col-stacked clear-block panel-display" <?php if (!empty($css_id)) { print "id=\"$css_id\""; } ?>>
- <div class="panel-col-top panel-panel">
- <div class="inside"><?php print $content['top']; ?></div>
- </div>
+ <?php if ($content['top']): ?>
+ <div class="panel-col-top panel-panel">
+ <div class="inside"><?php print $content['top']; ?></div>
+ </div>
+ <?php endif; ?>
+
<div class="center-wrapper">
<div class="panel-col-first panel-panel">
<div class="inside"><?php print $content['left']; ?></div>
</div>
-
<div class="panel-col-last panel-panel">
<div class="inside"><?php print $content['right']; ?></div>
</div>
</div>
- <div class="panel-col-bottom panel-panel">
- <div class="inside"><?php print $content['bottom']; ?></div>
- </div>
+
+ <?php if ($content['bottom']): ?>
+ <div class="panel-col-bottom panel-panel">
+ <div class="inside"><?php print $content['bottom']; ?></div>
+ </div>
+ <?php endif; ?>
</div>
diff --git a/plugins/layouts/twocol_stacked/twocol_stacked.inc b/plugins/layouts/twocol_stacked/twocol_stacked.inc
index cc69aa6..61e38b8 100644
--- a/plugins/layouts/twocol_stacked/twocol_stacked.inc
+++ b/plugins/layouts/twocol_stacked/twocol_stacked.inc
@@ -4,6 +4,7 @@
// Plugin definition
$plugin = array(
'title' => t('Two column stacked'),
+ 'category' => t('Columns: 2'),
'icon' => 'twocol_stacked.png',
'theme' => 'panels_twocol_stacked',
'css' => 'twocol_stacked.css',
diff --git a/plugins/page_wizards/landing_page.inc b/plugins/page_wizards/landing_page.inc
new file mode 100644
index 0000000..63180ea
--- /dev/null
+++ b/plugins/page_wizards/landing_page.inc
@@ -0,0 +1,272 @@
+<?php
+// $Id$
+/**
+ * @file
+ * A page creation wizard to quickly create simple landing pages.
+ *
+ * This wizard strips out a lot of features that are not normally needed for
+ * simple landing pages, such as access control, tabs, contexts, and the
+ * selection of a variant. It will just assume you want a panel and let you
+ * select the layout right away. This is 2 fewer forms than the standard
+ * add page process and it will point you at the finished page rather than
+ * sending you directly to the edit UI when finished.
+ *
+ * It also will auto-enable IPE if it can.
+ */
+$plugin = array(
+ 'title' => t('Landing page'),
+ 'page title' => t('Landing page wizard'),
+ 'description' => t('Landing pages are simple pages that have a path, possibly a visible menu entry, and a panel layout with simple content.'),
+
+ 'type' => 'panels',
+
+ 'form info' => array(
+ 'order' => array(
+ 'basic' => t('Basic settings'),
+ 'content' => t('Content'),
+ ),
+
+ 'forms' => array(
+ 'basic' => array(
+ 'form id' => 'panels_landing_page_basic',
+ ),
+ 'content' => array(
+ 'form id' => 'panels_landing_page_content',
+ ),
+ ),
+ ),
+
+ 'default cache' => 'panels_landing_page_new_page',
+
+ 'finish' => 'panels_landing_page_finish',
+);
+
+/**
+ * Provide defaults for a new cache.
+ *
+ * The cache will store all our temporary data; it isn't really a page
+ * in itself, but it does contain everything we need to make one at the end.
+ */
+function panels_landing_page_new_page(&$cache) {
+ $cache->name = '';
+ $cache->admin_title = '';
+ $cache->admin_description = '';
+ $cache->path = '';
+ $cache->menu_entry = FALSE;
+ $cache->menu = array(
+ 'type' => 'none',
+ 'title' => '',
+ 'weight' => 0,
+ 'name' => 'navigation',
+ 'parent' => array(
+ 'type' => 'none',
+ 'title' => '',
+ 'weight' => 0,
+ 'name' => 'navigation',
+ ),
+ );
+ $cache->display = panels_new_display();
+ $cache->display->layout = 'flexible';
+}
+
+/**
+ * First page of our page creator wizard.
+ */
+function panels_landing_page_basic(&$form, &$form_state) {
+ $cache = &$form_state['cache'];
+ ctools_include('dependent');
+
+ $form['admin_title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Administrative title'),
+ '#description' => t('The name of this page. This will appear in the administrative interface to easily identify it.'),
+ '#default_value' => $cache->admin_title,
+ '#required' => TRUE,
+ );
+
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Machine name'),
+ '#description' => t('The machine readable name of this page. It must be unique, and it must contain only alphanumeric characters and underscores. Once created, you will not be able to change this value!'),
+ '#default_value' => $cache->name,
+ '#required' => TRUE,
+ );
+
+ $form['admin_description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Administrative description'),
+ '#description' => t('A description of what this page is, does or is for, for administrative use.'),
+ '#default_value' => $cache->admin_description,
+ );
+
+ // path
+ $form['path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path'),
+ '#default_value' => $cache->path,
+ '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
+ '#required' => TRUE,
+ );
+
+ $form['menu_entry'] = array(
+ '#type' => 'checkbox',
+ '#default_value' => $cache->menu_entry,
+ '#title' => t('Add a visible menu entry for this page'),
+ );
+
+ $form['menu']['#tree'] = TRUE;
+
+ $form['menu']['title'] = array(
+ '#title' => t('Menu title'),
+ '#type' => 'textfield',
+ '#default_value' => $cache->menu['title'],
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('edit-menu-entry' => array(1)),
+ );
+
+ // Only display the menu selector if menu module is enabled.
+ if (module_exists('menu')) {
+ $form['menu']['name'] = array(
+ '#title' => t('Menu'),
+ '#type' => 'select',
+ '#options' => menu_get_menus(),
+ '#default_value' => $cache->menu['name'],
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('edit-menu-entry' => array(1)),
+ );
+ }
+ else {
+ $form['menu']['name'] = array(
+ '#type' => 'value',
+ '#value' => $cache->menu['name'],
+ );
+ $form['menu']['markup'] = array(
+ '#value' => t('Menu selection requires the activation of menu module.'),
+ );
+ }
+ $form['menu']['weight'] = array(
+ '#title' => t('Weight'),
+ '#type' => 'textfield',
+ '#default_value' => isset($cache->menu['weight']) ? $cache->menu['weight'] : 0,
+ '#description' => t('The lower the weight the higher/further left it will appear.'),
+ '#process' => array('ctools_dependent_process'),
+ '#dependency' => array('edit-menu-entry' => array(1)),
+ );
+
+ ctools_include('page-wizard', 'panels');
+ panels_page_wizard_add_layout($form, $form_state);
+}
+
+/**
+ * Submit function to store the form data in our cache.
+ */
+function panels_landing_page_basic_validate(&$form, &$form_state) {
+ // Ensure all 'page' features are loaded.
+ $page_task = page_manager_get_task('page');
+
+ // Validate that the name is ok.
+ $test = page_manager_page_load($form_state['values']['name']);
+ if ($test) {
+ form_error($form['name'], t('That name is used by another page: @page', array('@page' => $test->admin_title)));
+ }
+
+ // Ensure name fits the rules:
+ if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
+ form_error($form['name'], t('Page name must be alphanumeric or underscores only.'));
+ }
+
+ // Validate that the path is ok.
+ if (preg_match('/[%!\?#&]/', $form_state['values']['path'])) {
+ form_error($form['path'], t('%, !, ?, #, or & cannot appear in the path.'));
+ }
+
+ // Check to see if something is already using the path
+ $result = db_query("SELECT * FROM {menu_router} WHERE path = '%s'", $form_state['values']['path']);
+ while ($router = db_fetch_object($result)) {
+ form_error($form['path'], t('That path is already in use. This system cannot override existing paths.'));
+ return;
+ }
+
+ // Ensure the path is not already an alias to something else.
+ $result = db_query("SELECT src, dst FROM {url_alias} WHERE dst = '%s'", $form_state['values']['path']);
+ if ($alias = db_fetch_object($result)) {
+ form_error($form['path'], t('That path is currently assigned to be an alias for @alias. This system cannot override existing aliases.', array('@alias' => $alias->src)));
+ }
+}
+
+/**
+ * Submit function to store the form data in our cache.
+ */
+function panels_landing_page_basic_submit(&$form, &$form_state) {
+ $cache = &$form_state['cache'];
+ $cache->name = $form_state['values']['name'];
+ $cache->admin_title = $form_state['values']['admin_title'];
+ $cache->admin_description = $form_state['values']['admin_description'];
+ $cache->path = $form_state['values']['path'];
+ $cache->menu_entry = $form_state['values']['menu_entry'];
+ $cache->menu['title'] = $form_state['values']['menu']['title'];
+ $cache->menu['weight'] = $form_state['values']['menu']['weight'];
+ $cache->menu['name'] = $form_state['values']['menu']['name'];
+ $cache->menu['type'] = $cache->menu_entry ? 'normal' : 'none';
+ $cache->display->layout = $form_state['values']['layout'];
+ $cache->display->title = $form_state['values']['admin_title'];
+}
+
+/**
+ * Second page of our wizard. This one provides a layout and lets the
+ * user add content.
+ */
+function panels_landing_page_content(&$form, &$form_state) {
+ ctools_include('page-wizard', 'panels');
+ panels_page_wizard_add_content($form, $form_state);
+}
+
+/**
+ * Submit function to store the form data in our cache.
+ */
+function panels_landing_page_submit(&$form, &$form_state) {
+ panels_page_wizard_add_content_submit($form, $form_state);
+}
+
+/**
+ * Finish callback for the wizard.
+ *
+ * When the wizard is finished, this callback will create the actual
+ * page, save it, and redirect the user to view the new work.
+ */
+function panels_landing_page_finish(&$form_state) {
+ $cache = &$form_state['cache'];
+
+ // Ensure all 'page' features are loaded.
+ $page_task = page_manager_get_task('page');
+
+ // Assemble a new page subtask.
+ $subtask = page_manager_page_new();
+ $subtask->name = $cache->name;
+ $subtask->path = $cache->path;
+ $subtask->admin_title = $cache->admin_title;
+ $subtask->admin_description = $cache->admin_description;
+ $subtask->path = $cache->path;
+ $subtask->menu = $cache->menu;
+
+ // Create the the panel context variant configured with our display
+ $plugin = page_manager_get_task_handler('panel_context');
+
+ // Create a new handler.
+ $handler = page_manager_new_task_handler($plugin);
+ $handler->conf['title'] = t('Landing page');
+ $handler->conf['display'] = $cache->display;
+ $handler->conf['pipeline'] = 'ipe';
+
+ // Assemble a new $page cache and assign it our page subtask and task
+ // handler.
+ $page = new stdClass();
+ page_manager_page_new_page_cache($subtask, $page);
+ page_manager_handler_add_to_page($page, $handler);
+
+ // Save it
+ page_manager_save_page_cache($page);
+
+ // Send us to the new page immediately.
+ $form_state['redirect'] = url($cache->path);
+}
diff --git a/plugins/page_wizards/node_override.inc b/plugins/page_wizards/node_override.inc
new file mode 100644
index 0000000..9fb96a5
--- /dev/null
+++ b/plugins/page_wizards/node_override.inc
@@ -0,0 +1,290 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Page wizard that can create a variant on the node_view to take over a node
+ * for a particular type.
+ *
+ * This wizard does a lot that's cut and pasted from exports. We can get away
+ * with this because we know exports tend to remain relatively backward
+ * compatible, and because we know that our context IDs are locked in the
+ * node_view page.
+ */
+$plugin = array(
+ 'title' => t('Node template'),
+ 'page title' => t('Node template wizard'),
+ 'description' => t('The node page wizard can help you override the node page for a type of node.'),
+
+ 'type' => 'panels',
+
+ 'form info' => array(
+ 'order' => array(
+ 'type' => t('Select node type'),
+ 'content' => t('Content'),
+ ),
+
+ 'forms' => array(
+ 'type' => array(
+ 'form id' => 'panels_node_override_basic',
+ ),
+ 'content' => array(
+ 'form id' => 'panels_node_override_content',
+ ),
+ ),
+ ),
+
+ 'default cache' => 'panels_node_override_new_page',
+
+ 'start' => 'panels_node_override_start',
+ 'finish' => 'panels_node_override_finish',
+);
+
+/**
+ * Provide defaults for a new cache.
+ *
+ * The cache will store all our temporary data; it isn't really a page
+ * in itself, but it does contain everything we need to make one at the end.
+ */
+function panels_node_override_new_page(&$cache) {
+ $cache->type = '';
+ $cache->display = panels_new_display();
+ $cache->display->layout = 'flexible';
+}
+
+/**
+ * Callback called prior to the wizard starting up on every page
+ * load.
+ */
+function panels_node_override_start($form_info, $step, &$form_state) {
+ $form_state['page'] = page_manager_get_page_cache('node_view');
+ if (!empty($form_state['page']->locked)) {
+ $account = user_load($form_state['page']->locked->uid);
+ $name = theme('username', $account);
+ $lock_age = format_interval(time() - $form_state['page']->locked->updated);
+ $break = url(page_manager_edit_url($form_state['page']->task_name, array('actions', 'break-lock')));
+
+ drupal_set_message(t('WARNING! The node_view is being edited by user !user, and is therefore locked from editing by others. This wizard cannot create a new node override while this page is locked. This lock is !age old. Click here to <a href="!break">break this lock</a>.', array('!user' => $name, '!age' => $lock_age, '!break' => $break)), 'warning');
+ }
+}
+
+/**
+ * First page of our page creator wizard.
+ */
+function panels_node_override_basic(&$form, &$form_state) {
+ $types = node_get_types();
+ $form_state['types'] = $types;
+
+ $already_done = array();
+ // Figure out which types already have variants assigned to them.
+ foreach ($form_state['page']->handlers as $name => $handler) {
+ if ($handler->handler == 'panel_context' && !empty($handler->conf['access']['plugins'])) {
+ foreach ($handler->conf['access']['plugins'] as $plugin) {
+ if ($plugin['name'] == 'node_type') {
+ foreach ($plugin['settings']['type'] as $type) {
+ $already_done[$type] = $name;
+ }
+ }
+ }
+ }
+ }
+
+ if ($already_done) {
+ $items = array();
+ foreach ($already_done as $type => $handler_id) {
+ $items[] = check_plain($types[$type]->name) . ' ' . l(t('[Edit]'), page_manager_edit_url($form_state['page']->task_name, array('handlers', $handler_id, 'content')));
+ }
+
+ $form['already_done'] = array(
+ '#type' => 'item',
+ '#title' => t('Existing node templates'),
+ '#value' => theme('item_list', $items),
+ );
+ }
+
+ $options = array();
+ foreach ($types as $name => $type) {
+ if (empty($already_done[$name])) {
+ $options[$name] = $type->name;
+ }
+ }
+
+ $form['type'] = array(
+ '#type' => 'select',
+ '#title' => t('Node type'),
+ '#options' => $options,
+ '#default_value' => $form_state['cache']->type,
+ );
+
+ ctools_include('page-wizard', 'panels');
+ panels_page_wizard_add_layout($form, $form_state);
+}
+
+/**
+ * Submit function to store the form data in our cache.
+ */
+function panels_node_override_basic_submit(&$form, &$form_state) {
+ $cache = &$form_state['cache'];
+ $cache->display->layout = $form_state['values']['layout'];
+ $cache->type = $form_state['values']['type'];
+
+ // Create a new handler object and cache it; this way we can use the
+ // handler object for retrieving contexts properly.
+ // Create the the panel context variant configured with our display
+ $plugin = page_manager_get_task_handler('panel_context');
+
+ // Create a new handler.
+ $cache->handler = page_manager_new_task_handler($plugin);
+ $cache->handler->conf['title'] = $form_state['types'][$cache->type]->name;
+ $cache->handler->conf['pipeline'] = 'ipe';
+ $cache->handler->conf['access'] = array(
+ 'plugins' => array(
+ 0 => array(
+ 'name' => 'node_type',
+ 'settings' => array(
+ 'type' => array(
+ $cache->type => $cache->type,
+ ),
+ ),
+ 'context' => 'argument_nid_1',
+ 'not' => FALSE,
+ ),
+ ),
+ 'logic' => 'and',
+ );
+
+ // Find a region by trying some basic main content region IDs.
+ $layout = panels_get_layout($form_state['values']['layout']);
+ $regions = panels_get_regions($layout, $cache->display);
+ foreach (array('center', 'middle', 'content', 'main') as $candidate) {
+ if (!empty($regions[$candidate])) {
+ $region = $candidate;
+ break;
+ }
+ }
+
+ // If all of the above failed, use the first region.
+ if (empty($region)) {
+ $keys = array_keys($regions);
+ $region = reset($keys);
+ }
+
+ // Populate the layout with content. This is from an export, with minor
+ // changes to ensure defaults are correct and to add stuff to the proper region.
+ $pane = new stdClass;
+ $pane->pid = 'new-1';
+ $pane->panel = $region;
+ $pane->type = 'node_content';
+ $pane->subtype = 'node_content';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'links' => 1,
+ 'page' => 1,
+ 'no_extras' => 0,
+ 'override_title' => 0,
+ 'override_title_text' => '',
+ 'identifier' => '',
+ 'link' => 0,
+ 'leave_node_title' => 0,
+ 'context' => 'argument_nid_1',
+ 'build_mode' => 'full',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 0;
+ $cache->display->content['new-1'] = $pane;
+ $cache->display->panels[$region][0] = 'new-1';
+ $pane = new stdClass;
+ $pane->pid = 'new-2';
+ $pane->panel = $region;
+ $pane->type = 'node_comments';
+ $pane->subtype = 'node_comments';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'mode' => variable_get('comment_default_mode', COMMENT_MODE_THREADED_EXPANDED),
+ 'order' => variable_get('comment_default_order', COMMENT_ORDER_NEWEST_FIRST),
+ 'comments_per_page' => variable_get('comment_default_per_page', '50'),
+ 'context' => 'argument_nid_1',
+ 'override_title' => 0,
+ 'override_title_text' => '',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 1;
+ $cache->display->content['new-2'] = $pane;
+ $cache->display->panels[$region][1] = 'new-2';
+ $pane = new stdClass;
+ $pane->pid = 'new-3';
+ $pane->panel = $region;
+ $pane->type = 'node_comment_form';
+ $pane->subtype = 'node_comment_form';
+ $pane->shown = TRUE;
+ $pane->access = array();
+ $pane->configuration = array(
+ 'anon_links' => 1,
+ 'context' => 'argument_nid_1',
+ 'override_title' => 0,
+ 'override_title_text' => '',
+ );
+ $pane->cache = array();
+ $pane->style = array(
+ 'settings' => NULL,
+ );
+ $pane->css = array();
+ $pane->extras = array();
+ $pane->position = 2;
+ $cache->display->content['new-3'] = $pane;
+ $cache->display->panels[$region][2] = 'new-3';
+
+ $task = page_manager_get_task('node_view');
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ $cache->context = ctools_context_handler_get_all_contexts($task, NULL, $cache->handler);
+
+}
+
+/**
+ * Second page of our wizard. This one provides a layout and lets the
+ * user add content.
+ */
+function panels_node_override_content(&$form, &$form_state) {
+ ctools_include('page-wizard', 'panels');
+ panels_page_wizard_add_content($form, $form_state);
+}
+
+/**
+ * Store changes to the display.
+ */
+function panels_node_override_content_submit(&$form, &$form_state) {
+ panels_page_wizard_add_content_submit($form, $form_state);
+}
+
+/**
+ * Complete the wizard, create a new variant, and send them to the
+ * edit screen of that variant.
+ */
+function panels_node_override_finish(&$form_state) {
+ $page = &$form_state['page'];
+ $cache = &$form_state['cache'];
+
+ // Add the new handler to the page
+ $cache->handler->conf['display'] = $cache->display;
+ page_manager_handler_add_to_page($page, $cache->handler);
+
+ // Save it
+ page_manager_save_page_cache($page);
+
+ // Send us to the page manager edit form for this.
+ $form_state['redirect'] = url(page_manager_edit_url('node_view', array('handlers', $cache->handler->name, 'content')));
+ drupal_set_message(t('Your node template has been created.'));
+}
diff --git a/plugins/style_bases/pane/pane_plain_box/icon.png b/plugins/style_bases/pane/pane_plain_box/icon.png
new file mode 100644
index 0000000..436355b
--- /dev/null
+++ b/plugins/style_bases/pane/pane_plain_box/icon.png
Binary files differ
diff --git a/plugins/style_bases/pane/pane_plain_box/pane-plain-box.css b/plugins/style_bases/pane/pane_plain_box/pane-plain-box.css
new file mode 100644
index 0000000..5a01336
--- /dev/null
+++ b/plugins/style_bases/pane/pane_plain_box/pane-plain-box.css
@@ -0,0 +1,11 @@
+%style {
+ margin-bottom: 10px;
+ color: %text;
+ background-color: %background;
+}
+
+%style h2 {
+ color: %header-text;
+ background-color: %header-background;
+}
+
diff --git a/plugins/style_bases/pane/pane_plain_box/pane-plain-box.tpl.php b/plugins/style_bases/pane/pane_plain_box/pane-plain-box.tpl.php
new file mode 100644
index 0000000..ee12044
--- /dev/null
+++ b/plugins/style_bases/pane/pane_plain_box/pane-plain-box.tpl.php
@@ -0,0 +1,17 @@
+<?php
+// $Id$
+/**
+ * @file
+ *
+ * Display the box for rounded corners.
+ *
+ * - $pane: The pane being rendered
+ * - $display: The display being rendered
+ * - $content: An object containing the content and title
+ * - $output: The result of theme('panels_pane')
+ * - $classes: The classes that must be applied to the top divs.
+ */
+?>
+<div class="<?php print $classes ?>">
+ <?php print $output; ?>
+</div>
diff --git a/plugins/style_bases/pane/pane_plain_box/pane_plain_box.inc b/plugins/style_bases/pane/pane_plain_box/pane_plain_box.inc
new file mode 100644
index 0000000..71598ff
--- /dev/null
+++ b/plugins/style_bases/pane/pane_plain_box/pane_plain_box.inc
@@ -0,0 +1,94 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Definition of the style base for the rounded shadow box.
+ *
+ * This box is colorable, has rounded corners and a drop shadow.
+ */
+
+$plugin = array(
+ 'category' => t('Basic styles'),
+ 'title' => t('Plain'),
+ 'description' => t('A plain box with an optional border. You may set the color of the text and the border.'),
+ 'module' => 'panels',
+ 'type' => 'pane',
+
+ 'css' => 'pane-plain-box.css',
+ 'icon' => 'icon.png',
+
+ 'defaults' => array('font' => array(), 'header_font' => array(), 'border' => array(), 'header_border' => array(), 'padding' => array()),
+ 'palette' => array(
+ 'background' => array(
+ 'label' => t('Background'),
+ 'default_value' => '#FFFFFF',
+ ),
+ 'text' => array(
+ 'label' => t('Text'),
+ 'default_value' => '#000000',
+ ),
+ 'border' => array(
+ 'label' => t('Border'),
+ 'default_value' => '#000000',
+ ),
+ 'header-background' => array(
+ 'label' => t('Header background'),
+ 'default_value' => '#FFFFFF',
+ ),
+ 'header-text' => array(
+ 'label' => t('Header text'),
+ 'default_value' => '#000000',
+ ),
+ 'header-border' => array(
+ 'label' => t('Header border'),
+ 'default_value' => '#000000',
+ ),
+ ),
+
+ // This just uses theme_panels_pane because all we need is the class.
+// 'theme' => 'pane_plain_box',
+// 'preview' => 'panels_stylizer_pane_preview',
+
+ 'settings form' => 'panels_pane_plain_box_style_settings',
+ 'settings form submit' => 'panels_pane_plain_box_style_settings_submit',
+ 'build' => 'panels_pane_plain_box_style_base_build',
+);
+
+function panels_pane_plain_box_style_settings(&$form, &$form_state) {
+ $form['font'] = array();
+ ctools_stylizer_font_selector_form($form['font'], $form_state, t('Font'), $form_state['settings']['font']);
+
+ $form['border'] = array();
+ ctools_stylizer_border_selector_form($form['border'], $form_state, t('Border'), $form_state['settings']['border']);
+
+ $form['padding'] = array();
+ ctools_stylizer_padding_selector_form($form['padding'], $form_state, t('Padding'), $form_state['settings']['padding']);
+
+ $form['header_font'] = array();
+ ctools_stylizer_font_selector_form($form['header_font'], $form_state, t('Header font'), $form_state['settings']['header_font']);
+
+ $form['header_border'] = array();
+ ctools_stylizer_border_selector_form($form['header_border'], $form_state, t('Header border'), $form_state['settings']['header_border']);
+
+}
+
+function panels_pane_plain_box_style_settings_submit(&$form, &$form_state) {
+ ctools_stylizer_font_selector_form_submit($form['font'], $form_state, $form_state['values']['font'], $form_state['settings']['font']);
+ ctools_stylizer_font_selector_form_submit($form['header_font'], $form_state, $form_state['values']['header_font'], $form_state['settings']['header_font']);
+
+ ctools_stylizer_border_selector_form_submit($form['header_border'], $form_state, $form_state['values']['header_border'], $form_state['settings']['header_border']);
+ ctools_stylizer_border_selector_form_submit($form['border'], $form_state, $form_state['values']['border'], $form_state['settings']['border']);
+
+ ctools_stylizer_padding_selector_form_submit($form['padding'], $form_state, $form_state['values']['padding'], $form_state['settings']['padding']);
+}
+
+function panels_pane_plain_box_style_base_build($plugin, $settings, &$css, $replacements) {
+ ctools_stylizer_font_apply_style($css, '%style', $settings['font']);
+ ctools_stylizer_border_apply_style($css, '%style', $settings['border'], '%border');
+
+ ctools_stylizer_font_apply_style($css, '%style .pane-title', $settings['header_font']);
+ ctools_stylizer_border_apply_style($css, '%style .pane-title', $settings['header_border'], '%header-border', 'bottom');
+
+ ctools_stylizer_padding_apply_style($css, '%style .pane-title, %style .pane-content', $settings['padding']);
+}
diff --git a/plugins/style_bases/pane/pane_rounded_shadow/box-color.png b/plugins/style_bases/pane/pane_rounded_shadow/box-color.png
new file mode 100644
index 0000000..b67fd24
--- /dev/null
+++ b/plugins/style_bases/pane/pane_rounded_shadow/box-color.png
Binary files differ
diff --git a/plugins/style_bases/pane/pane_rounded_shadow/box-shadow.png b/plugins/style_bases/pane/pane_rounded_shadow/box-shadow.png
new file mode 100644
index 0000000..01f385d
--- /dev/null
+++ b/plugins/style_bases/pane/pane_rounded_shadow/box-shadow.png
Binary files differ
diff --git a/plugins/style_bases/pane/pane_rounded_shadow/icon.png b/plugins/style_bases/pane/pane_rounded_shadow/icon.png
new file mode 100644
index 0000000..c3a4572
--- /dev/null
+++ b/plugins/style_bases/pane/pane_rounded_shadow/icon.png
Binary files differ
diff --git a/plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.css b/plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.css
new file mode 100644
index 0000000..e8ba82a
--- /dev/null
+++ b/plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.css
@@ -0,0 +1,97 @@
+.rounded-shadow-top-edge, .rounded-shadow-bottom-edge, .rounded-shadow-left-edge, .rounded-shadow-right-edge, .rounded-shadow-wrap-corner {
+ position: relative;
+ /* hasLayout -1 ? For IE only */
+ zoom: 1;
+}
+%style {
+ padding-top: 10px;
+ margin-bottom: 30px;
+ color: %text;
+}
+
+%style h2 {
+ color: %header-text;
+}
+
+%style .rounded-shadow-background {
+ margin: 10px;
+ background: %background url(rounded-shadow-background.png) repeat;
+}
+
+%style .rounded-shadow-wrap-corner {
+ margin: -10px;
+}
+
+%style .rounded-shadow-top-edge {
+ top: -10px;
+ background: url(rounded-shadow-top-edge.png) repeat-x 0 top;
+ font-size: 1px;
+}
+
+%style .rounded-shadow-bottom-edge {
+ bottom: -10px;
+ background: url(rounded-shadow-bottom-edge.png) repeat-x 0 bottom;
+ font-size: 1px;
+}
+
+%style .rounded-shadow-left-edge {
+ background: url(rounded-shadow-left-edge.png) repeat-y 0 0;
+}
+
+%style .rounded-shadow-right-edge {
+ background: url(rounded-shadow-right-edge.png) repeat-y right 0;
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-top-edge,
+%style .rounded-shadow-wrap-corner .rounded-shadow-bottom-edge {
+ height: 19px;
+ margin: -10px 19px;
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-left,
+%style .rounded-shadow-wrap-corner .rounded-shadow-right {
+ position: absolute;
+ top: 0;
+ height: 19px;
+ width: 19px;
+ margin: 0 -19px;
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-left {
+ background-image: url(rounded-shadow-top-left-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-right {
+ right: 0;
+ background-image: url(rounded-shadow-top-right-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-bottom-edge .rounded-shadow-left {
+ background-image: url(rounded-shadow-bottom-left-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-bottom-edge .rounded-shadow-right {
+ right: 0;
+ background-image: url(rounded-shadow-bottom-right-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-right-edge {
+ padding: 3px 19px;
+}
+
+%style .panel-pane {
+ position: relative; top: -6px;
+}
+
+/*
+%style div.admin-links {
+ margin-top: -19px;
+ margin-left: -12px;
+}
+
+%style .panel-separator {
+ background: url(rounded-shadow-bottom-edge.png) repeat-x 0 center;
+ font-size: 1px;
+ height: 30px;
+}
+*/
diff --git a/plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.tpl.php b/plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.tpl.php
new file mode 100644
index 0000000..c2560cf
--- /dev/null
+++ b/plugins/style_bases/pane/pane_rounded_shadow/pane-rounded-shadow.tpl.php
@@ -0,0 +1,32 @@
+<?php
+// $Id$
+/**
+ * @file
+ *
+ * Display the box for rounded corners.
+ *
+ * - $output: The content of the box.
+ * - $classes: The classes that must be applied to the top divs.
+ * - $pane: The pane being rendered
+ * - $display: The display being rendered
+ * - $content: The content being rendered (will be already in $output)
+ */
+?>
+<div class="rounded-shadow <?php print $classes ?>">
+ <div class="rounded-shadow-background">
+ <div class="rounded-shadow-wrap-corner">
+ <div class="rounded-shadow-top-edge">
+ <div class="rounded-shadow-left"></div>
+ <div class="rounded-shadow-right"></div>
+ </div>
+ <div class="rounded-shadow-left-edge">
+ <div class="rounded-shadow-right-edge clear-block">
+ <?php print $output; ?>
+ </div>
+ </div>
+ <div class="rounded-shadow-bottom-edge">
+ <div class="rounded-shadow-left"></div><div class="rounded-shadow-right"></div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/plugins/style_bases/pane/pane_rounded_shadow/pane_rounded_shadow.inc b/plugins/style_bases/pane/pane_rounded_shadow/pane_rounded_shadow.inc
new file mode 100644
index 0000000..c3d8f82
--- /dev/null
+++ b/plugins/style_bases/pane/pane_rounded_shadow/pane_rounded_shadow.inc
@@ -0,0 +1,100 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Definition of the style base for the rounded shadow box.
+ *
+ * This box is colorable, has rounded corners and a drop shadow.
+ */
+
+$plugin = array(
+ 'category' => t('Basic styles'),
+ 'title' => t('Rounded shadow box'),
+ 'module' => 'panels',
+ 'type' => 'pane',
+ 'css' => 'pane-rounded-shadow.css',
+ 'icon' => 'icon.png',
+ 'defaults' => array('header_font' => array(), 'text_font' => array(), 'padding' => array(), 'header_border' => array()),
+ 'palette' => array(
+ 'background' => array(
+ 'label' => t('Background'),
+ 'default_value' => '#FFFFFF',
+ ),
+ 'text' => array(
+ 'label' => t('Text'),
+ 'default_value' => '#000000',
+ ),
+ 'header-text' => array(
+ 'label' => t('Header text'),
+ 'default_value' => '#000000',
+ ),
+ 'header-border' => array(
+ 'label' => t('Header border'),
+ 'default_value' => '#000000',
+ ),
+ ),
+ 'actions' => array(
+ array('load', 'box', 'box-color.png'),
+ array('colorize', 'background'),
+ array('load', 'shadow', 'box-shadow.png'),
+ array('merge_from', 'box'),
+ array('slice', 'rounded-shadow-top-left-corner.png', 17, 49, 19, 19),
+ array('slice', 'rounded-shadow-top-right-corner.png', 473, 49, 19, 19),
+ array('slice', 'rounded-shadow-bottom-left-corner.png', 17, 442, 19, 19),
+ array('slice', 'rounded-shadow-bottom-right-corner.png', 473, 442, 19, 19),
+ array('slice', 'rounded-shadow-left-edge.png', 17, 60, 10, 10),
+ array('slice', 'rounded-shadow-right-edge.png', 474, 60, 18, 10),
+ array('slice', 'rounded-shadow-top-edge.png', 28, 49, 10, 10),
+ array('slice', 'rounded-shadow-bottom-edge.png', 28, 443, 10, 18),
+ array('slice', 'rounded-shadow-background.png', 150, 150, 1, 1),
+ ),
+
+ 'theme' => 'pane_rounded_shadow',
+ 'build' => 'panels_rounded_shadow_style_base_build',
+// 'preview' => 'panels_stylizer_pane_preview',
+
+ 'settings form' => 'panels_pane_rounded_shadow_style_settings',
+ 'settings form submit' => 'panels_pane_rounded_shadow_style_settings_submit',
+ 'build' => 'panels_pane_rounded_shadow_style_base_build',
+);
+
+function template_preprocess_pane_rounded_shadow(&$vars) {
+ $vars['classes'] = $vars['content']->css_class;
+ $vars['content']->css_class = '';
+
+ $vars['output'] = theme('panels_pane', $vars['content'], $vars['pane'], $vars['display']);
+}
+
+function panels_pane_rounded_shadow_style_settings(&$form, &$form_state) {
+ $form['header_font'] = array();
+ ctools_stylizer_font_selector_form($form['header_font'], $form_state, t('Header font'), $form_state['settings']['header_font']);
+
+ $form['header_border'] = array();
+ ctools_stylizer_border_selector_form($form['header_border'], $form_state, t('Header border'), $form_state['settings']['header_border']);
+
+ $form['text_font'] = array();
+ ctools_stylizer_font_selector_form($form['text_font'], $form_state, t('Text font'), $form_state['settings']['text_font']);
+
+ $form['padding'] = array();
+ ctools_stylizer_padding_selector_form($form['padding'], $form_state, t('Padding'), $form_state['settings']['padding']);
+}
+
+function panels_pane_rounded_shadow_style_settings_submit(&$form, &$form_state) {
+ ctools_stylizer_font_selector_form_submit($form['header_font'], $form_state, $form_state['values']['header_font'], $form_state['settings']['header_font']);
+
+ ctools_stylizer_font_selector_form_submit($form['text_font'], $form_state, $form_state['values']['text_font'], $form_state['settings']['text_font']);
+
+ ctools_stylizer_padding_selector_form_submit($form['padding'], $form_state, $form_state['values']['padding'], $form_state['settings']['padding']);
+
+ ctools_stylizer_border_selector_form_submit($form['header_border'], $form_state, $form_state['values']['header_border'], $form_state['settings']['header_border']);
+
+}
+
+function panels_pane_rounded_shadow_style_base_build($plugin, $settings, &$css, $replacements) {
+ ctools_stylizer_font_apply_style($css, '%style .pane-title', $settings['header_font']);
+ ctools_stylizer_border_apply_style($css, '%style .pane-title', $settings['header_border'], '%header-border', 'bottom');
+ ctools_stylizer_font_apply_style($css, '%style .pane-content', $settings['text_font']);
+ ctools_stylizer_padding_apply_style($css, '%style', $settings['padding']);
+}
+
diff --git a/plugins/style_bases/region/region_plain_box/icon.png b/plugins/style_bases/region/region_plain_box/icon.png
new file mode 100644
index 0000000..436355b
--- /dev/null
+++ b/plugins/style_bases/region/region_plain_box/icon.png
Binary files differ
diff --git a/plugins/style_bases/region/region_plain_box/region-plain-box.css b/plugins/style_bases/region/region_plain_box/region-plain-box.css
new file mode 100644
index 0000000..e48b6d5
--- /dev/null
+++ b/plugins/style_bases/region/region_plain_box/region-plain-box.css
@@ -0,0 +1,6 @@
+%style {
+ padding-top: 10px;
+ margin-bottom: 10px;
+ color: %text;
+ background-color: %background;
+}
diff --git a/plugins/style_bases/region/region_plain_box/region-plain-box.tpl.php b/plugins/style_bases/region/region_plain_box/region-plain-box.tpl.php
new file mode 100644
index 0000000..2c35463
--- /dev/null
+++ b/plugins/style_bases/region/region_plain_box/region-plain-box.tpl.php
@@ -0,0 +1,29 @@
+<?php
+// $Id$
+/**
+ * @file
+ *
+ * Display the box for rounded corners.
+ *
+ * - $content: The content of the box.
+ * - $classes: The classes that must be applied to the top divs.
+ */
+?>
+<div class="rounded-shadow <?php print $classes ?>">
+ <div class="rounded-shadow-background">
+ <div class="rounded-shadow-wrap-corner">
+ <div class="rounded-shadow-top-edge">
+ <div class="rounded-shadow-left"></div>
+ <div class="rounded-shadow-right"></div>
+ </div>
+ <div class="rounded-shadow-left-edge">
+ <div class="rounded-shadow-right-edge clear-block">
+ <?php print $content; ?>
+ </div>
+ </div>
+ <div class="rounded-shadow-bottom-edge">
+ <div class="rounded-shadow-left"></div><div class="rounded-shadow-right"></div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/plugins/style_bases/region/region_plain_box/region_plain_box.inc b/plugins/style_bases/region/region_plain_box/region_plain_box.inc
new file mode 100644
index 0000000..2bdf0e5
--- /dev/null
+++ b/plugins/style_bases/region/region_plain_box/region_plain_box.inc
@@ -0,0 +1,66 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Definition of the style base for the rounded shadow box.
+ *
+ * This box is colorable, has rounded corners and a drop shadow.
+ */
+
+$plugin = array(
+ 'category' => t('Basic styles'),
+ 'title' => t('Plain'),
+ 'description' => t('A plain box with an optional border. You may set the color of the text and the border.'),
+ 'module' => 'panels',
+ 'type' => 'region',
+
+ 'css' => 'region-plain-box.css',
+ 'icon' => 'icon.png',
+
+ 'defaults' => array('font' => array(), 'border' => array(), 'padding' => array()),
+ 'palette' => array(
+ 'background' => array(
+ 'label' => t('Background'),
+ 'default_value' => '#FFFFFF',
+ ),
+ 'text' => array(
+ 'label' => t('Text'),
+ 'default_value' => '#000000',
+ ),
+ 'border' => array(
+ 'label' => t('Border'),
+ 'default_value' => '#000000',
+ ),
+ ),
+
+ 'theme' => 'region_plain_box',
+// 'preview' => 'panels_stylizer_region_preview',
+
+ 'settings form' => 'panels_region_plain_box_style_settings',
+ 'settings form submit' => 'panels_region_plain_box_style_settings_submit',
+ 'build' => 'panels_region_plain_box_style_base_build',
+);
+
+function panels_region_plain_box_style_settings(&$form, &$form_state) {
+ $form['font'] = array();
+ ctools_stylizer_font_selector_form($form['font'], $form_state, t('Font'), $form_state['settings']['font']);
+
+ $form['border'] = array();
+ ctools_stylizer_border_selector_form($form['border'], $form_state, t('Border'), $form_state['settings']['border']);
+
+ $form['padding'] = array();
+ ctools_stylizer_padding_selector_form($form['padding'], $form_state, t('Padding'), $form_state['settings']['padding']);
+}
+
+function panels_region_plain_box_style_settings_submit(&$form, &$form_state) {
+ ctools_stylizer_font_selector_form_submit($form['font'], $form_state, $form_state['values']['font'], $form_state['settings']['font']);
+ ctools_stylizer_border_selector_form_submit($form['border'], $form_state, $form_state['values']['border'], $form_state['settings']['border']);
+ ctools_stylizer_padding_selector_form_submit($form['padding'], $form_state, $form_state['values']['padding'], $form_state['settings']['padding']);
+}
+
+function panels_region_plain_box_style_base_build($plugin, $settings, &$css, $replacements) {
+ ctools_stylizer_font_apply_style($css, '%style', $settings['font']);
+ ctools_stylizer_border_apply_style($css, '%style', $settings['border'], '%border');
+ ctools_stylizer_padding_apply_style($css, '%style', $settings['padding']);
+}
diff --git a/plugins/style_bases/region/region_rounded_shadow/box-color.png b/plugins/style_bases/region/region_rounded_shadow/box-color.png
new file mode 100644
index 0000000..b67fd24
--- /dev/null
+++ b/plugins/style_bases/region/region_rounded_shadow/box-color.png
Binary files differ
diff --git a/plugins/style_bases/region/region_rounded_shadow/box-shadow.png b/plugins/style_bases/region/region_rounded_shadow/box-shadow.png
new file mode 100644
index 0000000..01f385d
--- /dev/null
+++ b/plugins/style_bases/region/region_rounded_shadow/box-shadow.png
Binary files differ
diff --git a/plugins/style_bases/region/region_rounded_shadow/icon.png b/plugins/style_bases/region/region_rounded_shadow/icon.png
new file mode 100644
index 0000000..c3a4572
--- /dev/null
+++ b/plugins/style_bases/region/region_rounded_shadow/icon.png
Binary files differ
diff --git a/plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.css b/plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.css
new file mode 100644
index 0000000..a782efa
--- /dev/null
+++ b/plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.css
@@ -0,0 +1,89 @@
+.rounded-shadow-top-edge, .rounded-shadow-bottom-edge, .rounded-shadow-left-edge, .rounded-shadow-right-edge, .rounded-shadow-wrap-corner {
+ position: relative;
+ /* hasLayout -1 ? For IE only */
+ zoom: 1;
+}
+%style {
+ padding-top: 10px;
+ margin-bottom: 30px;
+ color: %text;
+}
+
+%style .rounded-shadow-background {
+ margin: 10px;
+ background: %background url(rounded-shadow-background.png) repeat;
+}
+
+%style .rounded-shadow-wrap-corner {
+ margin: -10px;
+}
+
+%style .rounded-shadow-top-edge {
+ top: -10px;
+ background: url(rounded-shadow-top-edge.png) repeat-x 0 top;
+ font-size: 1px;
+}
+
+%style .rounded-shadow-bottom-edge {
+ bottom: -10px;
+ background: url(rounded-shadow-bottom-edge.png) repeat-x 0 bottom;
+ font-size: 1px;
+}
+
+%style .rounded-shadow-left-edge {
+ background: url(rounded-shadow-left-edge.png) repeat-y 0 0;
+}
+
+%style .rounded-shadow-right-edge {
+ background: url(rounded-shadow-right-edge.png) repeat-y right 0;
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-top-edge,
+%style .rounded-shadow-wrap-corner .rounded-shadow-bottom-edge {
+ height: 19px;
+ margin: -10px 19px;
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-left,
+%style .rounded-shadow-wrap-corner .rounded-shadow-right {
+ position: absolute;
+ top: 0;
+ height: 19px;
+ width: 19px;
+ margin: 0 -19px;
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-left {
+ background-image: url(rounded-shadow-top-left-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-right {
+ right: 0;
+ background-image: url(rounded-shadow-top-right-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-bottom-edge .rounded-shadow-left {
+ background-image: url(rounded-shadow-bottom-left-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-bottom-edge .rounded-shadow-right {
+ right: 0;
+ background-image: url(rounded-shadow-bottom-right-corner.png);
+}
+
+%style .rounded-shadow-wrap-corner .rounded-shadow-right-edge {
+ padding: 3px 19px;
+}
+
+/*
+%style div.admin-links {
+ margin-top: -19px;
+ margin-left: -12px;
+}
+
+%style .panel-separator {
+ background: url(rounded-shadow-bottom-edge.png) repeat-x 0 center;
+ font-size: 1px;
+ height: 30px;
+}
+*/
diff --git a/plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.tpl.php b/plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.tpl.php
new file mode 100644
index 0000000..2c35463
--- /dev/null
+++ b/plugins/style_bases/region/region_rounded_shadow/region-rounded-shadow.tpl.php
@@ -0,0 +1,29 @@
+<?php
+// $Id$
+/**
+ * @file
+ *
+ * Display the box for rounded corners.
+ *
+ * - $content: The content of the box.
+ * - $classes: The classes that must be applied to the top divs.
+ */
+?>
+<div class="rounded-shadow <?php print $classes ?>">
+ <div class="rounded-shadow-background">
+ <div class="rounded-shadow-wrap-corner">
+ <div class="rounded-shadow-top-edge">
+ <div class="rounded-shadow-left"></div>
+ <div class="rounded-shadow-right"></div>
+ </div>
+ <div class="rounded-shadow-left-edge">
+ <div class="rounded-shadow-right-edge clear-block">
+ <?php print $content; ?>
+ </div>
+ </div>
+ <div class="rounded-shadow-bottom-edge">
+ <div class="rounded-shadow-left"></div><div class="rounded-shadow-right"></div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/plugins/style_bases/region/region_rounded_shadow/region_rounded_shadow.inc b/plugins/style_bases/region/region_rounded_shadow/region_rounded_shadow.inc
new file mode 100644
index 0000000..204321c
--- /dev/null
+++ b/plugins/style_bases/region/region_rounded_shadow/region_rounded_shadow.inc
@@ -0,0 +1,72 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Definition of the style base for the rounded shadow box.
+ *
+ * This box is colorable, has rounded corners and a drop shadow.
+ */
+
+$plugin = array(
+ 'category' => t('Basic styles'),
+ 'title' => t('Rounded shadow box'),
+ 'module' => 'panels',
+ 'type' => 'region',
+ 'css' => 'region-rounded-shadow.css',
+ 'icon' => 'icon.png',
+ 'defaults' => array('font' => array(), 'padding' => array()),
+ 'palette' => array(
+ 'background' => array(
+ 'label' => t('Background'),
+ 'default_value' => '#FFFFFF',
+ ),
+ 'text' => array(
+ 'label' => t('Text color'),
+ 'default_value' => '#000000',
+ ),
+ ),
+ 'actions' => array(
+ array('load', 'box', 'box-color.png'),
+ array('colorize', 'background'),
+ array('load', 'shadow', 'box-shadow.png'),
+ array('merge_from', 'box'),
+ array('slice', 'rounded-shadow-top-left-corner.png', 17, 49, 19, 19),
+ array('slice', 'rounded-shadow-top-right-corner.png', 473, 49, 19, 19),
+ array('slice', 'rounded-shadow-bottom-left-corner.png', 17, 442, 19, 19),
+ array('slice', 'rounded-shadow-bottom-right-corner.png', 473, 442, 19, 19),
+ array('slice', 'rounded-shadow-left-edge.png', 17, 60, 10, 10),
+ array('slice', 'rounded-shadow-right-edge.png', 474, 60, 18, 10),
+ array('slice', 'rounded-shadow-top-edge.png', 28, 49, 10, 10),
+ array('slice', 'rounded-shadow-bottom-edge.png', 28, 443, 10, 18),
+ array('slice', 'rounded-shadow-background.png', 150, 150, 1, 1),
+ ),
+
+ 'theme' => 'region_rounded_shadow',
+
+ 'build' => 'panels_rounded_shadow_style_base_build',
+// 'preview' => 'panels_stylizer_region_preview',
+
+ 'settings form' => 'panels_region_rounded_shadow_style_settings',
+ 'settings form submit' => 'panels_region_rounded_shadow_style_settings_submit',
+ 'build' => 'panels_region_rounded_shadow_style_base_build',
+);
+
+function panels_region_rounded_shadow_style_settings(&$form, &$form_state) {
+ $form['font'] = array();
+ ctools_stylizer_font_selector_form($form['font'], $form_state, t('Font'), $form_state['settings']['font']);
+
+ $form['padding'] = array();
+ ctools_stylizer_padding_selector_form($form['padding'], $form_state, t('Padding'), $form_state['settings']['padding']);
+}
+
+function panels_region_rounded_shadow_style_settings_submit(&$form, &$form_state) {
+ ctools_stylizer_font_selector_form_submit($form['font'], $form_state, $form_state['values']['font'], $form_state['settings']['font']);
+ ctools_stylizer_padding_selector_form_submit($form['padding'], $form_state, $form_state['values']['padding'], $form_state['settings']['padding']);
+}
+
+function panels_region_rounded_shadow_style_base_build($plugin, $settings, &$css, $replacements) {
+ ctools_stylizer_font_apply_style($css, '%style', $settings['font']);
+ ctools_stylizer_padding_apply_style($css, '%style', $settings['padding']);
+}
+
diff --git a/plugins/styles/block.inc b/plugins/styles/block.inc
index 28274b1..af5a066 100644
--- a/plugins/styles/block.inc
+++ b/plugins/styles/block.inc
@@ -11,6 +11,7 @@ $plugin = array(
'title' => t('System block'),
'description' => t('Display the pane as a system block; this is more restrictive than the default.'),
'render pane' => 'panels_block_style_render_pane',
+ 'weight' => -10,
);
/**
@@ -35,6 +36,9 @@ function theme_panels_block_style_render_pane($content, $pane, $display) {
$content->delta = $content->subtype;
}
+ // If using per pane classes, $block->css_class will need to be added in your
+ // preprocess or template, along with any other Panels specific field you
+ // might want to utilize.
return theme('block', $content);
}
diff --git a/plugins/styles/naked.inc b/plugins/styles/naked.inc
index 229ec99..58746c5 100644
--- a/plugins/styles/naked.inc
+++ b/plugins/styles/naked.inc
@@ -11,6 +11,7 @@ $plugin = array(
'title' => t('No markup at all'),
'description' => t('Display the pane with no markup, not even a title.'),
'render pane' => 'panels_naked_style_render_pane',
+ 'weight' => -5,
);
/**
diff --git a/plugins/styles/stylizer.inc b/plugins/styles/stylizer.inc
new file mode 100644
index 0000000..a299787
--- /dev/null
+++ b/plugins/styles/stylizer.inc
@@ -0,0 +1,346 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Definition of the 'stylizer' panel style.
+ */
+
+// Plugin definition
+$plugin = array(
+ 'title' => t('Custom style'),
+ 'weight' => -10,
+ 'description' => t('Allows choice of a stylizer style'),
+
+ 'render pane' => 'panels_stylizer_stylizer_style_render_pane',
+ 'pane settings form' => 'panels_stylizer_stylizer_style_settings_form',
+
+ 'render region' => 'panels_stylizer_stylizer_style_render_region',
+ 'settings form' => 'panels_stylizer_stylizer_style_settings_form',
+
+ // We offer substyles so provide callbacks to do so.
+ 'get child' => 'panels_stylizer_get_substyle',
+ 'get children' => 'panels_stylizer_get_substyles',
+
+ // Set up an AJAX callback for the style
+ 'ajax' => array(
+ 'custom' => 'panels_stylizer_pane_add_style',
+ ),
+// 'settings validate' => 'panels_stylizer_stylizer_style_settings_validate',
+);
+
+/**
+ * Merge the main stylizer plugin with a style to create a sub plugin.
+ *
+ * This is used for both panels_stylizer_get_substyle and
+ * panels_stylizer_get_substyles.
+ */
+function panels_stylizer_merge_plugin($plugin, $style) {
+ $plugin['name'] = 'stylizer:' . $style->name;
+ $plugin['title'] = check_plain($style->admin_title);
+ $plugin['description'] = check_plain($style->admin_description);
+ $plugin['style'] = $style;
+ $plugin['weight'] = 0;
+
+ ctools_include('stylizer');
+ $base = ctools_get_style_base($style->settings['style_base']);
+ if ($base['type'] == 'pane') {
+ unset($plugin['render region']);
+ }
+ else {
+ unset($plugin['render pane']);
+ }
+
+ unset($plugin['settings form']);
+ unset($plugin['pane settings form']);
+ return $plugin;
+}
+
+/**
+ * Callback to provide a single stored stylizer style.
+ */
+function panels_stylizer_get_substyle($plugin, $style_name, $substyle_name) {
+ // Do not worry about caching; Panels is handling that for us.
+ ctools_include('export');
+ $item = ctools_export_crud_load('stylizer', $substyle_name);
+ if ($item) {
+ return panels_stylizer_merge_plugin($plugin, $item);
+ }
+}
+
+/**
+ * Callback to provide all stored stylizer styles.
+ */
+function panels_stylizer_get_substyles($plugin, $style_name) {
+ $styles[$style_name] = $plugin;
+ ctools_include('export');
+ ctools_include('stylizer');
+ $items = ctools_export_crud_load_all('stylizer');
+ foreach ($items as $name => $item) {
+ $base = ctools_get_style_base($item->settings['style_base']);
+ if ($base && $base['module'] == 'panels') {
+ $styles['stylizer:' . $name] = panels_stylizer_merge_plugin($plugin, $item);
+ }
+ }
+
+ return $styles;
+}
+
+function _panels_stylizer_get_style($plugin, $style_settings) {
+ if (!empty($plugin['style'])) {
+ return $plugin['style']->settings;
+ }
+ else if ($style_settings['style'] == '$') {
+ return $style_settings['settings'];
+ }
+ else {
+ ctools_include('export');
+ $style = ctools_export_crud_load('stylizer', $style_settings['styl