diff --git a/CHANGELOG.txt b/CHANGELOG.txt deleted file mode 100644 index 4d6a68f..0000000 --- a/CHANGELOG.txt +++ /dev/null @@ -1,39 +0,0 @@ -CHANGELOG for Panels 3.0 for Drupal 7 -Panels 7.x-3.0-dev -================== -#1025716: Panels fields broken in last update. -#1056464 by EclipseGc: Fix broken delete statement in mini panel delete. -#954324 by EclipseGc: Fix broken delete statement in mini panel uninstall. -#1008120: "classes" not passing through to rounded shadow pane stylizer style, preventing style from working. - -Panels 7.x-3.0-alpha2 (10-Jan-2011) -===================== - -#920266 by dereine: Typo in cache.inc caused wsods in rare circumstances. -#879482 by mvc: Ensure Panels and CTools module files are loaded during update. Apparently could cause WSOD if not. -#917614 by jskulski: IPE broken in IE. -#906520: Improve CSS for rounded shadow boxes on IE7. -#932632 by mikeytown2: Fix notice in .install file. -#927840: Add clear-block to dashboard HTML to ensure themes do not do weird things to it. -#869766: Fix occasional problem with flexible layout pushing the entire layout to the left in certain fixed-width only configurations. -#949310: E_STRICT violation on declaration of render_pane() method of display renderers. -#940002: Custom style modal was broken. -#953484: Panes were not properly using classes array. -#941532: panel nodes had some serious problems do to hook_node_*. -#954324: Mini panels failed during uninstall. -#827628: "Add content" dialog could lose content with the same title as other content. -Fix the naked style to support content that needs to be rendered. -#958072: Fix panels_node_node_access to not throw warnings on menu access tests. -#965286: Panel node update was trying to use db_insert() instead of db_update(). -#964334: Panels breaks views' row styles with the panels fields style. -#941802: Fix radio layout butotn. -#980696 by das-peter: Update calls to drupal_set_html_head() to D7. -#961662 by Nick Lewis: Account for pager info in simple caching. -#980870 by das-peter: CSS handling during caching broken. -#970076: Remove old hook_update functions. -#978768 by linclark: Fix notice with panel fields. -#977296: Regions with _ such as with two column bricks would not save content. -#987902: Fix flexible layout splitter resize brokenness. -#967734 by das-peter and intoxination: Upgrade node_get_types() in wizard. -#1020824: Finish fixing node template page wizard. -Fix landing page wizard. diff --git a/KNOWN_ISSUES.txt b/KNOWN_ISSUES.txt deleted file mode 100644 index 6696367..0000000 --- a/KNOWN_ISSUES.txt +++ /dev/null @@ -1,91 +0,0 @@ - -Known Issue http://drupal.org/node/191771 - 'Node' panes can have two titles or have two title areas. - Cause: - Content that comes into a pane is already formatted, and this happens - in theme('node'). theme('node') assumes it will be printing a title - most of the time. However, Panels wants the titles of panes to be - consistent, so it removes the title from the node to prevent your - node.tpl.php from printing it. The result is often an empty h2 which - has odd effects. - Solution: - Add an if statement to your node.tpl.php to prevent printing that h2 - if $node->title is empty. - -Known Issue http://drupal.org/node/186454 - Internet Explorer is really bad about making the rightmost panel - fall beneath the others. - Cause: - Internet explorer calculates margins and padding differntly from - everyone else, and this makes it entirely too easy for widths - to add up to greater than the amount of allotted space, despite - using percentage widths. - Solution: - There are two solutions to this problem: - 1) In your theme, try to eliminate padding from the the
- that directly contains your content; you can do this by - adding an empty
inside it that surrounds the content - and very specifically is set to margin: 0 and padding: 0 - - 2) if that doesn't work, override the widths of the panel-panel - divs and reduce them by 1 or 2%; usually this will give IE - enough space to quit pushing things around. - -Known Issue http://drupal.org/node/154351 - TinyMCE, FCKEditor and other wysiwyg editors really blow up on Panels - content editing. - Cause: - The modal dialogs that Panels uses are very particular about javascript - and these editors are too much for them. Also, these editors get - cranky about complicated forms with several text areas. - Solution: - Disable these editors on all of your panels admin pages. The important - URLs are admin/panels/* and panels/ajax/*. More details instructions - may follow if someone familiar with these systems submits a patch at - the above drupal.org URL. - -Known Issue http://drupal.org/node/180650 - The rounded corners style shows up as just a small graphic rather than - a full box around the panels as it shoujld. - Cause: - The rounded corners CSS relies on the ID for the panel, but the ID is - optional. - Solution: - Make sure your panel has an ID of some sort. With mini panels there is - no easy workaround as mini panels currently do not have IDs of their - own. - -Known Issue http://drupal.org/node/165745 - You see a message similar to this: - Table 'drupal.panels_info' doesn't exist query: SELECT * FROM panels_info - WHERE path = 'front_page_new' in... - - The important piece of information is 'panels_info'. - Cause: - The Meta Tags module (also known as nodewords.module) directly reads the - the panels tables and modifies its forms to add the tags. Unfortunately - for this module, Panels has changed *greatly* in the leap from 1.0 to - 2.0 and the tables aren't the same. However, the nodewords module doesn't - yet know this. Look in the nodewords issue queue for panels patches and - you should find something. - -Known Issue http://drupal.org/node/153399 - The drag and drop content UI doesn't seem to work at all under Safari. - - Cause: - Safari 2 has some serious problems with the javascript code. - Solution: - Upgrade to Safari 3 if possible. If not, use an an alternative browser - such as Firefox or Opera. - -Known Issue http://drupal.org/node/207859 - When using the secure pages module, the Panels administrative UI gives - unhelpful "An error occurred" popups when trying to add or edit content. - - Cause: - The secure pages module tries to move the entire administrative section - of the site to HTTPS, but Panels' AJAX calls are using a path that - secure pages doesn't know about. When trying to make non-secure ajax calls - from a secure page, the browser denies the call. - Solution: - The solution is to simply add panels/* to your Secure Pages configuration. \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index 2181b78..0000000 --- a/README.txt +++ /dev/null @@ -1,6 +0,0 @@ - -Welcome to Panels 3. - -A little documentation should go here, but Panels 3 is alsoi a beast - you're -best off checking the online handbook on Drupal.org, or this issue: -http://drupal.org/node/887560. diff --git a/UPGRADE.txt b/UPGRADE.txt deleted file mode 100644 index d1a42f2..0000000 --- a/UPGRADE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Upgrading from Panels-6.x-3.x to Panels-7.x-3.x - - - Style and layout plugins may no longer be registered by a central hook. - Only the plugin directories method may be used. - - - Layout 'panels function' is now 'regions function'. - - - Layout 'panels' key is now 'regions'. - - - panels_get_pane_title() deprecated. - - - panels_plugin_get_function() deprecated. - - - panels_required_context removed. These were deprecated long ago and - existed only to prevent crashes. - - - panels_optional_context removed. - - - $renderer->plugins['layout']['panels'] changed to $renderer->plugin['layout']['regions'] - - - display_renderer class is now in 'renderer', not 'handler'. - diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..fc1802f --- /dev/null +++ b/composer.json @@ -0,0 +1,29 @@ +{ + "name": "drupal/panels", + "description": "Core Panels display functions; provides no external UI, at least one other Panels module should be enabled.", + "type": "drupal-module", + "homepage": "https://www.drupal.org/project/panels", + "authors": [ + { + "name": "Jakob Perry", + "homepage": "https://www.drupal.org/u/japerry" + }, + { + "name": "Samuel Mortenson", + "homepage": "https://www.drupal.org/u/samuel.mortenson" + }, + { + "name": "See other contributors", + "homepage":"https://www.drupal.org/node/74958/committers" + } + + ], + "support": { + "issues": "https://www.drupal.org/project/issues/panels", + "irc": "irc://irc.freenode.org/drupal-scotch", + "source": "http://git.drupal.org/project/panels.git" + }, + "license": "GPL-2.0+", + "minimum-stability": "dev", + "require": {} +} \ No newline at end of file diff --git a/css/panels-dashboard.css b/css/panels-dashboard.css deleted file mode 100644 index a7efeae..0000000 --- a/css/panels-dashboard.css +++ /dev/null @@ -1,62 +0,0 @@ - -.dashboard-entry .dashboard-link { - font-size: 120%; - font-weight: bold; -} - -.dashboard-entry .dashboard-icon img { - vertical-align: middle; -} -.dashboard-title { - font-weight: bold; - font-size: 140%; - margin-bottom: .5em; -} - -.dashboard-link form input { - margin: 0; -} - -.dashboard-link form select { - margin: 0; -} - -.dashboard-left { - width: 47%; - float: left; -} - -.dashboard-right { - margin-left: 2em; - width: 47%; - float: left; -} - -.dashboard-question { - margin-top: 14em; - padding: 1em; - text-align: center; -} - -.dashboard-content table { - margin: 0; - width: 100%; -} - -.dashboard-content { - padding: 0 1em; -} - -.panels-dashboard .links { - text-align: right; -} - -.dashboard-pages .page-manager-page-operations { - text-align: right; -} - -.dashboard-block { - padding-bottom: 1em; - border-bottom: 1px dotted #ddd; - margin-bottom: 1em; -} diff --git a/css/panels.css b/css/panels.css deleted file mode 100644 index 99552a9..0000000 --- a/css/panels.css +++ /dev/null @@ -1,50 +0,0 @@ - -div.panel-pane div.admin-links { - font-size: xx-small; - margin-right: 1em; -} - -div.panel-pane div.admin-links li a { - color: #ccc; -} - -div.panel-pane div.admin-links li { - padding-bottom: 2px; - background: white; - z-index: 201; -} - -div.panel-pane div.admin-links:hover a, -div.panel-pane div.admin-links-hover a { - color: #000; -} - -div.panel-pane div.admin-links a:before { - content: "["; -} - -div.panel-pane div.admin-links a:after { - content: "]"; -} - -div.panel-pane div.panel-hide { - display: none; -} - -/** For IE we add the class via js; for other browsers we rely on :hover **/ -div.panel-pane div.panel-hide-hover, -div.panel-pane:hover div.panel-hide { - display: block; - position: absolute; - z-index: 200; - margin-top: -1.5em; -} - -div.panel-pane div.node { - margin: 0; - padding: 0; -} - -div.panel-pane div.feed a { - float: right; -} diff --git a/css/panels_admin.css b/css/panels_admin.css deleted file mode 100644 index 78094e2..0000000 --- a/css/panels_admin.css +++ /dev/null @@ -1,165 +0,0 @@ - -.layout-link { - float: left; - padding: 1em; - width: 125px; - height: 160px; -} - -.layout-link img { - margin-left: auto; - margin-right: auto; -} - -/* general style for the layout-icon */ -.layout-icon .caption { - width: 90px; - margin-bottom: 1em; -} - -/* styles for the basic panel-%implementation% edit page */ -.layout-container, -.right-container { - float: right; - padding: 0 0 0 .5em; - margin: 0; - width: 48.5%; -} - -.info-container, -.left-container { - padding-right: .5em; - width: 48.5%; -} - -.right-container fieldset, -.left-container fieldset, -.layout-container fieldset { - margin-top: 0; -} - -.layout-container .form-item { - margin: 0; -} - -.layout-container .form-submit { - margin-top: 1em; -} - -.layout-container .layout-icon, -.left-container .layout-icon { - float: right; - margin-left: .5em; -} - -.content-list ol { - padding-left: 0; - list-style-position: inside; -} - -.content-list dt { - font-weight: bold; -} - -.content-list dd { - margin-left: 2em; -} - -/* styles for the choose layout page */ -.panels-layouts-checkboxes .form-checkboxes .form-item, -#panels-choose-layout .form-type-radio, -.panels-choose-layout .form-type-radio { - float: left; - margin-right: .5em; - width: 90px; -} - -.panels-layouts-checkboxes .form-checkboxes .form-item .layout-icon, -#panels-choose-layout .form-type-radio .form-item .layout-icon, -.panels-choose-layout .form-type-radio .form-item .layout-icon { - float: none; - height: 11em; - width: 90px; -} -.panels-layouts-checkboxes .form-checkboxes .option input, -#panels-choose-layout .form-type-radio input, -.panels-choose-layout .form-type-radio input { - width: 50px; - display: block; - text-align: center; -} - -.panels-layouts-checkboxes .form-submit, -#panels-choose-layout .form-submit { - clear: left; -} - -.panels-layouts-checkboxes .panels-layout-list label, -#panels-choose-layout .panels-layout-list label { - width: 300px; - float: left; - clear: left; - background: url(../images/go-right.png) right no-repeat; - margin-right: 20px; -} - -.panels-layouts-checkboxes .panels-layouts-category { - font-weight: bold; - width: 100%; - float: left; -} - -.panels-layouts-checkboxes .description { - clear: left; -} - -.change-layout-display .layout-icon { - float: left; -} - -.change-layout-display > img { - padding: 25px 25px 25px 0; - float: left; -} - -table .operation { - text-align: right; - padding-right: 6px; -} - -table .argument-operation input { - padding: 0; - margin: 0; - position: relative; - top: 3px; -} - -.panels-admin-view { - padding: 1em; - border: 1px dotted black; - margin-bottom: 1em; -} - -tr.changed td { - background-color: #FFFFDD !important; -} - -tr.changed td span.star { - font-weight: bold; - color: #E09010; -} - -td select { - margin: 0; - padding: 0; -} - -.panels-style-settings, -.panels-style-settings-box, -#panels-style-setting { - float: left; -} - -.panels-style-settings-box .form-item { - margin: 0 1em 0 0; -} diff --git a/css/panels_dnd.css b/css/panels_dnd.css deleted file mode 100644 index 530b359..0000000 --- a/css/panels_dnd.css +++ /dev/null @@ -1,670 +0,0 @@ - -#panels-dnd-main { - margin: 0.5em 0; -} - -#panels-dnd-main div.panel-region { - padding: 0 0 .5em 0; - border: 1px dashed #ddd; - background: #f8f8f8; - -webkit-border-radius: 0.333em; - -moz-border-radius: 0.333em; -} - -#panels-dnd-main div.panel-region h2.label { - color: #555; - text-shadow: #fff 1px 1px 1px; - text-align: center; - font-size: 13pt; - margin: 0 0 .5em 0; - padding-right: 16px; - vertical-align: middle; -} - -#panels-dnd-main div.panel-region .pane-add { - float: left; - margin: 2px; - background: #999; - border: 1px solid #fff; -} - -#panels-dnd-main div.panel-region .pane-add-link { - position: relative; - display: block; - width: 16px; - height: 16px; - float: left; - margin: 2px 2px 2px 4px; -} - -div.panels-set-title-hide .panels-set-title { - display: none !important; -} - -/* Add Icon */ -#panels-dnd-main div.panel-region .pane-add-link { -} -#panels-dnd-main div.panel-region .pane-add-link img { - display: none; -} -#panels-dnd-main div.panel-region .pane-add-link a.ctools-dropdown-image-link { - border: none; - width: 16px; - height: 18px; - float: left; - background: url('../images/sprite.png') no-repeat 0 -1178px; -/* background: url('../images/sprite.png') no-repeat -166px -582px; */ -} - -.panel-portlet { - padding: 0em; - background: #ffffff; - border: 1px solid #bbb; -} - -div.panels-set-title-hide .panel-pane-is-title { - border: 1px solid #bbb; -} - -.panel-pane-is-title { - border: 2px solid #777; -} - -/* Cog Icon */ -.panel-portlet .buttons a img { - display: none; - margin: 0; -} -.panel-portlet .buttons a.ctools-dropdown-image-link { - border: none; - width: 16px; - height: 16px; - margin: 0 5px 0 0; - float: none; - display: block; - background: url('../images/sprite.png') no-repeat 0 -1178px; -} - -#panels-dnd-main .panel-pane, -#panels-dnd-main .helperclass { - margin: .5em; -} - -#panels-dnd-main-form .inline-icon-help { - vertical-align: middle; - margin: 2px 1px; -} - -.panel-pane.hidden-pane { - background: #f8f8f8; -} - -.panel-portlet .pane-content { - margin: .5em 0 .5em 0; - padding: 0 .25em 0 .25em; - display: none; /* initially hidden */ -} - -.panel-portlet .grab-title { - width: 100%; - height: 20px; - margin: 0 0 0.5em 0; - overflow: hidden; - background: #b3b3b3; - color: #fff; - text-shadow: #555 1px 1px 1px; - border-color: #999; - font-weight: bold; -} - -.panel-portlet .grabber { - cursor: move; - background: #b3b3b3 url('../images/bg-shade-medium.png') repeat-x 0 100%; -} - -.panel-portlet.hidden-pane .grab-title { - background-color: #888; -} - -.panel-portlet .changed div.grab-title { - background-color: #FFFFDD !important; - border-bottom: 1px solid #3D9CD7 !important; - color: black !important; -} - -.panel-portlet .changed.hidden-pane div.grab-title { - background-color: #B4B488 !important; - border-bottom: 1px solid #3D9CD7 !important; -} - -.panel-portlet .changed div.grab-title span.star { - font-weight: bold; - color: #E09010; -} - -.panel-portlet .grabber:hover { - color: #fff; - background-color: #2F78A5; -} - -.panel-portlet.hidden-pane .grab-title:hover { - background-color: #666; -} - -.panel-portlet .grabber:active { - background-color: red; -} - -.panel-portlet .grabber:hover, -.panel-portlet .grabber:active { - background: #858585 url('../images/bg-shade-dark.png') repeat-x 0 100%; - color: #fff; - text-shadow: #333 1px 1px 1px; - border-color: #858585; -} - -.panel-portlet .grab-title .text { - margin-left: 3px; - font-size: 90%; - line-height: 20px; -} - -.panel-portlet .buttons { - float: right; - padding: 0; - margin: 0; -} - -.panel-portlet .buttons input { - margin: 0; - padding: 0; - display: inline; -} - -.panel-portlet .buttons a img { - margin: 2px 1px; -} - -.panel-portlet .pane-title { - font-size:110%; - cursor: pointer; -} - -.panel-portlet .panel-pane-collapsible { - margin: 0; - padding: 0; -} - -.panel-portlet .toggle { - float: left; - width: 21px; - height: 21px; - cursor: pointer; - background: url('../images/sky.png') no-repeat 6px -245px; -} - -.panel-portlet .toggle-collapsed { - background: url('../images/sky.png') no-repeat 6px -1021px; -} - -/* CSS to guide a user to a place to drop */ -#panels-dnd-main .helperclass { - border: 1px dashed red; -} - -#panels-dnd-main .hoverclass { - border: 1px solid red !important; -} - -/* CSS for an area if something can be dropped in it */ -.panels-modal-content { - background: #fff; - color: #000; - padding: 0; - margin: 2px; - border: 1px solid #000; - width: 600px; - text-align: left; -} - -.panels-modal-content .modal-title { - font-size: 120%; - font-weight: bold; - color: white; - overflow: hidden; - white-space: nowrap; -} - -.panels-modal-content .modal-header { - background-color: #2385c2; - padding: 0 .25em 0 1em; -} - -.panels-modal-content .modal-header a { - color: white; - float: right; -} - -.panels-modal-content .modal-content { - padding: 0 1em; - overflow: auto; - width: 575px; - height: 400px; -} - -.panels-modal-content .modal-form { -} - -.panels-modal-content .form-checkboxes .form-item { - float: left; - width: 24%; -} - -.panels-hidden, -.panels-js-only { - display: none; -} - -a.close { - color: white; -} - -a.close:hover { - text-decoration: none; -} - -a.close img { - position: relative; - top: 1px; -} - -.panels-section-title { - clear: left; - border-bottom: 1px solid #ddf; - margin-bottom: .5em; - text-align: left; -} - -.panels-section-decorator { -} - -.panels-add-content-modal .panels-modal-add-category { - display: block; - border-bottom: 1px solid white; - padding-left: .5em; - margin-left: -2px; - position: relative; -} - -.panels-add-content-modal .panels-modal-add-category.active { - background: url(../images/arrow-active.png) center right no-repeat white; - border-right: none; -} - -.panels-add-content-modal { - background: url(../images/bg-content-modal.png); - height: 100%; - margin: -1em; - padding-top: 1em; - padding-left: 175px; - position: relative; -} - -.panels-section-columns { - height: 100%; - overflow: auto; -} -.panels-section-column { - width: 48%; - float: left; -} - -.panels-section-column .inside { - padding: 0 1em; -} - -.panels-section-column-categories { - width: 173px; - margin-left: -173px; -} - -.panels-categories-description { - padding: 0 1em; - text-align: center; - vertical-align: center; -} - -* html .panels-section-column-categories { - left: 173px; - position: relative; -} - -.panels-section-column-categories .panels-categories-box { - border-top: 1px solid white; - margin-bottom: 1em; -} - -.panels-section-column-categories .inside { - padding: 0; - } - -.panels-section-column-categories .content-type-button { - padding-left: 10px; -} - -.panels-modal-add-category { - color: #5b5b5b !important; - font-weight: bold; - line-height: 2em; -} - -.panels-section { - margin-bottom: 1em; -} - -.panels-section-column .content-type-button { - font-size: 8pt; - line-height: 1em; - overflow: hidden; - text-align: left; -} - -.content-type-button img { - border: 2px solid white; - float: left; -} - -.content-type-button img:hover { - border: 2px solid blue; -} - -.content-type-button div { - width: 85%; - top: -5px; - left: 2px; - float: left; - padding-left: 3px; - padding-top: 5px; -} - -#panels-preview .modal-throbber-wrapper { - width: 100%; - text-align: center; - margin-left: auto; - margin-right: auto; -} -/** modal forms CSS **/ -.panels-modal-content .form-item label { - width: 8em; - float: left; -} - -.panels-modal-content .form-item label.option { - width: auto; - float: none; -} - -.panels-modal-content .form-item .description { - clear: left; -} - -.panels-modal-content .form-item .description .tips { - margin-left: 2em; -} - -.panels-modal-content .no-float .form-item * { - float: none; -} - -.panels-modal-content .modal-form .no-float label { - width: auto; -} - -.panels-modal-content .modal-form fieldset, -.panels-modal-content .modal-form .form-checkboxes { - clear: left; -} - -#edit-configuration-nid { - clear: left; -} - -.option-text-aligner .form-item { - float: left; - padding: .25em 1em .25em 0; - margin: 0; -} - -.option-text-aligner { - clear: both; - width: 100%; - padding: 0; - margin: 0; -} - - -#panels-dnd-main div.panel-pane div.ctools-dropdown-container-wrapper { - margin-left: -158px; - margin-top: -4px; -} - -/* -html.js div.panels-display-links div.ctools-dropdown-container { - width: 275px; -} - -html.js div.panels-display-links div.ctools-dropdown-container ul li li a { - width: 250px; -} - -html.js div.panels-display-links div.ctools-dropdown-container ul li a { - width: 270px; -} -*/ - -#panels-dnd-main .panel-pane .pane-title { - padding: 0.25em 0.5em; -} -#panels-dnd-main .panel-pane .pane-title:after { - font-size: 0.8em; - color: crimson; - letter-spacing: normal; - display: block; -} -#panels-dnd-main .panel-pane.hidden-pane .pane-title:after { - content: " status: hidden"; -} -#panels-dnd-main .panel-pane.changed .pane-title:after { - content: " status: changes not saved"; -} -#panels-dnd-main .panel-pane.hidden-pane.changed .pane-title:after { - content: " status: hidden & changed"; -} - -/* @end */ - - -/* @group CTools Dropdown */ -#panels-dnd-main .ctools-dropdown a.ctools-dropdown-text-link, -html.js div.panels-display-links a.ctools-dropdown-text-link { - - background: url('../images/arrow-down-light.png') 0 3px no-repeat!important; - padding-left: 12px; -} -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container, -html.js div.panels-display-links div.ctools-dropdown-container { - width: 160px!important; - background: #fff url('../images/bg-shade-white-lrg.png') repeat-x 0 100%; - border: solid 1px #ddd!important; - margin: 0!important; -/* padding: 0.5em!important; */ - -webkit-border-radius: 0.333em; - -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 { - - text-decoration: none; - padding: 0; - margin: 0; - color: #555!important; - text-shadow: #fff 1px 1px 1px; -} -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li span.text, -html.js div.panels-display-links div.ctools-dropdown-container ul li span.text { - font-style: normal; - color: #000; - font-weight: bold; -} - -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li a, -html.js div.panels-display-links div.ctools-dropdown-container ul li a { - color: #555!important; - font-weight: normal; - width: auto; - padding: 0 10px; -} - -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li span.panels-text, -html.js div.panels-display-links div.ctools-dropdown-container ul li span.panels-text { - width: auto; - padding: 0 10px; -} - -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li .panels-italic, -html.js div.panels-display-links div.ctools-dropdown-container ul li .panels-italic { - font-style: italic; -} - -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li span.dropdown-header, -html.js div.panels-display-links div.ctools-dropdown-container ul li span.dropdown-header { - background-color: #fefefe; - padding: 0 10px; -} - -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li .panels-sub-menu ul li a, -html.js div.panels-display-links div.ctools-dropdown-container ul li .panels-sub-menu ul li a, -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li .panels-sub-menu span.panels-text, -html.js div.panels-display-links div.ctools-dropdown-container ul li .panels-sub-menu span.panels-text { - width:auto; - padding: 0 20px; -} - -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container hr, -html.js div.panels-display-links div.ctools-dropdown-container hr { - border: 0; - color: #ddd; - background-color: #ddd; - height: 1px; -} - -/* -html.js #panels-dnd-main div.ctools-dropdown div.ctools-dropdown-container ul li a:hover, -html.js div.panels-display-links div.ctools-dropdown-container ul li a:hover { - background: none!important; - color: #000!important; -} -*/ -/* @end */ - -/* @group Modal */ -/* Account for the extra div coming from ctools_modal_form_render() */ -div.messages div.messages { - background: none; - border: none; - margin: 0; - padding: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; -} -div.ctools-modal-content .modal-header { - background: #fff url('../images/bg-shade-light.png') repeat-x bottom left; - color: #777; - display: block; - font-weight: 700; - letter-spacing: normal; - padding: 0.25em 1em; - -moz-border-radius-topleft: 0.5em; - -moz-border-radius-topright: 0.5em; - -webkit-border-top-left-radius: 0.5em; - -webkit-border-top-right-radius: 0.5em; -} -div.ctools-modal-content .modal-title { - font-size: 120%; - font-weight: bold; - text-shadow: #fff 1px 1px 1px; - color: #555; -} -div.ctools-modal-content a.close { - color: #666; - font-weight: normal; - padding-left: 1.6em; - background: url('../images/sprite.png') no-repeat -166px -1686px; -} -div.ctools-modal-content a.close img { - display: none; -} -/** modal forms CSS **/ -div.ctools-modal-content .form-item label { - width: 100%; - float: none; - clear: both; -} -div.ctools-modal-content .resizable-textarea { - width: 100%; - margin-left: 0; - margin-right: 0; -} -div.ctools-modal-content { - font-size: 12px; - border: solid 1px #ddd; - -webkit-border-radius: 0.5em; - -moz-border-radius: 0.5em; - -webkit-box-shadow: -1em 1em 1em rgba(0, 0, 0, 0.5); -} -#modalBackdrop { - position: fixed!important; - background-color: #000!important; -} -/* @end */ - - -/** Override that obnoxious float on throbber. **/ -#panels-dnd-main .progress-disabled { - float: none; -} - -#panels-dnd-main .progress-disabled + .ajax-progress { - float: right; - position: relative; - top: -2em; -} - -/** Override that obnoxious float on throbber. **/ -.panels-categories-box .progress-disabled { - float: none; -} - -.panels-categories-box .progress-disabled + .ajax-progress { - float: right; - position: relative; - top: -2em; - margin-bottom: -2em; -} - -.panels-section-columns .progress-disabled { - display: block; - float: left; -} - -.panels-section-columns .ajax-progress .throbber { - float: right; - position: relative; - top: -2em; - margin-bottom: -2em; -} - diff --git a/css/panels_page.css b/css/panels_page.css deleted file mode 100644 index 1d731b9..0000000 --- a/css/panels_page.css +++ /dev/null @@ -1,14 +0,0 @@ - -div.panels-page-type-container { - clear: left; -} - -div.panels-page-type-container .form-checkboxes .form-item { - float: left; - width: 15em; -} - -#panels-page-settings .form-submit { - display: block; - clear: left; -} diff --git a/help/display.html b/help/display.html deleted file mode 100644 index e69de29..0000000 diff --git a/help/pane.html b/help/pane.html deleted file mode 100644 index e69de29..0000000 diff --git a/help/plugins-style.html b/help/plugins-style.html deleted file mode 100644 index e69de29..0000000 diff --git a/images/arrow-active.png b/images/arrow-active.png deleted file mode 100644 index 3bbd3c2..0000000 Binary files a/images/arrow-active.png and /dev/null differ diff --git a/images/arrow-down-light.png b/images/arrow-down-light.png deleted file mode 100644 index f011ac6..0000000 Binary files a/images/arrow-down-light.png and /dev/null differ diff --git a/images/bg-content-modal.png b/images/bg-content-modal.png deleted file mode 100644 index 600d074..0000000 Binary files a/images/bg-content-modal.png and /dev/null differ diff --git a/images/bg-shade-dark.png b/images/bg-shade-dark.png deleted file mode 100644 index 1be36f2..0000000 Binary files a/images/bg-shade-dark.png and /dev/null differ diff --git a/images/bg-shade-light.png b/images/bg-shade-light.png deleted file mode 100644 index ad7167b..0000000 Binary files a/images/bg-shade-light.png and /dev/null differ diff --git a/images/bg-shade-medium.png b/images/bg-shade-medium.png deleted file mode 100644 index e4b39fe..0000000 Binary files a/images/bg-shade-medium.png and /dev/null differ diff --git a/images/bg-shade-white-lrg.png b/images/bg-shade-white-lrg.png deleted file mode 100644 index 842e5f7..0000000 Binary files a/images/bg-shade-white-lrg.png and /dev/null differ diff --git a/images/blank.gif b/images/blank.gif deleted file mode 100644 index 75b945d..0000000 Binary files a/images/blank.gif and /dev/null differ diff --git a/images/close.gif b/images/close.gif deleted file mode 100644 index 46891b0..0000000 Binary files a/images/close.gif and /dev/null differ diff --git a/images/delete.png b/images/delete.png deleted file mode 100644 index f790555..0000000 Binary files a/images/delete.png and /dev/null differ diff --git a/images/go-down.png b/images/go-down.png deleted file mode 100644 index c2def1a..0000000 Binary files a/images/go-down.png and /dev/null differ diff --git a/images/go-right.png b/images/go-right.png deleted file mode 100644 index dd6058c..0000000 Binary files a/images/go-right.png and /dev/null differ diff --git a/images/go-up.png b/images/go-up.png deleted file mode 100644 index a52c7dc..0000000 Binary files a/images/go-up.png and /dev/null differ diff --git a/images/icon-addcontent.png b/images/icon-addcontent.png deleted file mode 100644 index 788d01f..0000000 Binary files a/images/icon-addcontent.png and /dev/null differ diff --git a/images/icon-cache.png b/images/icon-cache.png deleted file mode 100644 index 3e6f46b..0000000 Binary files a/images/icon-cache.png and /dev/null differ diff --git a/images/icon-configure.png b/images/icon-configure.png deleted file mode 100644 index e23d67c..0000000 Binary files a/images/icon-configure.png and /dev/null differ diff --git a/images/icon-delete.png b/images/icon-delete.png deleted file mode 100644 index 5f0cf69..0000000 Binary files a/images/icon-delete.png and /dev/null differ diff --git a/images/icon-draggable.png b/images/icon-draggable.png deleted file mode 100644 index dba8b67..0000000 Binary files a/images/icon-draggable.png and /dev/null differ diff --git a/images/icon-hidepane.png b/images/icon-hidepane.png deleted file mode 100644 index 8516980..0000000 Binary files a/images/icon-hidepane.png and /dev/null differ diff --git a/images/icon-showpane.png b/images/icon-showpane.png deleted file mode 100644 index 7549dd9..0000000 Binary files a/images/icon-showpane.png and /dev/null differ diff --git a/images/no-icon.png b/images/no-icon.png deleted file mode 100644 index 30584e6..0000000 Binary files a/images/no-icon.png and /dev/null differ diff --git a/images/no-layout-preview.png b/images/no-layout-preview.png deleted file mode 100644 index e78116b..0000000 Binary files a/images/no-layout-preview.png and /dev/null differ diff --git a/images/portlet-collapsed.png b/images/portlet-collapsed.png deleted file mode 100644 index 95a214a..0000000 Binary files a/images/portlet-collapsed.png and /dev/null differ diff --git a/images/portlet-expanded.png b/images/portlet-expanded.png deleted file mode 100644 index 46f39ec..0000000 Binary files a/images/portlet-expanded.png and /dev/null differ diff --git a/images/screenshot-1.jpg b/images/screenshot-1.jpg deleted file mode 100644 index 7204357..0000000 Binary files a/images/screenshot-1.jpg and /dev/null differ diff --git a/images/screenshot-2.jpg b/images/screenshot-2.jpg deleted file mode 100644 index 80203d8..0000000 Binary files a/images/screenshot-2.jpg and /dev/null differ diff --git a/images/screenshot-3.jpg b/images/screenshot-3.jpg deleted file mode 100644 index 2d6ce09..0000000 Binary files a/images/screenshot-3.jpg and /dev/null differ diff --git a/images/screenshot-4.jpg b/images/screenshot-4.jpg deleted file mode 100644 index bf7d77d..0000000 Binary files a/images/screenshot-4.jpg and /dev/null differ diff --git a/images/sky.png b/images/sky.png deleted file mode 100644 index 35b9380..0000000 Binary files a/images/sky.png and /dev/null differ diff --git a/images/sprite.png b/images/sprite.png deleted file mode 100644 index fe74899..0000000 Binary files a/images/sprite.png and /dev/null differ diff --git a/images/throbber.gif b/images/throbber.gif deleted file mode 100644 index 8a084b8..0000000 Binary files a/images/throbber.gif and /dev/null differ diff --git a/images/user-trash.png b/images/user-trash.png deleted file mode 100644 index 71e4c46..0000000 Binary files a/images/user-trash.png and /dev/null differ diff --git a/includes/add-content.inc b/includes/add-content.inc deleted file mode 100644 index 53e133b..0000000 --- a/includes/add-content.inc +++ /dev/null @@ -1,83 +0,0 @@ - $category_info) { - // 'root' category is actually displayed under the categories, so - // skip it. - if ($key == 'root') { - continue; - } - - $class = 'panels-modal-add-category'; - if ($key == $vars['category']) { - $class .= ' active'; - } - - $url = $vars['renderer']->get_url('select-content', $vars['region'], $key); - $vars['categories_array'][] = ctools_ajax_text_button($category_info['title'], $url, '', $class); - } - - // Now render the top level buttons (aka the root category) if any. - $vars['root_content'] = ''; - if (!empty($vars['categories']['root'])) { - foreach ($vars['categories']['root']['content'] as $content_type) { - $vars['root_content'] .= theme('panels_add_content_link', array('renderer' => $vars['renderer'], 'region' => $vars['region'], 'content_type' => $content_type)); - } - } -} - -/** - * Process the panels add content modal. - * - * This is run here so that preprocess can make changes before links are - * actually rendered. - */ -function template_process_panels_add_content_modal(&$vars) { - $content = !empty($vars['categories'][$vars['category']]['content']) ? $vars['categories'][$vars['category']]['content'] : array(); - - // If no category is selected or the category is empty or our special empty - // category render a 'header' that will appear instead of the columns. - if (empty($vars['category']) || empty($content) || $vars['category'] == 'root') { - $vars['header'] = t('Content options are divided by category. Please select a category from the left to proceed.'); - } - else { - $titles = array_keys($content); - natcasesort($titles); - - // This will default to 2 columns in the theme definition but could be - // changed by a preprocess. Ensure there is at least one column. - $columns = max(1, $vars['column_count']); - $vars['columns'] = array_fill(1, $columns, ''); - - $col_size = count($titles) / $columns; - $count = 0; - foreach ($titles as $title) { - $which = floor($count++ / $col_size) + 1; - $vars['columns'][$which] .= theme('panels_add_content_link', array('renderer' => $vars['renderer'], 'region' => $vars['region'], 'content_type' => $content[$title])); - } - } - - $vars['messages'] = theme('status_messages'); -} - -/** - * Preprocess the add content link used in the modal. - */ -function template_preprocess_panels_add_content_link(&$vars) { - $vars['title'] = filter_xss_admin($vars['content_type']['title']); - $vars['description'] = isset($vars['content_type']['description']) ? $vars['content_type']['description'] : $vars['title']; - $vars['icon'] = ctools_content_admin_icon($vars['content_type']); - $vars['url'] = $vars['renderer']->get_url('add-pane', $vars['region'], $vars['content_type']['type_name'], $vars['content_type']['subtype_name']); - $subtype_class = 'add-content-link-' . str_replace('_', '-', $vars['content_type']['subtype_name']); - $vars['image_button'] = ctools_ajax_image_button($vars['icon'], $vars['url'], $vars['description'], $subtype_class . '-image-button panels-modal-add-config'); - $vars['text_button'] = ctools_ajax_text_button($vars['title'], $vars['url'], $vars['description'], $subtype_class . '-text-button panels-modal-add-config'); -} diff --git a/includes/callbacks.inc b/includes/callbacks.inc deleted file mode 100644 index 255a3cd..0000000 --- a/includes/callbacks.inc +++ /dev/null @@ -1,205 +0,0 @@ - -100, - 'title' => t('Panel page'), - 'description' => '' . t('You must activate the page manager module for this functionality.') . '', - ); - } - if (empty($vars['links']['panels_mini'])) { - $vars['links']['panels_mini'] = array( - 'title' => t('Mini panel'), - 'description' => '' . t('You must activate the Mini panels module for this functionality.') . '', - ); - } - if (empty($vars['links']['panels_node'])) { - $vars['links']['panels_mini'] = array( - 'title' => t('Panel node'), - 'description' => '' . t('You must activate the panel node module for this functionality.') . '', - ); - } -} - -/** - * 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/structure/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/structure/panels/layouts/list/$item->name/edit"), - 'class' => 'links', - ), - ); - - // Only show 10. - if (++$count >= 10) { - break; - } - } - - if ($rows) { - $content = theme('table', array('rows' => $rows, 'attributes' => array('class' => 'panels-manage'))); - } - else { - $content = '

' . t('There are no custom layouts.') . '

'; - } - - $vars['blocks']['panels_layout'] = array( - 'title' => t('Manage custom layouts'), - 'link' => l(t('Go to list'), 'admin/structure/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')) { - $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/structure/pages/wizard/' . $id), - 'description' => $plugin['description'], - ); - - $vars['blocks']['wizards']['content'] .= theme('panels_dashboard_link', array('link' => $link)); - } - } - - } - - uasort($vars['links'], 'ctools_plugin_sort'); - - $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', array('link' => $link)); - } - - 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', array('block' => $block)); - } -} - -function panels_admin_settings_page() { - $form = array(); - if (module_exists('page_manager')) { - foreach (page_manager_get_tasks() as $task) { - if ($function = ctools_plugin_get_function($task, 'admin settings')) { - $function($form); - } - } - } - - ctools_include('content'); - foreach (ctools_get_content_types() as $content) { - if ($function = ctools_plugin_get_function($content, 'admin settings')) { - $function($form); - } - } - - ctools_include('plugins', 'panels'); - $pipelines = panels_get_renderer_pipelines(); - $options = array(); - foreach ($pipelines as $key => $value) { - $options[$key] = $value->admin_title; - } - if (count($options) > 1) { - $form['panels_renderer_default'] = array( - '#type' => 'select', - '#title' => t('Default renderer'), - '#options' => $options, - '#default_value' => variable_get('panels_renderer_default', 'standard'), - '#description' => t('The default renderer for new panel pages.'), - ); - } - - if (empty($form)) { - return array('#value' => t('There are currently no settings to change, but additional plugins or modules may provide them in the future.')); - } - - return system_settings_form($form); -} - -/** - * Settings for panel contexts created by the page manager. - */ -function panels_admin_panel_context_page() { - ctools_include('common', 'panels'); - return drupal_get_form('panels_common_settings', 'panels_page'); -} - diff --git a/includes/legacy.inc b/includes/legacy.inc deleted file mode 100644 index c65f781..0000000 --- a/includes/legacy.inc +++ /dev/null @@ -1,41 +0,0 @@ -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); - } - - // At this time there are no legacy checks. -} diff --git a/js/display_editor.js b/js/display_editor.js deleted file mode 100644 index 40e1146..0000000 --- a/js/display_editor.js +++ /dev/null @@ -1,546 +0,0 @@ -/** - * @file display_editor.js - * - * Contains the javascript for the Panels display editor. - */ - -(function ($) { - -// randomly lock a pane. -// @debug only -Drupal.settings.Panels = Drupal.settings.Panels || {}; - - -/** Delete pane button **/ -Drupal.Panels.bindClickDelete = function(context) { - $('a.pane-delete:not(.pane-delete-processed)', context) - .addClass('pane-delete-processed') - .click(function() { - if (confirm(Drupal.t('Remove this pane?'))) { - var id = '#' + $(this).attr('id').replace('pane-delete-', ''); - $(id).remove(); - Drupal.Panels.Draggable.savePositions(); - } - return false; - }); -}; - -Drupal.Panels.bindPortlet = function() { - var handle = $(this).find('.panel-pane-collapsible > div.pane-title'); - var content = $(this).find('.panel-pane-collapsible > div.pane-content'); - if (content.length) { - var toggle = $(''); - handle.before(toggle); - toggle.click(function() { - content.slideToggle(20); - toggle.toggleClass('toggle-collapsed'); - }); - handle.click(function() { - content.slideToggle(20); - toggle.toggleClass('toggle-collapsed'); - }); - content.hide(); - } -}; - -Drupal.Panels.Draggable = { - // The draggable object - object: null, - - // Where objects can be dropped - dropzones: [], - current_dropzone: null, - - // positions within dropzones where an object can be plazed - landing_pads: [], - current_pad: null, - - // Where the object is - mouseOffset: { x: 0, y: 0 }, - windowOffset: { x: 0, y: 0 }, - offsetDivHeight: 0, - - // original settings to be restored - original: {}, - // a placeholder so that if the object is let go but not over a drop zone, - // it can be put back where it belongs - placeholder: {}, - - hoverclass: 'hoverclass', - helperclass: 'helperclass', - accept: 'div.panel-region', - handle: 'div.grabber', - draggable: 'div.panel-portlet', - main: 'div#panels-dnd-main', - - // part of the id to remove to get just the number - draggableId: 'panel-pane-', - - // part of the id to remove to get just the number - regionId: 'panel-region-', - - // What to add to the front of a the id to get the form id for a panel - formId: 'input#edit-', - - maxWidth: 250, - - unsetDropZone: function() { - $(this.current_dropzone.obj).removeClass(this.hoverclass); - this.current_dropzone = null; - for (var i in this.landing_pads) { - $(this.landing_pads[i].obj).remove(); - } - this.landing_pads = []; - this.current_pad = null; - }, - - createLandingPad: function(where, append) { - var obj = $('
 
'); - if (append) { - $(where).append(obj); - } - else { - $(where).before(obj); - } - var offset = $(obj).offset(); - - $(obj).css({ - display: 'none' - }); - this.landing_pads.push({ - centerX: offset.left + ($(obj).innerWidth() / 2), - centerY: offset.top + ($(obj).innerHeight() / 2), - obj: obj - }); - return obj; - }, - - calculateDropZones: function(event, dropzone) { - var draggable = Drupal.Panels.Draggable; - var dropzones = []; - $(this.accept).each(function() { - var offset = $(this).offset(); - offset.obj = this; - offset.region = this.id.replace(draggable.regionId, ''); - offset.width = $(this).outerWidth(); - offset.height = $(this).outerHeight(); - dropzones.push(offset); - }); - this.dropzones = dropzones; - }, - - reCalculateDropZones: function() { - for (var i in this.dropzones) { - var offset = $(this.dropzones[i].obj).offset(); - offset.width = $(this.dropzones[i].obj).outerWidth(); - offset.height = $(this.dropzones[i].obj).outerHeight(); - $.extend(this.dropzones[i], offset); - } - }, - - changeDropZone: function(new_dropzone) { - // Unset our old dropzone. - if (this.current_dropzone) { - this.unsetDropZone(); - } - - // Set up our new dropzone. - this.current_dropzone = new_dropzone; - $(this.current_dropzone.obj).addClass(this.hoverclass); - // add a landing pad - this.createLandingPad(this.current_dropzone.obj, true); - - var that = this; - // Create a landing pad before each existing portlet. - $(this.current_dropzone.obj).find(this.draggable).each(function() { - if (that.object.id != this.id) { - that.createLandingPad(this, false); - } - }); - }, - - findLandingPad: function(x, y) { - var shortest_distance = null; - var nearest_pad = null; - // find the nearest pad. - for (var i in this.landing_pads) { - // This isn't the real distance, this is the square of the - // distance -- no point in spending processing time on - // sqrt. - var dstx = Math.abs(x - this.landing_pads[i].centerX); - var dsty = Math.abs(y - this.landing_pads[i].centerY); - var distance = (dstx * dstx) + (dsty * dsty); - if (shortest_distance == null || distance < shortest_distance) { - shortest_distance = distance; - nearest_pad = this.landing_pads[i]; - } - } - if (nearest_pad != this.current_pad) { - if (this.current_pad) { - $(this.current_pad.obj).hide(); - } - this.current_pad = nearest_pad; - $(nearest_pad.obj).show(); - } - }, - - findDropZone: function(x, y) { - // Go through our dropzones and see if we're over one. - var new_dropzone = null; - for (var i in this.dropzones) { -// console.log('x:' + x + ' left:' + this.dropzones[i].left + ' right: ' + this.dropzones[i].left + this.dropzones[i].width); - if (this.dropzones[i].left < x && - x < this.dropzones[i].left + this.dropzones[i].width && - this.dropzones[i].top < y && - y < this.dropzones[i].top + this.dropzones[i].height) { - new_dropzone = this.dropzones[i]; - break; - } - } - // If we're over one, see if it's different. - if (new_dropzone && (!this.regionLock || this.regionLockRegions[new_dropzone.region])) { - var changed = false; - if (!this.current_dropzone || new_dropzone.obj.id != this.current_dropzone.obj.id) { - this.changeDropZone(new_dropzone); - changed = true; - } - this.findLandingPad(x, y); - if (changed) { - // recalculate the size of our drop zones due to the fact that we're drawing landing pads. - this.reCalculateDropZones(); - } - } - // If we're not over one, be sure to unhilite one if we were just - // over it. - else if (this.current_dropzone) { - this.unsetDropZone(); - } - }, - - /** save button clicked, or pane deleted **/ - savePositions: function() { - var draggable = Drupal.Panels.Draggable; - $(draggable.accept).each(function() { - var val = ''; - $(this).find(draggable.draggable).each(function() { - if (val) { - val += ','; - } - - val += this.id.replace(draggable.draggableId, ''); - }); - var region = this.id.replace(draggable.regionId, ''); - $('input[name="panel[pane][' + region + ']"]').val(val); - }); - return false; - } -}; - -Drupal.Panels.DraggableHandler = function() { - $(this).addClass('panel-draggable'); - var draggable = Drupal.Panels.Draggable; - var scrollBuffer = 10; - var scrollDistance = 10; - var scrollTimer = 30; - - getMouseOffset = function(docPos, mousePos, windowPos) { - return { x: mousePos.x - docPos.x + windowPos.x, y: mousePos.y - docPos.y + windowPos.y}; - }; - - getMousePos = function(ev) { - ev = ev || window.event; - - if (ev.pageX || ev.pageY) { - return { x:ev.pageX, y:ev.pageY }; - } - return { - x:ev.clientX + document.body.scrollLeft - document.body.clientLeft, - y:ev.clientY + document.body.scrollTop - document.body.clientTop - }; - }; - - getPosition = function(e) { - /* - if (document.defaultView && document.defaultView.getComputedStyle) { - var css = document.defaultView.getComputedStyle(e, null); - return { - x: parseInt(css.getPropertyValue('left')), - y: parseInt(css.getPropertyValue('top')) - }; - } - */ - var left = 0; - var top = 0; - - while (e.offsetParent) { - left += e.offsetLeft; - top += e.offsetTop; - e = e.offsetParent; - } - - left += e.offsetLeft; - top += e.offsetTop; - - return { x:left, y:top }; - }; - - mouseUp = function(e) { - clearTimeout(draggable.timeoutId); - draggable.dropzones = []; - - if (draggable.current_pad) { - // Drop the object where we're hovering - $(draggable.object).insertAfter($(draggable.current_pad.obj)); - Drupal.Panels.changed($(draggable.object)); - } - else { - // or put it back where it came from - $(draggable.object).insertAfter(draggable.placeholder); - } - // remove the placeholder - draggable.placeholder.remove(); - - // restore original settings. - $(draggable.object).css(draggable.original); - if (draggable.current_dropzone) { - draggable.unsetDropZone(); - } - - $(document).unbind('mouseup').unbind('mousemove'); - draggable.savePositions(); - }; - - mouseMove = function(e) { - draggable.mousePos = getMousePos(e); - - draggable.findDropZone(draggable.mousePos.x, draggable.mousePos.y); - - var windowMoved = parseInt(draggable.offsetDivHeight - $(draggable.main).innerHeight()); - - draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + windowMoved + 'px'; - draggable.object.style.left = draggable.mousePos.x - draggable.mouseOffset.x + 'px'; - $(draggable.object).toggleClass('moving'); - }; - - mouseDown = function(e) { - // If we mouse-downed over something clickable, don't drag! - if (e.target.nodeName == 'A' || e.target.nodeName == 'INPUT' || e.target.parentNode.nodeName == 'A' || e.target.nodeName.nodeName == 'INPUT') { - return; - } - - draggable.object = $(this).parent(draggable.draggable).get(0); - draggable.paneId = draggable.object.id.replace(draggable.draggableId, ''); - - // create a placeholder so we can put this object back if dropped in an invalid location. - draggable.placeholder = $('"'); - $(draggable.object).after(draggable.placeholder); - - // Store original CSS so we can put it back. - draggable.original = { - position: $(draggable.object).css('position'), - width: 'auto', - left: $(draggable.object).css('left'), - top: $(draggable.object).css('top'), - 'z-index': $(draggable.object).css('z-index'), - 'margin-bottom': $(draggable.object).css('margin-bottom'), - 'margin-top': $(draggable.object).css('margin-top'), - 'margin-left': $(draggable.object).css('margin-left'), - 'margin-right': $(draggable.object).css('margin-right'), - 'padding-bottom': $(draggable.object).css('padding-bottom'), - 'padding-top': $(draggable.object).css('padding-top'), - 'padding-left': $(draggable.object).css('padding-left'), - 'padding-right': $(draggable.object).css('padding-right') - }; - - draggable.mousePos = getMousePos(e); - var originalPos = $(draggable.object).offset(); - var width = Math.min($(draggable.object).innerWidth(), draggable.maxWidth); - - draggable.offsetDivHeight = $(draggable.main).innerHeight(); - draggable.findDropZone(draggable.mousePos.x, draggable.mousePos.y); - - // Make copies of these because in FF3, they actually change when we - // move the item, whereas they did not in FF2. - - if (e.layerX || e.layerY) { - var layerX = e.layerX; - var layerY = e.layerY; - } - else if (e.originalEvent && e.originalEvent.layerX) { - var layerX = e.originalEvent.layerX; - var layerY = e.originalEvent.layerY; - } - - // Make the draggable relative, get it out of the way and make it - // invisible. - $(draggable.object).css({ - position: 'relative', - 'z-index': 100, - width: width + 'px', - 'margin-bottom': (-1 * parseInt($(draggable.object).outerHeight())) + 'px', - 'margin-top': 0, - 'margin-left': 0, - 'margin-right': (-1 * parseInt($(draggable.object).outerWidth())) + 'px', - 'padding-bottom': 0, - 'padding-top': 0, - 'padding-left': 0, - 'padding-right': 0, - 'left': 0, - 'top': 0 - }) - .insertAfter($(draggable.main)); - var newPos = $(draggable.object).offset(); - - var windowOffset = { left: originalPos.left - newPos.left, top: originalPos.top - newPos.top } - - // if they grabbed outside the area where we make the draggable smaller, move it - // closer to the cursor. - if (layerX != 'undefined' && layerX > width) { - windowOffset.left += layerX - 10; - } - else if (layerX != 'undefined' && e.offsetX > width) { - windowOffset.left += e.offsetX - 10; - } - - // This is stored so we can move with it. - draggable.mouseOffset = { x: draggable.mousePos.x - windowOffset.left, y: draggable.mousePos.y - windowOffset.top}; - draggable.offsetDivHeight = $(draggable.main).innerHeight(); - - draggable.object.style.top = windowOffset.top + 'px'; - draggable.object.style.left = windowOffset.left + 'px'; - $(document).unbind('mouseup').unbind('mousemove').mouseup(mouseUp).mousemove(mouseMove); - - draggable.calculateDropZones(draggable.mousePos, e); - draggable.timeoutId = setTimeout('timer()', scrollTimer); - - // If locking to a particular set of regions, set that: - if (Drupal.settings.Panels && Drupal.settings.Panels.RegionLock && Drupal.settings.Panels.RegionLock[draggable.paneId]) { - draggable.regionLock = true; - draggable.regionLockRegions = Drupal.settings.Panels.RegionLock[draggable.paneId]; - } - else { - draggable.regionLock = false; - draggable.regionLockRegions = null; - } - - return false; - }; - - timer = function() { - if (!draggable.timeCount) { - draggable.timeCount = 0; - } - draggable.timeCount = draggable.timeCount + 1; - var left = $(window).scrollLeft(); - var right = left + $(window).width(); - var top = $(window).scrollTop(); - var bottom = top + $(window).height(); - - if (draggable.mousePos.x < left + scrollBuffer && left > 0) { - window.scrollTo(left - scrollDistance, top); - draggable.mousePos.x -= scrollDistance; - draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px'; - } - else if (draggable.mousePos.x > right - scrollBuffer) { - window.scrollTo(left + scrollDistance, top); - draggable.mousePos.x += scrollDistance; - draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px'; - } - else if (draggable.mousePos.y < top + scrollBuffer && top > 0) { - window.scrollTo(left, top - scrollDistance); - draggable.mousePos.y -= scrollDistance; - draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px'; - } - else if (draggable.mousePos.y > bottom - scrollBuffer) { - window.scrollTo(left, top + scrollDistance); - draggable.mousePos.y += scrollDistance; - draggable.object.style.top = draggable.mousePos.y - draggable.mouseOffset.y + 'px'; - } - - draggable.timeoutId = setTimeout('timer()', scrollTimer); - } - - $(this).mousedown(mouseDown); -}; - -$.fn.extend({ - panelsDraggable: Drupal.Panels.DraggableHandler -}); - -/** - * Implement Drupal behavior for autoattach - */ -Drupal.behaviors.PanelsDisplayEditor = { - attach: function(context) { - // Show javascript only items. - $('span#panels-js-only').css('display', 'inline'); - - $('#panels-dnd-main div.panel-pane:not(.panel-portlet)') - .addClass('panel-portlet') - .each(Drupal.Panels.bindPortlet); - - // The above doesn't work if context IS the pane, so do this to catch that. - if ($(context).hasClass('panel-pane') && !$(context).hasClass('panel-portlet')) { - $(context) - .addClass('panel-portlet') - .each(Drupal.Panels.bindPortlet); - } - - // Make draggables and make sure their positions are saved. - $(context).find('div.grabber:not(.panel-draggable)').panelsDraggable(); - Drupal.Panels.Draggable.savePositions(); - - // Bind buttons. - $('input#panels-hide-all', context).click(Drupal.Panels.clickHideAll); - $('input#panels-show-all', context).click(Drupal.Panels.clickShowAll); - - Drupal.Panels.bindClickDelete(context); - - $('#panels-live-preview-button:not(.panels-preview-processed)') - .addClass('panels-preview-processed') - .click(function () { - if (!$('#panels-preview').size()) { - $('#panels-dnd-main').parents('form').after('
'); - } - var html = ''; - html += ' '; - - $('#panels-preview').html(html); - }); - - var setTitleClass = function () { - if ($('#edit-display-title-hide-title').val() == 2) { - $('#panels-dnd-main').removeClass('panels-set-title-hide'); - } - else { - $('#panels-dnd-main').addClass('panels-set-title-hide'); - } - } - - // The panes have an option to set the display title, but only if - // a select is set to the proper value. This sets a class on the - // main edit div so that the option to set the display title - // is hidden if that is not selected, and visible if it is. - $('#edit-display-title-hide-title:not(.panels-title-processed)') - .addClass('panels-title-processed') - .change(setTitleClass); - - setTitleClass(); - } -} - -$(function() { - /** - * AJAX responder command to render the preview. - */ - Drupal.ajax.prototype.commands.panel_preview = function(ajax, command, status) { - $('#panels-preview').html(command.output); - } -}); - -})(jQuery); diff --git a/js/layout.js b/js/layout.js deleted file mode 100644 index e54fd02..0000000 --- a/js/layout.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @file layout.js - * - * Contains javascript to make layout modification a little nicer. - */ - -(function ($) { - Drupal.Panels.Layout = {}; - Drupal.Panels.Layout.autoAttach = function() { - $('div.form-item div.layout-icon').click(function() { - $widget = $('input', $(this).parent()); - // Toggle if a checkbox, turn on if a radio. - $widget.attr('checked', !$widget.attr('checked') || $widget.is('input[type=radio]')); - }); - }; - - $(Drupal.Panels.Layout.autoAttach); -})(jQuery); diff --git a/js/panels-base.js b/js/panels-base.js deleted file mode 100644 index 4df3c80..0000000 --- a/js/panels-base.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file - * Implement basic methods required by all of panels. - */ - -(function ($) { - Drupal.Panels = Drupal.Panels || {}; - - Drupal.Panels.changed = function(item) { - if (!item.is('.changed')) { - item.addClass('changed'); - item.find('div.grabber span.text').append(' * '); - } - }; - - Drupal.Panels.restripeTable = function(table) { - // :even and :odd are reversed because jquery counts from 0 and - // we count from 1, so we're out of sync. - $('tbody tr:not(:hidden)', $(table)) - .removeClass('even') - .removeClass('odd') - .filter(':even') - .addClass('odd') - .end() - .filter(':odd') - .addClass('even'); - }; -})(jQuery); diff --git a/layouts/onecol/onecol.css b/layouts/onecol/onecol.css new file mode 100644 index 0000000..743f22f --- /dev/null +++ b/layouts/onecol/onecol.css @@ -0,0 +1,3 @@ +.panel-1col .panel-panel{ + width: 100%; +} diff --git a/layouts/onecol/onecol.png b/layouts/onecol/onecol.png new file mode 100644 index 0000000..176ed69 Binary files /dev/null and b/layouts/onecol/onecol.png differ diff --git a/layouts/onecol/panels-onecol.html.twig b/layouts/onecol/panels-onecol.html.twig new file mode 100644 index 0000000..0e898ef --- /dev/null +++ b/layouts/onecol/panels-onecol.html.twig @@ -0,0 +1,19 @@ +{# +/** + * @file + * Template for a one column panel layout. + * + * This template provides a very simple "one column" panel display layout. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - content.middle: The only panel in the layout. + */ +#} +
+
+ {{ content.middle }} +
+
diff --git a/layouts/threecol_25_50_25/panels-threecol-25-50-25.html.twig b/layouts/threecol_25_50_25/panels-threecol-25-50-25.html.twig new file mode 100644 index 0000000..d3c9e2c --- /dev/null +++ b/layouts/threecol_25_50_25/panels-threecol-25-50-25.html.twig @@ -0,0 +1,29 @@ +{# +/** + * @file + * Template for a 3 column panel layout. + * + * This template provides a three column 25%-50%-25% panel display layout. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - content.left: Content in the left column. + * - content.middle: Content in the middle column. + * - content.right: Content in the right column. + */ +#} +
+
+ {{ content.left }} +
+ +
+ {{ content.middle }} +
+ +
+ {{ content.right }} +
+
diff --git a/layouts/threecol_25_50_25/threecol_25_50_25.css b/layouts/threecol_25_50_25/threecol_25_50_25.css new file mode 100644 index 0000000..5da1dfc --- /dev/null +++ b/layouts/threecol_25_50_25/threecol_25_50_25.css @@ -0,0 +1,12 @@ +.panel-3col { + display: flex; + justify-content: space-between; +} + +.panel-3col > .panel-panel { + flex: 0 1 25%; +} + +.panel-3col > .panel-col-middle { + flex: 0 1 50%; +} diff --git a/layouts/threecol_25_50_25/threecol_25_50_25.png b/layouts/threecol_25_50_25/threecol_25_50_25.png new file mode 100644 index 0000000..ad6832a Binary files /dev/null and b/layouts/threecol_25_50_25/threecol_25_50_25.png differ diff --git a/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.html.twig b/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.html.twig new file mode 100644 index 0000000..8f77316 --- /dev/null +++ b/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.html.twig @@ -0,0 +1,44 @@ +{# +/** + * @file + * Template for a 3 column panel layout. + * + * This template provides a three column 25%-50%-25% panel display layout, with + * additional areas for the top and the bottom. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - content.top: Content in the top row. + * - content.left: Content in the left column. + * - content.middle: Content in the middle column. + * - content.right: Content in the right column. + * - content.bottom: Content in the bottom row. + */ +#} +
+ {% if content.top %} +
+ {{ content.top }} +
+ {% endif %} + +
+ {{ content.left }} +
+ +
+ {{ content.middle }} +
+ +
+ {{ content.right }} +
+ + {% if content.bottom %} +
+ {{ content.bottom }} +
+ {% endif %} +
diff --git a/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.css b/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.css new file mode 100644 index 0000000..ff0bcd7 --- /dev/null +++ b/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.css @@ -0,0 +1,17 @@ +.panel-3col-stacked { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.panel-3col-stacked > .panel-panel { + flex: 0 1 25%; +} + +.panel-3col-stacked > .panel-full-width { + flex: 0 1 100%; +} + +.panel-3col-stacked > .panel-col-middle { + flex: 0 1 50%; +} diff --git a/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.png b/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.png new file mode 100644 index 0000000..14b4779 Binary files /dev/null and b/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.png differ diff --git a/layouts/threecol_33_34_33/panels-threecol-33-34-33.html.twig b/layouts/threecol_33_34_33/panels-threecol-33-34-33.html.twig new file mode 100644 index 0000000..e54ad03 --- /dev/null +++ b/layouts/threecol_33_34_33/panels-threecol-33-34-33.html.twig @@ -0,0 +1,29 @@ +{# +/** + * @file + * Template for a 3 column panel layout. + * + * This template provides a three column 33%-34%-33% panel display layout. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - content.left: Content in the left column. + * - content.middle: Content in the middle column. + * - content.right: Content in the right column. + */ +#} +
+
+ {{ content.left }} +
+ +
+ {{ content.middle }} +
+ +
+ {{ content.right }} +
+
diff --git a/layouts/threecol_33_34_33/threecol_33_34_33.css b/layouts/threecol_33_34_33/threecol_33_34_33.css new file mode 100644 index 0000000..898c9ec --- /dev/null +++ b/layouts/threecol_33_34_33/threecol_33_34_33.css @@ -0,0 +1,9 @@ +.panel-3col-33 { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.panel-3col-33 > .panel-panel { + flex: 1 33%; +} diff --git a/layouts/threecol_33_34_33/threecol_33_34_33.png b/layouts/threecol_33_34_33/threecol_33_34_33.png new file mode 100644 index 0000000..468f8bb Binary files /dev/null and b/layouts/threecol_33_34_33/threecol_33_34_33.png differ diff --git a/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.html.twig b/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.html.twig new file mode 100644 index 0000000..2848756 --- /dev/null +++ b/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.html.twig @@ -0,0 +1,44 @@ +{# +/** + * @file + * Template for a 3 column panel layout. + * + * This template provides a three column 33%-34%-33% panel display layout, with + * additional areas for the top and the bottom. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - content.top: Content in the top row. + * - content.left: Content in the left column. + * - content.middle: Content in the middle column. + * - content.right: Content in the right column. + * - content.bottom: Content in the bottom row. + */ +#} +
+ {% if content.top %} +
+ {{ content.top }} +
+ {% endif %} + +
+ {{ content.left }} +
+ +
+ {{ content.middle }} +
+ +
+ {{ content.right }} +
+ + {% if content.bottom %} +
+ {{ content.bottom }} +
+ {% endif %} +
diff --git a/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.css b/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.css new file mode 100644 index 0000000..04b1ebe --- /dev/null +++ b/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.css @@ -0,0 +1,13 @@ +.panel-3col-33-stacked { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.panel-3col-33-stacked > .panel-panel { + flex: 0 1 33%; +} + +.panel-3col-33-stacked > .panel-full-width { + flex: 0 1 100%; +} diff --git a/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.png b/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.png new file mode 100644 index 0000000..ffd1351 Binary files /dev/null and b/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.png differ diff --git a/layouts/twocol/panels-twocol.html.twig b/layouts/twocol/panels-twocol.html.twig new file mode 100644 index 0000000..776c865 --- /dev/null +++ b/layouts/twocol/panels-twocol.html.twig @@ -0,0 +1,25 @@ +{# +/** + * @file + * Template for a 2 column panel layout. + * + * This template provides a two column panel display layout, with + * each column roughly equal in width. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - $content['left']: Content in the left column. + * - $content['right']: Content in the right column. + */ +#} +
+
+ {{ content.left }} +
+ +
+ {{ content.right }} +
+
diff --git a/layouts/twocol/twocol.css b/layouts/twocol/twocol.css new file mode 100644 index 0000000..13fef2c --- /dev/null +++ b/layouts/twocol/twocol.css @@ -0,0 +1,9 @@ +.panel-2col { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.panel-2col > .panel-panel { + flex: 0 1 50%; +} diff --git a/layouts/twocol/twocol.png b/layouts/twocol/twocol.png new file mode 100644 index 0000000..9d2965e Binary files /dev/null and b/layouts/twocol/twocol.png differ diff --git a/layouts/twocol_bricks/panels-twocol-bricks.html.twig b/layouts/twocol_bricks/panels-twocol-bricks.html.twig new file mode 100644 index 0000000..3b1d634 --- /dev/null +++ b/layouts/twocol_bricks/panels-twocol-bricks.html.twig @@ -0,0 +1,50 @@ +{# +/** + * @file + * Template for a 2 column panel layout. + * + * This template provides a two column panel display layout with full width + * areas at the top, bottom and in the middle. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - content.top: Content in the top row. + * - content.left_above: Content in the top left column. + * - content.right_above: Content in the top right column. + * - content.middle: Content in the middle column. + * - content.left_below: Content in the bottom left column. + * - content.right_below: Content in the bottom right column. + * - content.bottom: Content in the bottom row. + */ +#} +
+
+ {{ content.top }} +
+ +
+ {{ content.left_above }} +
+ +
+ {{ content.right_above }} +
+ +
+ {{ content.middle }} +
+ +
+ {{ content.left_below }} +
+ +
+ {{ content.right_below }} +
+ +
+ {{ content.bottom }} +
+
diff --git a/layouts/twocol_bricks/twocol_bricks.css b/layouts/twocol_bricks/twocol_bricks.css new file mode 100644 index 0000000..0954aed --- /dev/null +++ b/layouts/twocol_bricks/twocol_bricks.css @@ -0,0 +1,13 @@ +.panel-2col-bricks { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.panel-2col-bricks > .panel-panel { + flex: 0 1 50%; +} + +.panel-2col-bricks > .panel-full-width { + flex: 0 1 100%; +} diff --git a/layouts/twocol_bricks/twocol_bricks.png b/layouts/twocol_bricks/twocol_bricks.png new file mode 100644 index 0000000..450395c Binary files /dev/null and b/layouts/twocol_bricks/twocol_bricks.png differ diff --git a/layouts/twocol_stacked/panels-twocol-stacked.html.twig b/layouts/twocol_stacked/panels-twocol-stacked.html.twig new file mode 100644 index 0000000..75e9514 --- /dev/null +++ b/layouts/twocol_stacked/panels-twocol-stacked.html.twig @@ -0,0 +1,36 @@ +{# +/** + * @file + * Template for a 2 column panel layout. + * + * This template provides a two column panel display layout, with + * additional areas for the top and the bottom. + * + * Variables: + * - $id: An optional CSS id to use for the layout. + * - $content: An array of content, each item in the array is keyed to one + * panel of the layout. This layout supports the following sections: + * - content.top: Content in the top row. + * - content.left: Content in the left column. + * - content.middle: Content in the middle column. + * - content.right: Content in the right column. + * - content.bottom: Content in the bottom row. + */ +#} +
+
+ {{ content.top }} +
+ +
+ {{ content.left }} +
+ +
+ {{ content.right }} +
+ +
+ {{ content.bottom }} +
+
diff --git a/layouts/twocol_stacked/twocol_stacked.css b/layouts/twocol_stacked/twocol_stacked.css new file mode 100644 index 0000000..8e66476 --- /dev/null +++ b/layouts/twocol_stacked/twocol_stacked.css @@ -0,0 +1,13 @@ +.panel-2col-stacked { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.panel-2col-stacked > .panel-panel { + flex: 0 1 50%; +} + +.panel-2col-stacked > .panel-full-width { + flex: 0 1 100%; +} diff --git a/layouts/twocol_stacked/twocol_stacked.png b/layouts/twocol_stacked/twocol_stacked.png new file mode 100644 index 0000000..30ab8b6 Binary files /dev/null and b/layouts/twocol_stacked/twocol_stacked.png differ diff --git a/panels.layouts.yml b/panels.layouts.yml index 048f098..e0bcc22 100644 --- a/panels.layouts.yml +++ b/panels.layouts.yml @@ -1,7 +1,7 @@ onecol: label: Single column - category: Columns: 1 - path: plugins/layouts/onecol + category: 'Columns: 1' + path: layouts/onecol icon: onecol.png css: onecol.css template: panels-onecol @@ -11,8 +11,8 @@ onecol: twocol: label: Two column - category: Columns: 2 - path: plugins/layouts/twocol + category: 'Columns: 2' + path: layouts/twocol icon: twocol.png css: twocol.css template: panels-twocol @@ -21,3 +21,111 @@ twocol: label: Left side right: label: Right side + +twocol_bricks: + label: Two column bricks + category: 'Columns: 2' + path: layouts/twocol_bricks + icon: twocol_bricks.png + css: twocol_bricks.css + template: panels-twocol-bricks + regions: + top: + label: Top + left_above: + label: Left above + right_above: + label: Right above + middle: + label: Middle + left_below: + label: Left below + right_below: + label: Right below + bottom: + label: Bottom + +twocol_stacked: + label: Two column stacked + category: 'Columns: 2' + path: layouts/twocol_stacked + icon: twocol_stacked.png + css: twocol_stacked.css + template: panels-twocol-stacked + regions: + top: + label: Top + left: + label: Left side + right: + label: Right side + bottom: + label: Bottom + +threecol_25_50_25: + label: Three column 25/50/25 + category: 'Columns: 3' + path: layouts/threecol_25_50_25 + icon: threecol_25_50_25.png + css: threecol_25_50_25.css + template: panels-threecol-25-50-25 + regions: + left: + label: Left side + middle: + label: Middle column + right: + label: Right side + +threecol_25_50_25_stacked: + label: Three column 25/50/25 stacked + category: 'Columns: 3' + path: layouts/threecol_25_50_25_stacked + icon: threecol_25_50_25_stacked.png + css: threecol_25_50_25_stacked.css + template: panels-threecol-25-50-25-stacked + regions: + top: + label: Top + left: + label: Left side + middle: + label: Middle column + right: + label: Right side + bottom: + label: Bottom + +threecol_33_34_33: + label: Three column 33/34/33 + category: 'Columns: 3' + path: layouts/threecol_33_34_33 + icon: threecol_33_34_33.png + css: threecol_33_34_33.css + template: panels-threecol-33-34-33 + regions: + left: + label: Left side + middle: + label: Middle column + right: + label: Right side + +threecol_33_34_33_stacked: + label: Three column 33/34/33 stacked + category: 'Columns: 3' + path: layouts/threecol_33_34_33_stacked + icon: threecol_33_34_33_stacked.png + css: threecol_33_34_33_stacked.css + template: panels-threecol-33-34-33-stacked + regions: + top: + label: Top + left: + label: Left side + middle: + label: Middle column + right: + label: Right side + bottom: + label: Bottom diff --git a/panels.module b/panels.module index a701ffe..05e0a2e 100644 --- a/panels.module +++ b/panels.module @@ -73,130 +73,6 @@ function panels_page_variant_presave(PageVariantInterface $page_variant) { // -------------------------------------------------------------------------- // Core Drupal hook implementations - -/** - * Implementation of hook_theme() - */ -function panels_theme() { - $theme = array(); - - /* - $layouts = panels_get_layouts(); - foreach ($layouts as $name => $data) { - foreach (array('theme', 'admin theme') as $callback) { - if (!empty($data[$callback])) { - $theme[$data[$callback]] = array( - 'variables' => 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. - if (!function_exists("theme_$data[theme]")) { - $theme[$data[$callback]]['template'] = str_replace('_', '-', $data[$callback]); - $theme[$data[$callback]]['file'] = $data['file']; // for preprocess. - } - } - } - } - */ - - // @todo: Port the follow to Drupal 8! - /* - $theme['panels_layout_link'] = array( - 'variables' => array('title' => NULL, 'id' => NULL, 'image' => NULL, 'link' => NULL, 'class' => NULL), - ); - $theme['panels_layout_icon'] = array( - 'variables' => array('id' => NULL, 'image' => NULL, 'title' => NULL), - ); - $theme['panels_pane'] = array( - 'variables' => array('output' => array(), 'pane' => array(), 'display' => array()), - 'path' => drupal_get_path('module', 'panels') . '/templates', - 'template' => 'panels-pane', - ); - $theme['panels_common_content_list'] = array( - 'variables' => array('display' => NULL), - 'file' => 'includes/common.inc', - ); - $theme['panels_render_display_form'] = array( - 'render element' => 'element', - ); - - $theme['panels_dashboard'] = array( - 'variables' => array(), - 'path' => drupal_get_path('module', 'panels') . '/templates', - 'file' => '../includes/callbacks.inc', - 'template' => 'panels-dashboard', - ); - - $theme['panels_dashboard_link'] = array( - 'variables' => array('link' => array()), - 'path' => drupal_get_path('module', 'panels') . '/templates', - 'file' => '../includes/callbacks.inc', - 'template' => 'panels-dashboard-link', - ); - - $theme['panels_dashboard_block'] = array( - 'variables' => array('block' => array()), - 'path' => drupal_get_path('module', 'panels') . '/templates', - 'file' => '../includes/callbacks.inc', - 'template' => 'panels-dashboard-block', - ); - - $theme['panels_add_content_modal'] = array( - 'variables' => array('renderer' => NULL, 'categories' => array(), 'region' => NULL, 'category' => NULL, 'column_count' => 2), - 'path' => drupal_get_path('module', 'panels') . '/templates', - 'file' => '../includes/add-content.inc', - 'template' => 'panels-add-content-modal', - ); - - $theme['panels_add_content_link'] = array( - 'variables' => array('renderer' => NULL, 'region' => NULL, 'content_type' => NULL), - 'path' => drupal_get_path('module', 'panels') . '/templates', - 'file' => '../includes/add-content.inc', - 'template' => 'panels-add-content-link', - ); - - // We don't need layout and style themes in maintenance mode. - // Disabling this: See http://drupal.org/node/979912 for information. -// if (defined('MAINTENANCE_MODE')) { -// return $theme; -// } - - // Register layout and style themes on behalf of all of these items. - ctools_include('plugins', 'panels'); - - $styles = panels_get_styles(); - foreach ($styles as $name => $data) { - if (!empty($data['render pane'])) { - $theme[$data['render pane']] = array( - 'variables' => array('content' => NULL, 'pane' => NULL, 'display' => NULL, 'style' => NULL, 'settings' => NULL), - 'path' => $data['path'], - 'file' => $data['file'], - ); - } - if (!empty($data['render region'])) { - $theme[$data['render region']] = array( - 'variables' => array('display' => NULL, 'owner_id' => NULL, 'panes' => NULL, 'settings' => NULL, 'region_id' => NULL, 'style' => NULL), - 'path' => $data['path'], - 'file' => $data['file'], - ); - } - - if (!empty($data['hook theme'])) { - if (is_array($data['hook theme'])) { - $theme += $data['hook theme']; - } - else if (function_exists($data['hook theme'])) { - $data['hook theme']($theme, $data); - } - } - } - */ - - return $theme; -} - /** * Implementation of hook_menu */ @@ -220,19 +96,6 @@ function panels_menu() { 'file' => 'includes/callbacks.inc', 'access arguments' => array('use panels dashboard'), ); - // Provide a nice location for a panels admin panel. - $items['admin/structure/panels'] = array( - 'title' => 'Panels', - 'page callback' => 'panels_admin_page', - 'description' => 'Get a bird\'s eye view of items related to Panels.', - ) + $admin_base; - - $items['admin/structure/panels/dashboard'] = array( - 'title' => 'Dashboard', - 'page callback' => 'panels_admin_page', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ) + $admin_base; $items['admin/structure/panels/settings'] = array( 'title' => 'Settings', @@ -272,118 +135,17 @@ function panels_menu() { } } - return $items; } /** - * Menu loader function to load a cache item for Panels AJAX. - * - * This load all of the includes needed to perform AJAX, and loads the - * cache object and makes sure it is valid. - */ -function panels_edit_cache_load($cache_key) { - ctools_include('display-edit', 'panels'); - ctools_include('plugins', 'panels'); - ctools_include('ajax'); - ctools_include('modal'); - ctools_include('context'); - - return panels_edit_cache_get($cache_key); -} - -/** - * Implementation of hook_init() - */ -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; + * Implements hook_cache_flush(). + * TODO: Implement the cache_panels, if applicable for 8.x + function panels_cache_flush() { + return array('cache_panels'); } - - ctools_add_css('panels', 'panels'); -} - -/** - * Implementation of hook_flush_caches(). - * - * We implement this so that we can be sure our legacy rendering state setting - * in $conf is updated whenever caches are cleared. - */ -//function panels_flush_caches() { -// $legacy = panels_get_legacy_state(); -// $legacy->determineStatus(); -//} - -/** - * Implements hook_flush_caches(). */ -function panels_flush_caches() { - if (db_table_exists('cache_panels')) { - return array('cache_panels'); - } -} -// --------------------------------------------------------------------------- -// CTools hook implementations -// -// These aren't core Drupal hooks but they are just as important. - -/** - * 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; - } - - // We don't support the 'ctools' 'cache' plugin and pretending to causes - // errors when they're in use. - if ($module == 'ctools' && $plugin == 'cache') { - return; - // if we did we'd make a plugin/ctools_cache or something. - } - - if ($module == 'page_manager' || $module == 'panels' || $module == 'ctools' || $module == 'stylizer') { - // Panels and CTools both implement a 'cache' plugin but we don't implement - // the CTools version. - if ($module == 'ctools' && $plugin == 'cache') { - return; - } - - return 'plugins/' . $plugin; - } -} - -/** - * Implements hook_ctools_plugin_type(). - * - * Register layout, style, cache, and display_renderer plugin types, declaring - * relevant plugin type information as necessary. - */ -function panels_ctools_plugin_type() { - return array( - 'layouts' => array( - 'load themes' => TRUE, // Can define layouts in themes - 'process' => 'panels_layout_process', - 'child plugins' => TRUE, - ), - 'styles' => array( - 'load themes' => TRUE, - 'process' => 'panels_plugin_styles_process', - 'child plugins' => TRUE, - ), - 'cache' => array(), - 'display_renderers' => array( - 'classes' => array('renderer'), - ), - ); -} /** * Ensure a layout has a minimal set of data. @@ -396,42 +158,6 @@ function panels_layout_process(&$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_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 @@ -1405,12 +1131,10 @@ function panels_ajax_router() { // 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. +// @todo This system needs to be ported to Drupal 8 /** * 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 @@ -1418,22 +1142,23 @@ function panels_cache_get($obj, $did, $skip_cache = FALSE) { 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. @@ -1441,7 +1166,6 @@ function panels_cache_clear($obj, $did) { * 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(); @@ -1458,11 +1182,12 @@ function panels_edit_cache_get_default(&$display, $content_types = NULL, $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); @@ -1472,11 +1197,11 @@ function panels_edit_cache_get($cache_key) { // 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) { @@ -1487,11 +1212,12 @@ function panels_edit_cache_set($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) { @@ -1504,11 +1230,11 @@ function panels_edit_cache_save($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) { @@ -1521,10 +1247,10 @@ function panels_edit_cache_clear($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; @@ -1542,7 +1268,7 @@ function panels_edit_cache_break_lock($cache) { // no fallback. return; } - + */ // -------------------------------------------------------------------------- // Callbacks on behalf of the panel_context plugin. // @@ -1765,35 +1491,4 @@ function panels_print_layout($layout, $content, $meta = 'standard') { */ function _panels_builder_filter($layout) { return empty($layout['builder']); -} - -// -------------------------------------------------------------------------- -// Deprecated functions -// -// Everything below this line will eventually go away. - -/** - * 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; -} - -/** - * Remove default sidebar related body classes and provide own css classes - */ -function panels_preprocess_html(&$vars) { - $panel_body_css = &drupal_static('panel_body_css'); - if (!empty($panel_body_css['body_classes_to_remove'])) { - $classes_to_remove = explode(' ', $panel_body_css['body_classes_to_remove']); - foreach ($vars['classes_array'] as $key => $css_class) { - if (in_array($css_class, $classes_to_remove)) { - unset($vars['classes_array'][$key]); - } - } - } - if (!empty($panel_body_css['body_classes_to_add'])) { - $vars['classes_array'][] = check_plain($panel_body_css['body_classes_to_add']); - } -} +} \ No newline at end of file diff --git a/panels.permissions.yml b/panels.permissions.yml index 0023a44..245d542 100644 --- a/panels.permissions.yml +++ b/panels.permissions.yml @@ -8,12 +8,6 @@ view pane admin links: administer pane access: title: 'Configure access settings on Panel panes' description: 'Access rules (often also called visibility rules) can be configured on a per-pane basis. This permission allows users to configure those settings.' -use panels in place editing: - title: 'Use the Panels In-Place Editor' - description: 'Allows a user to utilize Panels'' In-Place Editor.' -change layouts in place editing: - title: 'Change layouts with the Panels In-Place Editor' - description: 'Allows a user to change layouts with the IPE.' administer advanced pane settings: title: 'Configure advanced settings on Panel panes' administer panels layouts: diff --git a/panels_ipe/css/panels_ipe.css b/panels_ipe/css/panels_ipe.css index 676a183..f1dc565 100644 --- a/panels_ipe/css/panels_ipe.css +++ b/panels_ipe/css/panels_ipe.css @@ -42,7 +42,7 @@ content: "\e904"; } -.ipe-icon.ipe-icon-place_content:before { +.ipe-icon.ipe-icon-manage_content:before { content: "\e905"; } @@ -77,6 +77,10 @@ content: "\e90a"; } +.ipe-icon.ipe-icon-search:before { + content: "\e90d"; +} + .ipe-icon.ipe-icon-configure { font-size: 20px; } @@ -103,6 +107,11 @@ margin: 0; } +/* Add a box shadow to the IPE tabs. */ +.ipe-tabs-content { + box-shadow: 3px -1px 2px 0 rgba(0, 0, 0, 0.3333); +} + /* Remove list styling from the output of the TabsView. */ .ipe-tabs { list-style: none; @@ -119,23 +128,28 @@ margin-bottom: -1px; background-color: white; border-top: 1px solid darkgray; + box-shadow: 3px -1px 2px 0 rgba(0, 0, 0, 0.3333); } .ipe-tab:first-child { border-left: 1px solid darkgray; border-top-left-radius: 5px; + box-shadow: -2px -1px 2px rgba(0, 0, 0, 0.3333); + right: -5px; } .ipe-tab:last-child { border-right: 1px solid darkgray; border-top-right-radius: 5px; + box-shadow: 2px -1px 2px rgba(0, 0, 0, 0.3333); } .ipe-tab a { + font-size: 13px; + text-transform: capitalize; color: black; - padding: 10px 5px 12px 5px; + padding: 10px 15px 12px 5px; display: block; - text-transform: uppercase; vertical-align: top; border: none; cursor: pointer; @@ -202,11 +216,28 @@ /* Show layouts as clickable things. */ .ipe-layout { cursor: pointer; + position: relative; display: inline-block; margin-right: 10px; vertical-align: top; } +/* Show the layout's label on hover. */ +.ipe-layout-label { + display: block; + position: absolute; + top: 5px; + font-weight: bold; + color: white; + text-shadow: 0 0 5px black, 1px 1px black; + opacity: 0; + transition: .2s; +} + +.ipe-layout:hover .ipe-layout-label { + opacity: 100; +} + /* Make sure image icons aren't t0o big */ .ipe-layout-image { width: 75px; @@ -267,11 +298,26 @@ div.ipe-actions-block { } .ipe-action-list [data-action-id="move"] select { + font-size: 14px; + margin: 0; background: transparent; border: none; text-transform: uppercase; } +.ipe-action-list [data-action-id="move"] option { + font-size: 14px; +} + + + +/* Fix contextual links hovering over action links. */ + +[data-block-id] [data-contextual-id].contextual { + /* Top is normally hard-set at 6px, this puts it below the action bar. */ + top: 35px; +} + /* Indicate that blocks are draggable */ [data-block-id].active { cursor: move; @@ -282,6 +328,16 @@ div.ipe-actions-block { border: 1px dashed #3c3c3c; } +/* Indicate an HTML request if a block is syncing. */ +[data-block-id].syncing:before { + float: left; + vertical-align: middle; + font-family: PanelsIPEIcon; + font-size: 24px; + content: "\e907"; + animation: ipe-spin 1s infinite linear; +} + /* This is used for highlighting new content on screen. */ .ipe-highlight { animation: ipe-blink .4s ease-in-out 2; @@ -325,15 +381,40 @@ div.ipe-actions-block { min-height: 70px; } +.ipe-category-picker-bottom.top-open { + border-top: 1px solid darkgray; +} + .ipe-category-picker-top { display: none; + overflow-y: scroll; } .ipe-category-picker-top.active { display: block; - padding: 0 20px 20px 20px; + padding: 10px 0 10px 0; max-height: 100%; - border-bottom: 1px solid darkgray; +} + +.ipe-category-picker-search { + border-bottom: 1px solid lightgray; + padding-bottom: 5px; +} + +.ipe-category-picker-search input { + display: inline-block; + width: inherit; + margin-left: 5px; +} + +.ipe-category-picker-search input[type="submit"] { + display: none; + background: white; + color: black; + border: 1px solid lightgray; + border-radius: 5px; + padding: 6px; + transition: .2s; } #panels-ipe-tray .ipe-category { @@ -345,9 +426,8 @@ div.ipe-actions-block { color: black; display: inline-block; padding: 10px; - text-transform: uppercase; + text-transform: capitalize; font-size: 15px; - font-weight: bold; border: 1px solid transparent; border-radius: 5px; transition: .2s; @@ -391,6 +471,16 @@ div.ipe-actions-block { margin-left: -10px; } +/* Style the create button a bit differently than other categories. */ +.ipe-create-category { + border: 1px solid lightgray; + background: #FBFBFB; +} + +.ipe-create-category:hover { + background: white; +} + .ipe-category-count { color: white; background: black; @@ -404,55 +494,84 @@ div.ipe-actions-block { vertical-align: middle; } -.ipe-block-content-type { - max-width: 250px; - overflow: hidden; +/* Display block picker elements in a flex grid. */ +.ipe-block-picker-list .ipe-category-picker-top.active { + display: inline-flex; + flex-flow: row wrap; + justify-content: center; } -.ipe-block-plugin { - text-transform: uppercase; - display: inline-block; - margin: 5px 20px 0 0; +.ipe-category-picker-top.active.form-displayed { + display: block; +} + +.ipe-blockpicker-item { + flex-basis: 240px; + width: 240px; + margin-left: 10px; + margin-top: 10px; text-align: left; - position: relative; } -.ipe-block-plugin-info { - display: inline-block; +.ipe-blockpicker-item-info { + font-size: 12px; } -.ipe-block-plugin h5 { - font-size: 14px; - font-weight: bold; +.ipe-blockpicker-item h5 { + position: relative; + bottom: 0; margin: 0; + display: inline-block; + padding: 10px 20px 10px 10px; + font-size: 12px; + line-height: 1.5em; + font-weight: bold; + text-transform: capitalize; } -.ipe-block-plugin p { +.ipe-blockpicker-item p { font-size: 12px; } -/* The "Add" button for Block Plugins. */ -.ipe-block-plugin a { - display: inline-block; - vertical-align: top; - font-size: 12px; - padding: 0 5px 0 5px; - border: 1px solid darkgray; - border-radius: 5px; +.ipe-blockpicker-item a { + border: 1px solid #333; + background: #FBFBFB; + display: flex; + flex-direction: column; + position: relative; + height: 100%; + border-radius: 0; + padding: 0; transition: .2s; color: inherit; cursor: pointer; } -.ipe-block-plugin a:hover { +.ipe-blockpicker-item a:after { + content: "+"; + display: inline-block; + position: absolute; + right: 10px; + top: 20%; + font-size: 16px; +} + +.ipe-blockpicker-item a:hover { + background: white; border-color: rgb(67, 125, 33); - color: inherit; + color: rgb(67, 125, 33); +} + +.ipe-block-content-type-info p { + padding: 0 10px 10px 10px; } /* Theme the category view. */ -.ipe-category-picker-top h4 { +.ipe-category-picker-top > h4 { text-transform: uppercase; + display: block; + width: 100%; border-bottom: 1px solid darkgray; padding: 5px; margin: 0 0 10px 0; @@ -461,7 +580,7 @@ div.ipe-actions-block { /* Theme the block plugin and layout forms. */ -.ipe-block-plugin-form,.ipe-layout-form,.ipe-block-type-form { +.ipe-block-form,.ipe-layout-form { text-align: left; margin: 0 auto; display: inline-block; @@ -502,7 +621,6 @@ div.ipe-actions-block { .ipe-form { max-width: 650px; padding: 5px; - overflow-y: scroll; } /* Vertical-tabs specific styling. */ @@ -521,39 +639,80 @@ div.ipe-actions-block { /* Styles required to do the card-flip affect. */ /* Credit to https://davidwalsh.name/css-flip for the original CSS. */ -.ipe-block-plugin-form .flip-container { +.ipe-block-form .flip-container { perspective: 1000; } /* flip the pane when hovered */ -.ipe-block-plugin-form.flipped .flipper, .ipe-block-plugin-form.flipped .flipper { +.ipe-block-form.flipped .flipper, .ipe-block-form.flipped .flipper { transform: rotateY(180deg); } /* flip speed goes here */ -.ipe-block-plugin-form .flipper { +.ipe-block-form .flipper { transition: 0.6s; transform-style: preserve-3d; position: relative; } /* hide back of pane during swap */ -.ipe-block-plugin-form .front, .ipe-block-plugin-form .back { +.ipe-block-form .front, .ipe-block-form .back { backface-visibility: hidden; background: white; overflow: hidden; } /* front pane, placed above back */ -.ipe-block-plugin-form .front { +.ipe-block-form .front { z-index: 2; /* for firefox 31 */ transform: rotateY(0deg); } /* back, initially hidden pane */ -.ipe-block-plugin-form .back { +.ipe-block-form .back { transform: rotateY(180deg); position: absolute; top: 0; right: 0; } + +/* Mobile specific styles. */ +@media screen and (max-width: 40em) { + + /* Only display the search "button" on mobile. */ + .ipe-category-picker-search input[type="submit"] { + display: inline-block; + } + + /* Hide tab titles on mobile. */ + .ipe-tab-title { + display: none; + } + + /* Increase tab padding on mobile. */ + .ipe-tab a { + padding: 10px 15px 12px 15px; + } + + /* Show block plugins in one column. */ + .ipe-blockpicker-item { + display: block; + width: inherit; + } + + /* Increase font size on block plugins. */ + .ipe-blockpicker-item h5 { + font-size: 16px; + } + + /* Put the actions below the block title. */ + div.ipe-actions { + display: block; + } + + /* Put actions to the right of the bar. */ + .ipe-actions ul.ipe-action-list { + text-align: right; + } + +} diff --git a/panels_ipe/fonts/ipeicons.woff b/panels_ipe/fonts/ipeicons.woff index 9f1e1b3..a85cb48 100755 Binary files a/panels_ipe/fonts/ipeicons.woff and b/panels_ipe/fonts/ipeicons.woff differ diff --git a/panels_ipe/fonts/selection.json b/panels_ipe/fonts/selection.json index 4a6bfd4..82fd134 100755 --- a/panels_ipe/fonts/selection.json +++ b/panels_ipe/fonts/selection.json @@ -4,26 +4,50 @@ { "icon": { "paths": [ - "M810.667 273.493l-60.16-60.16-238.507 238.507-238.507-238.507-60.16 60.16 238.507 238.507-238.507 238.507 60.16 60.16 238.507-238.507 238.507 238.507 60.16-60.16-238.507-238.507z" + "M661.333 597.333h-33.707l-11.947-11.52c41.813-48.64 66.987-111.787 66.987-180.48 0-153.173-124.16-277.333-277.333-277.333s-277.333 124.16-277.333 277.333 124.16 277.333 277.333 277.333c68.693 0 131.84-25.173 180.48-66.987l11.52 11.947v33.707l213.333 212.907 63.573-63.573-212.907-213.333zM405.333 597.333c-106.24 0-192-85.76-192-192s85.76-192 192-192 192 85.76 192 192-85.76 192-192 192z" ], "attrs": [], "isMulticolor": false, "grid": 0, "tags": [ - "ic_close_black_24px" + "ic_search_black_24px (1)" ] }, "attrs": [], "properties": { + "order": 17, + "id": 13, + "prevSize": 32, + "code": 59661, + "name": "ic_search_black_24px1" + }, + "setIdx": 0, + "setId": 5, + "iconIdx": 0 + }, + { + "icon": { + "paths": [ + "M810.667 273.493l-60.16-60.16-238.507 238.507-238.507-238.507-60.16 60.16 238.507 238.507-238.507 238.507 60.16 60.16 238.507-238.507 238.507 238.507 60.16-60.16-238.507-238.507z" + ], + "attrs": [], + "isMulticolor": false, + "tags": [ + "ic_close_black_24px" + ], + "grid": 0 + }, + "attrs": [], + "properties": { "order": 16, - "id": 12, + "id": 0, "prevSize": 32, "code": 59660, "name": "ic_close_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 0 + "setId": 5, + "iconIdx": 1 }, { "icon": { @@ -32,22 +56,22 @@ ], "attrs": [], "isMulticolor": false, - "grid": 0, "tags": [ "ic_note_add_black_24px" - ] + ], + "grid": 0 }, "attrs": [], "properties": { "order": 14, - "id": 11, + "id": 1, "prevSize": 32, "code": 59659, "name": "ic_note_add_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 1 + "setId": 5, + "iconIdx": 2 }, { "icon": { @@ -64,14 +88,14 @@ "attrs": [], "properties": { "order": 13, - "id": 0, + "id": 2, "prevSize": 32, "code": 59658, "name": "ic_cancel_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 2 + "setId": 5, + "iconIdx": 3 }, { "icon": { @@ -88,14 +112,14 @@ "attrs": [], "properties": { "order": 12, - "id": 1, + "id": 3, "prevSize": 32, "code": 59657, "name": "ic_settings_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 3 + "setId": 5, + "iconIdx": 4 }, { "icon": { @@ -112,14 +136,14 @@ "attrs": [], "properties": { "order": 6, - "id": 2, + "id": 4, "prevSize": 32, "code": 59656, "name": "ic_remove_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 4 + "setId": 5, + "iconIdx": 5 }, { "icon": { @@ -136,14 +160,14 @@ "attrs": [], "properties": { "order": 10, - "id": 3, + "id": 5, "prevSize": 32, "code": 59655, "name": "ic_sync_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 5 + "setId": 5, + "iconIdx": 6 }, { "icon": { @@ -160,14 +184,14 @@ "attrs": [], "properties": { "order": 9, - "id": 4, + "id": 6, "prevSize": 32, "code": 59648, "name": "ic_expand_less_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 6 + "setId": 5, + "iconIdx": 7 }, { "icon": { @@ -184,14 +208,14 @@ "attrs": [], "properties": { "order": 6, - "id": 5, + "id": 7, "prevSize": 32, "code": 59649, "name": "ic_expand_more_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 7 + "setId": 5, + "iconIdx": 8 }, { "icon": { @@ -208,14 +232,14 @@ "attrs": [], "properties": { "order": 5, - "id": 6, + "id": 8, "prevSize": 32, "code": 59650, "name": "ic_warning_black_24px" }, "setIdx": 0, - "setId": 4, - "iconIdx": 8 + "setId": 5, + "iconIdx": 9 }, { "icon": { @@ -232,14 +256,14 @@ "attrs": [], "properties": { "order": 4, - "id": 7, + "id": 9, "prevSize": 32, "code": 59651, "name": "tab_change_layout" }, "setIdx": 0, - "setId": 4, - "iconIdx": 9 + "setId": 5, + "iconIdx": 10 }, { "icon": { @@ -256,14 +280,14 @@ "attrs": [], "properties": { "order": 3, - "id": 8, + "id": 10, "prevSize": 32, "code": 59652, "name": "tab_edit" }, "setIdx": 0, - "setId": 4, - "iconIdx": 10 + "setId": 5, + "iconIdx": 11 }, { "icon": { @@ -280,14 +304,14 @@ "attrs": [], "properties": { "order": 4, - "id": 9, + "id": 11, "prevSize": 32, "code": 59653, "name": "tab_manage_content" }, "setIdx": 0, - "setId": 4, - "iconIdx": 11 + "setId": 5, + "iconIdx": 12 }, { "icon": { @@ -304,14 +328,14 @@ "attrs": [], "properties": { "order": 1, - "id": 10, + "id": 12, "prevSize": 32, "code": 59654, "name": "tab_save" }, "setIdx": 0, - "setId": 4, - "iconIdx": 12 + "setId": 5, + "iconIdx": 13 } ], "height": 1024, diff --git a/panels_ipe/js/models/BlockModel.js b/panels_ipe/js/models/BlockModel.js index ab6694f..930f83c 100644 --- a/panels_ipe/js/models/BlockModel.js +++ b/panels_ipe/js/models/BlockModel.js @@ -50,6 +50,13 @@ provider: null, /** + * The ID of the plugin for this block. + * + * @type {string} + */ + plugin_id: null, + + /** * The HTML content of the block. This is stored in the model as the * IPE doesn't actually care what the block's content is, the functional * elements of the model are the metadata. The BlockView renders this @@ -57,8 +64,26 @@ * * @type {string} */ - html: null + html: null, + /** + * Whether or not this block is currently in a syncing state. + * + * @type {bool} + */ + syncing: false + + }, + + /** + * @type {function} + * + * @return {string} + * A URL that can be used to refresh this Block model. Only fetch methods + * are supported currently. + */ + url: function () { + return Drupal.panels_ipe.urlRoot(drupalSettings) + '/block/' + this.get('uuid'); } }); diff --git a/panels_ipe/js/models/RegionModel.js b/panels_ipe/js/models/RegionModel.js index b5333a5..3523be8 100644 --- a/panels_ipe/js/models/RegionModel.js +++ b/panels_ipe/js/models/RegionModel.js @@ -39,6 +39,52 @@ * @see Drupal.panels_ipe.BlockCollection */ blockCollection: null + }, + + /** + * Checks if our BlockCollection contains a given Block UUID. + * + * @param {string} block_uuid + * The universally unique identifier of the block. + * + * @returns {boolean} + */ + hasBlock: function(block_uuid) { + return this.get('blockCollection').get(block_uuid) ? true : false; + }, + + /** + * Gets a Block from our BlockCollection based on its UUID. + * + * @param {string} block_uuid + * The universally unique identifier of the block. + * + * @returns {Drupal.panels_ipe.BlockModel|undefined} + * The block if it is inside this region. + */ + getBlock: function(block_uuid) { + return this.get('blockCollection').get(block_uuid); + }, + + /** + * Removes a Block from our BlockCollection based on its UUID. + * + * @param {Drupal.panels_ipe.BlockModel|string} block + * The block or it's universally unique identifier. + * @param {object} options + */ + removeBlock: function(block, options) { + this.get('blockCollection').remove(block, options); + }, + + /** + * Adds a new BlockModel to our BlockCollection. + * + * @param {Drupal.panels_ipe.BlockModel} block + * @param {object} options + */ + addBlock: function (block, options) { + this.get('blockCollection').add(block, options); } }); diff --git a/panels_ipe/js/panels_ipe.js b/panels_ipe/js/panels_ipe.js index 6dacbdb..84664d9 100644 --- a/panels_ipe/js/panels_ipe.js +++ b/panels_ipe/js/panels_ipe.js @@ -50,7 +50,7 @@ // the form is rendered. if (context.className == 'panels-ipe-block-plugin-form flip-container' && settings['panels_ipe']['toggle_preview']) { - var $form = $('.ipe-block-plugin-form'); + var $form = $('.ipe-block-form'); // Flip the form. $form.toggleClass('flipped'); @@ -72,8 +72,16 @@ // A new Block Content entity has been created. Trigger an app-level event // to switch tabs and open the placement form. if (settings['panels_ipe']['new_block_content']) { - Drupal.panels_ipe.app.trigger('addContentBlock', settings['panels_ipe']['new_block_content']); + var data = settings['panels_ipe']['new_block_content']; delete settings['panels_ipe']['new_block_content']; + Drupal.panels_ipe.app.trigger('addContentBlock', data); + } + + // A Block Content entity has been edited. + if (settings['panels_ipe']['edit_block_content']) { + var data = settings['panels_ipe']['edit_block_content']; + delete settings['panels_ipe']['edit_block_content']; + Drupal.panels_ipe.app.trigger('editContentBlockDone', data); } } }; @@ -94,15 +102,14 @@ var tab_collection = new Drupal.panels_ipe.TabCollection(); if (settings.panels_ipe.layout.changeable) { - tab_collection.add({title: 'Change Layout', id: 'change_layout'}); + tab_collection.add(createTabModel(Drupal.t('Change Layout'), 'change_layout')); } - tab_collection.add({title: 'Create Content', id: 'create_content'}); - tab_collection.add({title: 'Place Content', id: 'place_content'}); + tab_collection.add(createTabModel(Drupal.t('Manage Content'), 'manage_content')); // The edit/save/cancel tabs are special, and are tracked by our app. - var edit_tab = new Drupal.panels_ipe.TabModel({title: 'Edit', id: 'edit'}); - var save_tab = new Drupal.panels_ipe.TabModel({title: 'Save', id: 'save'}); - var cancel_tab = new Drupal.panels_ipe.TabModel({title: 'Cancel', id: 'cancel'}); + var edit_tab = createTabModel(Drupal.t('Edit'), 'edit'); + var save_tab = createTabModel(Drupal.t('Save'), 'save'); + var cancel_tab = createTabModel(Drupal.t('Cancel'), 'cancel'); tab_collection.add(edit_tab); tab_collection.add(save_tab); tab_collection.add(cancel_tab); @@ -121,8 +128,7 @@ if (settings.panels_ipe.layout.changeable) { tab_views.change_layout = new Drupal.panels_ipe.LayoutPicker(); } - tab_views.create_content = new Drupal.panels_ipe.BlockContentPicker(); - tab_views.place_content = new Drupal.panels_ipe.BlockPicker(); + tab_views.manage_content = new Drupal.panels_ipe.BlockPicker(); // Create an AppView instance. Drupal.panels_ipe.app_view = new Drupal.panels_ipe.AppView({ @@ -171,8 +177,15 @@ // initializing and ready to render. Backbone.trigger('PanelsIPEInitialized'); - // Render our AppView. - $('body').append(Drupal.panels_ipe.app_view.render().$el); + // Render our AppView, without rendering the layout. + $('body').append(Drupal.panels_ipe.app_view.render(false).$el); + + // Set our initial URL root. + Drupal.panels_ipe.setUrlRoot(settings); + + function createTabModel(title, id) { + return new Drupal.panels_ipe.TabModel({title: title, id: id}); + } }; Drupal.panels_ipe.setFlipperHeight = function ($form) { @@ -188,14 +201,11 @@ $current_side = $form.find('.flipper > .back'); } - // If the new side is larger than the current side, change the height. - if ($new_side.outerHeight() > $current_side.outerHeight()) { - $current_side.animate({height: $new_side.outerHeight() + 10}, 600); - } + $current_side.animate({height: $new_side.outerHeight() + 10}, 600); }; /** - * Returns the urlRoot for all callbacks + * Returns the urlRoot for all callbacks. * * @param {Object} settings * The contextual drupalSettings. @@ -204,8 +214,18 @@ * A base path for most other URL callbacks in this App. */ Drupal.panels_ipe.urlRoot = function (settings) { + return settings.panels_ipe.url_root; + }; + + /** + * Sets the urlRoot for all callbacks. + * + * @param {Object} settings + * The contextual drupalSettings. + */ + Drupal.panels_ipe.setUrlRoot = function (settings) { var panels_display = settings.panels_ipe.panels_display; - return settings.path.baseUrl + 'admin/panels_ipe/variant/' + panels_display.storage_type + '/' + panels_display.storage_id; + settings.panels_ipe.url_root = settings.path.baseUrl + 'admin/panels_ipe/variant/' + panels_display.storage_type + '/' + panels_display.storage_id; }; }(jQuery, _, Backbone, Drupal)); diff --git a/panels_ipe/js/views/AppView.js b/panels_ipe/js/views/AppView.js index 6f9f823..0449fdc 100644 --- a/panels_ipe/js/views/AppView.js +++ b/panels_ipe/js/views/AppView.js @@ -18,6 +18,14 @@ template: _.template('
'), /** + * @type {function} + */ + template_content_block_edit: _.template( + '

' + Drupal.t('Edit existing "<%- label %>" content') + '

' + + '
' + ), + + /** * @type {Drupal.panels_ipe.TabsView} */ tabsView: null, @@ -62,6 +70,8 @@ this.listenTo(this.model, 'addBlockPlugin', this.addBlockPlugin); this.listenTo(this.model, 'configureBlock', this.configureBlock); this.listenTo(this.model, 'addContentBlock', this.addContentBlock); + this.listenTo(this.model, 'editContentBlock', this.editContentBlock); + this.listenTo(this.model, 'editContentBlockDone', this.editContentBlockDone); // Listen to tabs that don't have associated BackboneViews. this.listenTo(this.model.get('editTab'), 'change:active', this.clickEditTab); @@ -75,10 +85,16 @@ /** * Appends the IPE tray to the bottom of the screen. * + * @param {bool} render_layout + * Whether or not the layout should be rendered. Useful for just calling + * render on UI elements and not content. + * * @return {Drupal.panels_ipe.AppView} * Returns this, for chaining. */ - render: function () { + render: function (render_layout) { + render_layout = typeof render_layout !== 'undefined' ? render_layout : true; + // Empty our list. this.$el.html(this.template(this.model.toJSON())); // Add our tab collection to the App. @@ -88,7 +104,7 @@ this.$el.toggleClass('unsaved', this.model.get('unsaved')); // Re-render our layout. - if (this.layoutView) { + if (this.layoutView && render_layout) { this.layoutView.render(); } return this; @@ -110,6 +126,9 @@ this.model.get('layout').set({active: true}); this.$el.addClass('active'); + + // Add a top-level body class. + $('body').addClass('panels-ipe-active'); }, /** @@ -128,6 +147,9 @@ this.model.get('layout').set({active: false}); this.$el.removeClass('active'); + + // Remove our top-level body class. + $('body').removeClass('panels-ipe-active'); }, /** @@ -137,6 +159,10 @@ * The new layout model. */ changeLayout: function (layout) { + // Early render the tabs and layout - if changing the Layout was the first + // action on the page the Layout would have never been rendered. + this.render(); + // Grab all the blocks from the current layout. var regions = this.model.get('layout').get('regionCollection'); var block_collection = new Drupal.panels_ipe.BlockCollection(); @@ -212,13 +238,19 @@ */ clickCancelTab: function () { var cancel_tab = this.model.get('cancelTab'); - if (cancel_tab.get('active') && !cancel_tab.get('loading')) { - // Remove our changes and refresh the page. - cancel_tab.set({loading: true}); - $.ajax(Drupal.panels_ipe.urlRoot(drupalSettings) + '/cancel') - .done(function (data) { - location.reload(); - }); + + if (confirm(Drupal.t('Are you sure you want to cancel your changes?'))) { + if (cancel_tab.get('active') && !cancel_tab.get('loading')) { + // Remove our changes and refresh the page. + cancel_tab.set({loading: true}); + $.ajax(Drupal.panels_ipe.urlRoot(drupalSettings) + '/cancel') + .done(function (data) { + location.reload(); + }); + } + } + else { + cancel_tab.set('active', false, {silent: true}); } }, @@ -233,15 +265,11 @@ addBlockPlugin: function (block, region) { this.layoutView.addBlock(block, region); - // Mark all tabs as inactive and close the view. - this.tabsView.collection.each(function (tab) { - tab.set('active', false); - }); - // Indicate that there are unsaved changes in the app. this.model.set('unsaved', true); - this.tabsView.closeTabContent(); + // Switch back to the edit tab. + this.tabsView.switchTab('edit'); }, /** @@ -251,9 +279,12 @@ * The Block that needs to have its form opened. */ configureBlock: function (block) { - this.tabsView.tabViews['place_content'].activeCategory = 'On Screen'; - this.tabsView.tabViews['place_content'].autoClick = '[data-existing-block-id=' + block.get('uuid') + ']'; - this.tabsView.switchTab('place_content'); + var info = { + url: Drupal.panels_ipe.urlRoot(drupalSettings) + '/block_plugins/' + block.get('id') + '/block/' + block.get('uuid') + '/form', + model: block + }; + + this.loadBlockForm(info); }, /** @@ -263,19 +294,55 @@ * The UUID of the newly added Content Block. */ addContentBlock: function (uuid) { - // Deactivate the current category in our Create Content tab. - this.tabsView.tabViews['create_content'].activeCategory = null; - // Delete the current block plugin collection so that a new one is pulled in. - this.tabsView.tabViews['place_content'].collection = null; + delete this.tabsView.tabViews['manage_content'].collection; // Auto-click the new block, which we know is in the "Custom" category. // @todo When configurable categories are in, determine this from the // passed-in settings. - this.tabsView.tabViews['place_content'].autoClick = '[data-plugin-id="block_content:' + uuid + '"]'; - this.tabsView.tabViews['place_content'].activeCategory = 'Custom'; + this.tabsView.tabViews['manage_content'].autoClick = '[data-plugin-id="block_content:' + uuid + '"]'; + this.tabsView.tabViews['manage_content'].activeCategory = 'Custom'; + + this.tabsView.tabViews['manage_content'].render(); + }, + + /** + * Opens the Manage Content tray when editing an existing Content Block. + * + * @param {Drupal.panels_ipe.BlockModel} block + * The Block that needs to have its form opened. + */ + editContentBlock: function (block) { + var plugin_split = block.get('id').split(':'); + + var info = { + url: Drupal.panels_ipe.urlRoot(drupalSettings) + '/block_content/edit/block/' + plugin_split[1] + '/form', + model: block + }; - this.tabsView.switchTab('place_content'); + this.loadBlockForm(info, this.template_content_block_edit); + }, + + /** + * React after a content block has been edited. + * + * @param {string} block_content_uuid + * The UUID of the Block Content entity that was edited. + */ + editContentBlockDone: function(block_content_uuid) { + // Find all on-screen Blocks that render this Content Block and refresh + // them from the server. + this.layoutView.model.get('regionCollection').each(function (region) { + var id = 'block_content:' + block_content_uuid; + var blocks = region.get('blockCollection').where({id: id}); + + for (var i in blocks) { + blocks[i].set('syncing', true); + blocks[i].fetch(); + } + }); + + this.tabsView.switchTab('edit'); }, /** @@ -286,8 +353,41 @@ this.model.get('cancelTab').set('hidden', !this.model.get('unsaved')); this.model.get('saveTab').set('hidden', !this.model.get('unsaved')); - // Re-render ourselves. - this.render(); + // Re-render ourselves, pass "false" as we don't need to re-render the + // layout, just the tabs. + this.render(false); + }, + + /** + * Helper function to switch tabs to Manage Content and load an arbitrary + * form. + * + * @param {object} info + * An object compatible with Drupal.panels_ipe.CategoryView.loadForm() + * @param {function} template + * An optional callback function for the form template. + */ + loadBlockForm: function (info, template) { + // We're going to open the manage content tab, which may take time to + // render. Load the Block edit form on render. + var manage_content = this.tabsView.tabViews['manage_content']; + manage_content.on('render', function () { + + if (template) { + manage_content.loadForm(info, template); + } + else { + manage_content.loadForm(info); + } + + // We only need this event to trigger once. + manage_content.off('render', null, this); + }, this); + + // Disable the active category to avoid confusion. + manage_content.activeCategory = null; + + this.tabsView.switchTab('manage_content'); } }); diff --git a/panels_ipe/js/views/BlockContentPicker.js b/panels_ipe/js/views/BlockContentPicker.js deleted file mode 100644 index 3b26f90..0000000 --- a/panels_ipe/js/views/BlockContentPicker.js +++ /dev/null @@ -1,151 +0,0 @@ - -/** - * @file - * Renders a list of existing Block Content Types for selection. - * - * see Drupal.panels_ipe.BlockContentTypeCollection - * - */ - -(function ($, _, Backbone, Drupal, drupalSettings) { - - 'use strict'; - - Drupal.panels_ipe.BlockContentPicker = Drupal.panels_ipe.CategoryView.extend(/** @lends Drupal.panels_ipe.BlockContentPicker# */{ - - /** - * @type {function} - */ - template_category: _.template( - '' + - '
' + - '

<%- label %>

' + - '

<%- trimmed_description %>

' + - '
' + - '
' - ), - - /** - * @type {function} - */ - template_form: _.template( - '

Add new <%- label %> content

' + - '
' - ), - - /** - * @type {function} - */ - template_loading: _.template( - '' - ), - - /** - * @type {object} - */ - events: { - 'click [data-category]': 'toggleBlockType' - }, - - /** - * @constructs - * - * @augments Backbone.View - * - * @param {Object} options - * An object containing the following keys: - * @param {Drupal.panels_ipe.BlockContentTypeCollection} options.collection - * An optional initial collection. - */ - initialize: function (options) { - if (options && options.collection) { - this.collection = options.collection; - } - }, - - /** - * Renders the selection menu for picking Blocks. - * - * @return {Drupal.panels_ipe.BlockContentPicker} - * Return this, for chaining. - */ - render: function () { - // Empty ourselves. - this.$el.html(this.template()); - - // Initialize our BlockPluginCollection if it doesn't already exist. - if (!this.collection) { - var self = this; - - // Indicate an AJAX request. - this.$el.html(this.template_loading()); - - // Fetch a collection of block content types from the server. - this.collection = new Drupal.panels_ipe.BlockContentTypeCollection(); - this.collection.fetch().done(function () { - // We have a collection now, re-render ourselves. - self.render(); - }); - - return this; - } - - // Note that the parent method renderCategories() is not called, as we - // are only using the base View for its active logic and styling. - this.collection.each(function (model) { - var active = this.activeCategory == model.id; - model.set('active', active); - - var template_vars = model.toJSON(); - - // Reduce the length of the Block Content description if needed. - template_vars.trimmed_description = template_vars.description; - if (template_vars.trimmed_description.length > 30) { - template_vars.trimmed_description = template_vars.description.substring(0, 30) + '...'; - } - - this.$('.ipe-categories').append(this.template_category(template_vars)); - }, this); - - // Check if a category is selected. If so, mark the top tray as active. - if (this.activeCategory) { - this.$('.ipe-category-picker-top').addClass('active'); - } - - return this; - }, - - /** - * Informs the CategoryView of our form's callback URL. - * - * @param {Object} e - * The event object. - * - * @return {Object} - * An object containing the properties "url" and "model". - */ - getFormInfo: function(e) { - // Get the current block content type (which we store as the category). - var type = $(e.currentTarget).data('category'); - - var block_content_type = this.collection.get(type); - var url = Drupal.panels_ipe.urlRoot(drupalSettings) + '/block_content/' + type + '/form'; - - return {url: url, model: block_content_type}; - }, - - /** - * Overrides the default CategoryView toggleCategory method to display a - * form instead of models related to a certain category. - * - * @param {Object} e - * The event object. - */ - toggleBlockType: function(e) { - this.toggleCategory(e); - this.displayForm(e); - } - - }); - -}(jQuery, _, Backbone, Drupal, drupalSettings)); diff --git a/panels_ipe/js/views/BlockPicker.js b/panels_ipe/js/views/BlockPicker.js index 78fa6b4..c4a3ba0 100644 --- a/panels_ipe/js/views/BlockPicker.js +++ b/panels_ipe/js/views/BlockPicker.js @@ -25,37 +25,57 @@ collection: null, /** + * @type {Drupal.panels_ipe.BlockContentTypeCollection} + */ + contentCollection: null, + + /** * @type {function} */ template_plugin: _.template( - '
' + - '
' + - '
<%- label %>
' + - '

Provider: <%- provider %>

' + - '
' + - ' Add' + + '' ), /** * @type {function} */ - template_existing: _.template( - '
' + - '
' + - '
<%- label %>
' + - '

Provider: <%- provider %>

' + - '
' + - ' Configure' + + template_content_type: _.template( + '' ), /** * @type {function} */ + template_create_button: _.template( + '' + + ' ' + + ' <%- name %>' + + '' + ), + + /** + * @type {function} + */ template_form: _.template( - '

Configure <%- label %> block

' + - '
' + '<% if (typeof(plugin_id) !== "undefined") { %>' + + '

' + Drupal.t('Configure <%- label %> block') + '

' + + '<% } else { %>' + + '

' + Drupal.t('Create new <%- label %> content') + '

' + + '<% } %>' + + '
' ), /** @@ -70,7 +90,7 @@ */ events: { 'click .ipe-block-plugin [data-plugin-id]': 'displayForm', - 'click .ipe-block-plugin [data-existing-block-id]': 'displayForm' + 'click .ipe-block-type [data-block-type]': 'displayForm' }, /** @@ -87,6 +107,9 @@ if (options && options.collection) { this.collection = options.collection; } + + this.on('tabActiveChange', this.tabActiveChange, this); + // Extend our parent's events. _.extend(this.events, Drupal.panels_ipe.CategoryView.prototype.events); }, @@ -98,45 +121,47 @@ * Return this, for chaining. */ render: function () { - var self = this; + var create_active = this.activeCategory === 'Create Content'; - // Initialize our BlockPluginCollection if it doesn't already exist. + // Initialize our collections if they don't already exist. if (!this.collection) { - // Indicate an AJAX request. - this.$el.html(this.template_loading()); - - // Fetch a collection of block plugins from the server. - this.collection = new Drupal.panels_ipe.BlockPluginCollection(); - this.collection.fetch().done(function () { - // We have a collection now, re-render ourselves. - self.render(); - }); - + this.fetchCollection('default'); + return this; + } + else if (create_active && !this.contentCollection) { + this.fetchCollection('content'); return this; } // Render our categories. this.renderCategories(); - // If we're viewing the current layout tab, show a custom item. - var on_screen_count = 0; - Drupal.panels_ipe.app.get('layout').get('regionCollection').each(function (region) { - region.get('blockCollection').each(function (block) { - if (self.activeCategory && self.activeCategory == 'On Screen') { - block.set('region', region.get('name')); - self.$('.ipe-category-picker-top').append(self.template_item(block)); + // Add a unique class to our top region to scope CSS. + this.$el.addClass('ipe-block-picker-list'); + + // Prepend a custom button for creating content. + this.$('.ipe-categories').prepend(this.template_create_button({ + name: 'Create Content', + active: create_active + })); + + // If the create content category is active, render items in our top + // region. + if (create_active) { + // Hide the search box. + this.$('.ipe-category-picker-search').hide(); + + this.contentCollection.each(function (block_content_type) { + var template_vars = block_content_type.toJSON(); + + // Reduce the length of the description if needed. + template_vars.trimmed_description = template_vars.description; + if (template_vars.trimmed_description.length > 30) { + template_vars.trimmed_description = template_vars.description.substring(0, 30) + '...'; } - ++on_screen_count; - }); - }); - // Prepend on screen blocks to our collection. - if (on_screen_count > 0) { - this.$('.ipe-categories').prepend(this.template_category({ - name: 'On Screen', - count: on_screen_count, - active: this.activeCategory === 'On Screen' - })); + this.$('.ipe-category-picker-top').append(this.template_content_type(template_vars)); + }, this); } // Check if we need to automatically select one item. @@ -145,6 +170,8 @@ this.autoClick = null; } + this.trigger('render'); + return this; }, @@ -157,14 +184,21 @@ * @return {string} * The rendered block plugin. */ - template_item: function(block_plugin) { - // This is an existing block. - if (block_plugin.get('uuid')) { - return this.template_existing(block_plugin.toJSON()); + template_item: function (block_plugin) { + var template_vars = block_plugin.toJSON(); + + // Not all blocks have labels, add a default if necessary. + if (!template_vars.label) { + template_vars.label = Drupal.t('No label'); } - else { - return this.template_plugin(block_plugin.toJSON()); + + // Reduce the length of the Block label if needed. + template_vars.trimmed_label = template_vars.label; + if (template_vars.trimmed_label.length > 30) { + template_vars.trimmed_label = template_vars.label.substring(0, 30) + '...'; } + + return this.template_plugin(template_vars); }, /** @@ -176,36 +210,58 @@ * @return {Object} * An object containing the properties "url" and "model". */ - getFormInfo: function(e) { - // Get the current plugin_id. + getFormInfo: function (e) { + // Get the current plugin_id or type. var plugin_id = $(e.currentTarget).data('plugin-id'); + var url = Drupal.panels_ipe.urlRoot(drupalSettings); + var model; // Generate a base URL for the form. - var layout_id = Drupal.panels_ipe.app.get('layout').get('id'); - var url = Drupal.panels_ipe.urlRoot(drupalSettings) + '/layout/' + layout_id + '/block_plugins/'; - - var plugin; - - // This is a new block. if (plugin_id) { - plugin = this.collection.get(plugin_id); - url += plugin_id + '/form'; + model = this.collection.get(plugin_id); + url += '/block_plugins/' + plugin_id + '/form'; } - // This is an existing block. else { - // Get the Block UUID and Region Name - var block_id = $(e.currentTarget).data('existing-block-id'); - var region_name = $(e.currentTarget).data('existing-region-name'); + var block_type = $(e.currentTarget).data('block-type'); + + model = this.contentCollection.get(block_type); + url += '/block_content/' + block_type + '/form'; + } + + return {url: url, model: model}; + }, + + /** + * Fetches a collection from the server and re-renders the View. + * + * @param {string} type + * The type of collection to fetch. + */ + fetchCollection: function (type) { + var collection; + var self = this; + + if (type == 'default') { + // Indicate an AJAX request. + this.$el.html(this.template_loading()); - // Get the Block plugin - plugin = Drupal.panels_ipe.app.get('layout').get('regionCollection') - .get(region_name).get('blockCollection').get(block_id); - plugin_id = plugin.get('id'); + // Fetch a collection of block plugins from the server. + this.collection = new Drupal.panels_ipe.BlockPluginCollection(); + collection = this.collection; + } + else { + // Indicate an AJAX request. + this.$('.ipe-category-picker-top').html(this.template_loading()); - url += plugin_id + '/block/' + block_id + '/form'; + // Fetch a collection of block content types from the server. + this.contentCollection = new Drupal.panels_ipe.BlockContentTypeCollection(); + collection = this.contentCollection; } - return {url: url, model: plugin}; + collection.fetch().done(function () { + // We have a collection now, re-render ourselves. + self.render(); + }); } }); diff --git a/panels_ipe/js/views/BlockView.js b/panels_ipe/js/views/BlockView.js index ff86370..9c8ba63 100644 --- a/panels_ipe/js/views/BlockView.js +++ b/panels_ipe/js/views/BlockView.js @@ -15,8 +15,8 @@ * @type {function} */ template_actions: _.template( - '
' + - '
Block: <%- label %>
' + + '
' + + '
' + Drupal.t('Block: <%- label %>') + '
' + '
    ' + '
  • ' + ' ' + @@ -28,11 +28,16 @@ ' ' + '
  • ' + '
  • ' + - ' ' + + ' ' + '
  • ' + '
  • ' + ' ' + '
  • ' + + '<% if (plugin_id == "block_content") { %>' + + '
  • ' + + ' ' + + '
  • ' + + '<% } %>' + '
' + '
' ), @@ -61,8 +66,8 @@ if (options.el && !this.model.get('html')) { this.model.set({html: this.$el.prop('outerHTML')}); } - this.listenTo(this.model, 'reset', this.render); - this.listenTo(this.model, 'change:active', this.render); + this.listenTo(this.model, 'sync', this.finishedSync); + this.listenTo(this.model, 'change:syncing', this.render); }, /** @@ -76,9 +81,6 @@ this.$el.replaceWith(this.model.get('html')); this.setElement("[data-block-id='" + this.model.get('uuid') + "']"); - // Attach any Drupal behaviors. - Drupal.attachBehaviors(this.el); - // We modify our content if the IPE is active. if (this.model.get('active')) { // Prepend the ipe-actions header. @@ -111,7 +113,40 @@ }); } + // Add a special class if we're currently syncing HTML from the server. + if (this.model.get('syncing')) { + this.$el.addClass('syncing'); + } + + return this; + }, + + /** + * Overrides the default remove function to make a copy of our current HTML + * into the Model for future rendering. This is required as modules like + * Quickedit modify Block HTML without our knowledge. + * + * @returns {Drupal.panels_ipe.BlockView} + */ + remove: function () { + // Remove known augmentations to HTML so that they do not persist. + this.$('.ipe-actions-block').remove(); + this.$el.removeClass('ipe-highlight active'); + + // Update our Block model HTML based on our current visual state. + this.model.set({html: this.$el.prop('outerHTML')}); + + // Call the normal Backbow.view.remove() routines. + this._removeElement(); + this.stopListening(); return this; + }, + + /** + * Reacts to our model being synced from the server. + */ + finishedSync: function () { + this.model.set('syncing', false); } }); diff --git a/panels_ipe/js/views/CategoryView.js b/panels_ipe/js/views/CategoryView.js index 12e13ca..7d93636 100644 --- a/panels_ipe/js/views/CategoryView.js +++ b/panels_ipe/js/views/CategoryView.js @@ -25,10 +25,25 @@ collection: null, /** + * The attribute that we should search for. Defaults to "label". + * + * @type {string} + */ + searchAttribute: 'label', + + /** * @type {function} */ template: _.template( - '
' + '' + + '
' + + '
' + + '
' + + '
' ), /** @@ -59,7 +74,8 @@ * @type {object} */ events: { - 'click [data-category]': 'toggleCategory' + 'click [data-category]': 'toggleCategory', + 'keyup .ipe-category-picker-search input[type="text"]': 'searchCategories' }, /** @@ -76,6 +92,8 @@ if (options && options.collection) { this.collection = options.collection; } + + this.on('tabActiveChange', this.tabActiveChange, this); }, /** @@ -86,7 +104,14 @@ */ renderCategories: function () { // Empty ourselves. - this.$el.html(this.template()); + var search_prompt; + if (this.activeCategory) { + search_prompt = Drupal.t('Search current category'); + } + else { + search_prompt = Drupal.t('Search all categories'); + } + this.$el.html(this.template({search_prompt: search_prompt})); // Get a list of categories from the collection. var categories_count = {}; @@ -113,13 +138,23 @@ if (this.activeCategory) { var $top = this.$('.ipe-category-picker-top'); $top.addClass('active'); + this.$('.ipe-category-picker-bottom').addClass('top-open'); this.collection.each(function (model) { if (model.get('category') === this.activeCategory) { $top.append(this.template_item(model)); } }, this); + + // Add a top-level body class. + $('body').addClass('panels-ipe-category-picker-top-open'); + } + else { + // Remove our top-level body class. + $('body').removeClass('panels-ipe-category-picker-top-open'); } + this.setTopMaxHeight(); + return this; }, @@ -153,7 +188,9 @@ if (animation === 'slideUp') { // Close the tab, then re-render. var self = this; - this.$('.ipe-category-picker-top')[animation]('fast', function () { self.render(); }); + this.$('.ipe-category-picker-top')[animation]('fast', function () { + self.render(); + }); } else if (animation === 'slideDown') { // We need to render first as hypothetically nothing is open. @@ -178,19 +215,87 @@ getFormInfo: function(e) {}, /** - * Displays a Configuration form in our top region. + * Determines form info from the current click event and displays a form. * * @param {Object} e * The event object. */ displayForm: function (e) { - var self = this; - var info = this.getFormInfo(e); // Indicate an AJAX request. + this.loadForm(info); + }, + + /** + * Reacts to the search field changing and displays category items based on + * our search. + */ + searchCategories: function (e) { + // Grab the formatted search from out input field. + var search = $(e.currentTarget).val().trim().toLowerCase(); + + // If the search is empty, re-render the view. + if (search.length == 0) { + this.render(); + this.$('.ipe-category-picker-search input').focus(); + return; + } + + // Filter our collection based on the input. + var results = this.collection.filter(function(model) { + var attribute = model.get(this.searchAttribute); + return attribute.toLowerCase().indexOf(search) != -1; + }, this); + + // Empty ourselves. + var $top = this.$('.ipe-category-picker-top'); + $top.empty(); + + // Render categories that matched the search. + if (results.length > 0) { + $top.addClass('active'); + this.$('.ipe-category-picker-bottom').addClass('top-open'); + + for (var i in results) { + // If a category is empty, search within that category. + if (this.activeCategory) { + if (results[i].get('category') === this.activeCategory) { + $top.append(this.template_item(results[i])); + } + } + else { + $top.append(this.template_item(results[i])); + } + } + + $('body').addClass('panels-ipe-category-picker-top-open'); + } + else { + $top.removeClass('active'); + $('body').removeClass('panels-ipe-category-picker-top-open'); + } + + this.setTopMaxHeight(); + }, + + /** + * Displays a configuration form in our top region. + * + * @param {Object} info + * An object containing the form URL the model for our form template. + * @param {function} template + * An optional callback function for the form template. + */ + loadForm: function(info, template) { + template = template || this.template_form; + var self = this; + + // Hide the search box. + this.$('.ipe-category-picker-search').fadeOut('fast'); + this.$('.ipe-category-picker-top').fadeOut('fast', function () { - self.$('.ipe-category-picker-top').html(self.template_form(info.model.toJSON())); + self.$('.ipe-category-picker-top').html(template(info.model.toJSON())); self.$('.ipe-category-picker-top').fadeIn('fast'); // Setup the Drupal.Ajax instance. @@ -203,9 +308,13 @@ ajax.options.complete = function () { self.$('.ipe-category-picker-top .ipe-icon-loading').remove(); - self.setFormMaxHeight(); + self.setTopMaxHeight(); + + // Remove the inline display style and add a unique class. + self.$('.ipe-category-picker-top').css('display', '').addClass('form-displayed'); - self.$('.ipe-category-picker-top *').hide().fadeIn(); + self.$('.ipe-category-picker-top').hide().fadeIn(); + self.$('.ipe-category-picker-bottom').addClass('top-open'); }; // Make the Drupal AJAX request. @@ -214,20 +323,37 @@ }, /** - * Calculates and sets maximum height of our form based on known floating - * and fixed elements. + * Responds to our associated tab being opened/closed. + * + * @param {bool} state + * Whether or not our associated tab is open. + */ + tabActiveChange: function (state) { + $('body').toggleClass('panels-ipe-category-picker-top-open', state); + }, + + /** + * Calculates and sets maximum height of our top area based on known + * floating and fixed elements. */ - setFormMaxHeight: function() { + setTopMaxHeight: function () { // Calculate the combined height of (known) floating elements. - var used_height = $('#toolbar-item-administration-tray:visible').outerHeight() + - $('#toolbar-bar').outerHeight() + - this.$('.ipe-category-picker-bottom').outerHeight(); + var used_height = this.$('.ipe-category-picker-bottom').outerHeight() + + this.$('.ipe-category-picker-search').outerHeight() + + $('.ipe-tabs').outerHeight(); + + // Add optional toolbar support. + var toolbar = $('#toolbar-bar'); + if (toolbar.length > 0) { + used_height += $('#toolbar-item-administration-tray:visible').outerHeight() + + toolbar.outerHeight(); + } - // 175 (px) is an arbitrary offset, just to give padding on top. - var max_height = $(window).height() - used_height - 175; + // The .ipe-category-picker-top padding is 30 pixels, plus five for margin. + var max_height = $(window).height() - used_height - 35; // Set the form's max height. - this.$('.ipe-form').css('max-height', max_height); + this.$('.ipe-category-picker-top').css('max-height', max_height); } }); diff --git a/panels_ipe/js/views/LayoutPicker.js b/panels_ipe/js/views/LayoutPicker.js index d45b2c7..097995f 100644 --- a/panels_ipe/js/views/LayoutPicker.js +++ b/panels_ipe/js/views/LayoutPicker.js @@ -15,14 +15,19 @@ * @type {function} */ template_form: _.template( - '

Configure <%- label %> layout

' + + '

' + Drupal.t('Configure <%- label %> layout') + '

' + '
' ), /** * @type {function} */ - template_layout: _.template('
  • <%- label %>
  • '), + template_layout: _.template( + '
  • ' + + ' <%- label %>' + + ' <%- label %>' + + '
  • ' + ), /** * @type {function} @@ -89,18 +94,21 @@ this.renderCategories(); // Flag the current layout. - var current_layout_text = Drupal.t('

    @text

    ', {'@text': 'Current Layout'}); + var current_layout_text = '

    ' + Drupal.t('Current Layout') + '

    '; this.$('[data-layout-id="' + current_layout + '"]').append(current_layout_text); // Prepend the current layout as its own category. this.$('.ipe-categories').prepend(this.template_category({ - name: 'Current Layout', + name: Drupal.t('Current Layout'), count: null, active: this.activeCategory === 'Current Layout' })); // If we're viewing the current layout tab, show a custom item. if (this.activeCategory && this.activeCategory == 'Current Layout') { + // Hide the search box. + this.$('.ipe-category-picker-search').hide(); + this.collection.each(function (layout) { if (Drupal.panels_ipe.app.get('layout').get('id') == layout.get('id')) { this.$('.ipe-category-picker-top').append(this.template_item(layout)); diff --git a/panels_ipe/js/views/LayoutView.js b/panels_ipe/js/views/LayoutView.js index ffd9097..265ba97 100644 --- a/panels_ipe/js/views/LayoutView.js +++ b/panels_ipe/js/views/LayoutView.js @@ -16,7 +16,7 @@ */ template_region_actions: _.template( '
    ' + - '
    Region: <%- name %>
    ' + + '
    ' + Drupal.t('Region: <%- name %>') + '
    ' + '
      ' + '
      ' ), @@ -52,11 +52,12 @@ events: { 'mousedown [data-action-id="move"] > select': 'showBlockRegionList', 'blur [data-action-id="move"] > select': 'hideBlockRegionList', - 'change [data-action-id="move"] > select': 'selectBlockRegionList', + 'change [data-action-id="move"] > select': 'newRegionSelected', 'click [data-action-id="up"]': 'moveBlock', 'click [data-action-id="down"]': 'moveBlock', 'click [data-action-id="remove"]': 'removeBlock', 'click [data-action-id="configure"]': 'configureBlock', + 'click [data-action-id="edit-content-block"]': 'editContentBlock', 'drop .ipe-droppable': 'dropBlock' }, @@ -85,6 +86,8 @@ if (this.model.get('html')) { this.$el.html(this.model.get('html')); } + + this.on('tabActiveChange', this.tabActiveChange, this); this.listenTo(this.model, 'change:active', this.changeState); }, @@ -191,7 +194,7 @@ */ showBlockRegionList: function (e) { // Get the BlockModel id (uuid). - var id = $(e.currentTarget).closest('[data-block-action-id]').data('block-action-id'); + var id = this.getEventBlockUuid(e); $(e.currentTarget).empty(); @@ -199,7 +202,7 @@ this.model.get('regionCollection').each(function (region) { var option = $(this.template_region_option(region.toJSON())); // If this is the current region, place it first in the list. - if (region.get('blockCollection').get(id)) { + if (region.getBlock(id)) { option.attr('selected', 'selected'); $(e.currentTarget).prepend(option); } @@ -216,7 +219,7 @@ * The event object. */ hideBlockRegionList: function (e) { - $(e.currentTarget).html(''); + $(e.currentTarget).html(''); }, /** @@ -225,38 +228,104 @@ * @param {Object} e * The event object. */ - selectBlockRegionList: function (e) { - // Get the BlockModel id (uuid). - var id = $(e.currentTarget).closest('[data-block-action-id]').data('block-action-id'); + newRegionSelected: function (e) { + var block_uuid = this.getEventBlockUuid(e); + var new_region_name = $(e.currentTarget).children(':selected').data('region-option-name'); + + if (new_region_name) { + this.moveBlockToRegion(block_uuid, new_region_name); + this.hideBlockRegionList(e); + this.render(); + this.highlightBlock(block_uuid, true); + this.saveToTempStore(); + } + }, - // Grab the value of this region. - var region_name = $(e.currentTarget).children(':selected').data('region-option-name'); + /** + * Get the block Uuid related to an event. + * + * @param {Object} e + * The event object. + * + * @return {String} + * The block Uuid + */ + getEventBlockUuid: function (e) { + return $(e.currentTarget).closest('[data-block-action-id]').data('block-action-id'); + }, + + /** + * Get the block Uuid related to an event. + * + * @param {Object} e + * The event object. + * + * @return {String} + * The block Uuid + */ + getEventBlockId: function (e) { + return $(e.currentTarget).closest('[data-block-edit-id]').data('block-edit-id'); + }, - // First, remove the Block from the current region. - var block; + /** + * Moves an existing Block to a new region. + * + * @param block_uuid + * The universally unique identifier of the block. + * @param target_region_id + * The id of the target region. + */ + moveBlockToRegion: function (block_uuid, target_region_id) { + var target_region = this.model.get('regionCollection').get(target_region_id); + var original_region = this.getRegionContainingBlock(block_uuid); + target_region.addBlock(original_region.getBlock(block_uuid)); + original_region.removeBlock(block_uuid); + }, + + /** + * Determines what region a Block resides in. + * + * @param block_uuid + * The universally unique identifier of the block. + * + * @returns {Drupal.panels_ipe.RegionModel|undefined} + * The region containing the block if it was found. + */ + getRegionContainingBlock: function (block_uuid) { var region_collection = this.model.get('regionCollection'); - region_collection.each(function (region) { - var block_collection = region.get('blockCollection'); - if (block_collection.get(id)) { - block = block_collection.get(id); - block_collection.remove(block); + for (var i = 0, l = region_collection.length; i < l; i++) { + var region = region_collection.at(i); + if (region.hasBlock(block_uuid)) { + return region; } - }); - - // Next, add the Block to the new region. - if (block) { - var region = this.model.get('regionCollection').get(region_name); - region.get('blockCollection').add(block); } + }, + + /** + * Highlights a block by adding a css class and optionally scrolls to the + * block's location. + * + * @param {string} block_uuid + * The universally unique identifier of the block. + * @param {bool} scroll + * Whether or not the page should scroll to the block. Defaults to false. + */ + highlightBlock: function (block_uuid, scroll) { + scroll = scroll || false; - // Hide the select list. - this.hideBlockRegionList(e); + var $block = this.$('[data-block-id="' + block_uuid + '"]'); + $block.addClass('ipe-highlight'); - // Re-render. - this.render(); + if (scroll) { + $('body').animate({scrollTop: $block.offset().top}, 600); + } + }, - // Highlight the block. - this.$('[data-block-id="' + id + '"]').addClass('ipe-highlight'); + /** + * Marks the global AppModel as unsaved. + */ + markUnsaved: function () { + Drupal.panels_ipe.app.set('unsaved', true); }, /** @@ -273,6 +342,36 @@ }, /** + * Saves the current state of the layout to the tempstore. + */ + saveToTempStore: function () { + var model = this.model; + var urlRoot = Drupal.panels_ipe.urlRoot(drupalSettings); + var options = {url: urlRoot + '/layouts/' + model.get('id') + '/tempstore'}; + + Backbone.sync('update', model, options); + + this.markUnsaved(); + }, + + /** + * Removes the block on the server via an AJAX call. + * + * @param {string} block_uuid + * The UUID/ID of a BlockModel. + */ + removeServerSideBlock: function (block_uuid) { + $.ajax({ + url: Drupal.panels_ipe.urlRoot(drupalSettings) + '/remove_block', + method: 'DELETE', + data: JSON.stringify(block_uuid), + contentType: "application/json; charset=UTF-8" + }); + + this.markUnsaved(); + }, + + /** * Moves a block up or down in its RegionModel's BlockCollection. * * @param {Object} e @@ -280,7 +379,7 @@ */ moveBlock: function (e) { // Get the BlockModel id (uuid). - var id = $(e.currentTarget).closest('[data-block-action-id]').data('block-action-id'); + var id = this.getEventBlockUuid(e); // Get the direction the block is moving. var dir = $(e.currentTarget).data('action-id'); @@ -288,19 +387,15 @@ // Grab the model for this region. var region_name = $(e.currentTarget).closest('[data-region-name]').data('region-name'); var region = this.model.get('regionCollection').get(region_name); - var block = region.get('blockCollection').get(id); + var block = region.getBlock(id); // Shift the Block. region.get('blockCollection').shift(block, dir); - // Re-render ourselves. this.render(); + this.highlightBlock(id); - // Highlight the block. - this.$('[data-block-id="' + id + '"]').addClass('ipe-highlight'); - - // Mark that we have unsaved changes in our App. - Drupal.panels_ipe.app.set('unsaved', true); + this.saveToTempStore(); }, /** @@ -311,23 +406,19 @@ */ removeBlock: function (e) { // Get the BlockModel id (uuid). - var id = $(e.currentTarget).closest('[data-block-action-id]').data('block-action-id'); + var id = this.getEventBlockUuid(e); // Grab the model for this region. var region_name = $(e.currentTarget).closest('[data-region-name]').data('region-name'); var region = this.model.get('regionCollection').get(region_name); // Remove the block. - region.get('blockCollection').remove(id); - - // Add the UUID to an array our backend will later consume. - this.model.get('deletedBlocks').push(id); + region.removeBlock(id); // Re-render ourselves. this.render(); - // Mark that we have unsaved changes in our App. - Drupal.panels_ipe.app.set('unsaved', true); + this.removeServerSideBlock(id); }, /** @@ -338,14 +429,40 @@ */ configureBlock: function (e) { // Get the BlockModel id (uuid). - var id = $(e.currentTarget).closest('[data-block-action-id]').data('block-action-id'); + var id = this.getEventBlockUuid(e); // Grab the model for this region. var region_name = $(e.currentTarget).closest('[data-region-name]').data('region-name'); var region = this.model.get('regionCollection').get(region_name); - // Send a App-level event so our BlockPicker View can respond and display a Form. - Drupal.panels_ipe.app.trigger('configureBlock', region.get('blockCollection').get(id)); + // Send an App-level event so our BlockPicker View can display a Form. + Drupal.panels_ipe.app.trigger('configureBlock', region.getBlock(id)); + }, + + /** + * Edits an existing Content Block. + * + * @param {Object} e + * The event object. + */ + editContentBlock: function (e) { + // Get the BlockModel id (uuid). + var id = this.getEventBlockUuid(e); + + // Get the blockModel content id. + var plugin_id = this.getEventBlockId(e); + + // Split plugin id. + var plugin_split = plugin_id.split(':'); + + if (plugin_split[0] == 'block_content') { + // Grab the model for this region. + var region_name = $(e.currentTarget).closest('[data-region-name]').data('region-name'); + var region = this.model.get('regionCollection').get(region_name); + + // Send a App-level event so our BlockPicker View can respond and display a Form. + Drupal.panels_ipe.app.trigger('editContentBlock', region.getBlock(id)); + } }, /** @@ -363,8 +480,8 @@ // Get the BlockModel and remove it from its last position. var old_region = this.model.get('regionCollection').get(old_region_name); - var block = old_region.get('blockCollection').get(id); - old_region.get('blockCollection').remove(block, {silent: true}); + var block = old_region.getBlock(id); + old_region.removeBlock(block, {silent: true}); // Get the new region name and index from the droppable. var new_region_name = $(e.currentTarget).data('droppable-region-name'); @@ -372,18 +489,18 @@ // Add the BlockModel to its new region/index. var new_region = this.model.get('regionCollection').get(new_region_name); - new_region.get('blockCollection').add(block, {at: index, silent: true}); - - // Re-render ourselves. - // We do this twice as jQuery UI mucks with the DOM as it lets go of a - // cloned element. Typically we would only ever need to re-render once. - this.render().render(); - - // Highlight the block. - this.$('[data-block-id="' + id + '"]').addClass('ipe-highlight'); + new_region.addBlock(block, {at: index, silent: true}); + + // Re-render after the current execution cycle, to account for DOM editing + // that jQuery.ui is going to do on this run. Modules like Contextual do + // something similar to ensure rendering order is preserved. + var self = this; + window.setTimeout(function () { + self.render(); + self.highlightBlock(id); + }); - // Mark that we have unsaved changes in our App. - Drupal.panels_ipe.app.set('unsaved', true); + this.saveToTempStore(); }, /** @@ -398,9 +515,10 @@ // First, check if the Block already exists and remove it if so. var index = null; this.model.get('regionCollection').each(function (region) { - if (region.get('blockCollection').get(block.get('uuid'))) { - index = region.get('blockCollection').indexOf(block.get('uuid')); - region.get('blockCollection').remove(block.get('uuid')); + var old_block = region.getBlock(block.get('uuid')); + if (old_block) { + index = region.get('blockCollection').indexOf(old_block); + region.removeBlock(old_block); } }); @@ -409,16 +527,13 @@ if (region) { // Add the block, at its previous index if necessary. var options = {}; - if (index) { + if (index !== null && index !== -1) { options.at = index; } - region.get('blockCollection').add(block, options); + region.addBlock(block, options); - // Re-render ourselves. this.render(); - - // Highlight the block. - this.$('[data-block-id="' + block.get('uuid') + '"]').addClass('ipe-highlight'); + this.highlightBlock(block.get('uuid'), true); } } diff --git a/panels_ipe/js/views/TabsView.js b/panels_ipe/js/views/TabsView.js index 4221b43..49ee92c 100644 --- a/panels_ipe/js/views/TabsView.js +++ b/panels_ipe/js/views/TabsView.js @@ -75,6 +75,9 @@ this.$el.append('
        '); this.$el.append('
        '); + // Remove any previously added body classes. + $('body').removeClass('panels-ipe-tabs-open'); + // Append each of our tabs and their tab content view. this.collection.each(function (tab) { // Return early if this tab is hidden. @@ -89,6 +92,9 @@ // Check to see if this tab has content. if (tab.get('active') && this.tabViews[id]) { + // Add a top-level body class. + $('body').addClass('panels-ipe-tabs-open'); + // Render the tab content. this.$('.ipe-tabs-content').append(this.template_content(tab.toJSON())); this.tabViews[id].setElement('[data-tab-content-id="' + id + '"]').render(); @@ -148,6 +154,11 @@ } tab.set('active', false); } + + // Inform the tab's view of the change. + if (this.tabViews[tab.get('id')]) { + this.tabViews[tab.get('id')].trigger('tabActiveChange', tab.get('active')); + } }, this); // Trigger a re-render, with animation if needed. @@ -168,7 +179,12 @@ closeTabContent: function () { // Close the tab, then re-render. var self = this; - this.$('.ipe-tabs-content')['slideUp']('fast', function () { self.render(); }); + this.$('.ipe-tabs-content')['slideUp']('fast', function () { + self.render(); + }); + + // Remove our top-level body class. + $('body').removeClass('panels-ipe-tabs-open'); }, /** diff --git a/panels_ipe/panels_ipe.libraries.yml b/panels_ipe/panels_ipe.libraries.yml index 1e6fa1d..5aad058 100644 --- a/panels_ipe/panels_ipe.libraries.yml +++ b/panels_ipe/panels_ipe.libraries.yml @@ -16,7 +16,6 @@ panels_ipe: js/views/BlockView.js: {} js/views/CategoryView.js: {} js/views/BlockPicker.js: {} - js/views/BlockContentPicker.js: {} js/views/LayoutPicker.js: {} js/views/LayoutView.js: {} js/views/TabsView.js: {} diff --git a/panels_ipe/panels_ipe.routing.yml b/panels_ipe/panels_ipe.routing.yml index fdb69a9..464dc4b 100644 --- a/panels_ipe/panels_ipe.routing.yml +++ b/panels_ipe/panels_ipe.routing.yml @@ -16,21 +16,32 @@ panels_ipe.block_plugins: _method: 'GET' panels_ipe.block_plugin.form: - path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/layout/{layout_id}/block_plugins/{plugin_id}/form' + path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/block_plugins/{plugin_id}/form' defaults: _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::getBlockPluginForm' requirements: _panels_storage_access: read _permission: 'access panels in-place editing' + options: + _admin_route: FALSE panels_ipe.block_plugin_existing.form: - path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/layout/{layout_id}/block_plugins/{plugin_id}/block/{block_uuid}/form' + path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/block_plugins/{plugin_id}/block/{block_uuid}/form' defaults: _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::getBlockPluginForm' requirements: _panels_storage_access: read _permission: 'access panels in-place editing' +panels_ipe.remove_block: + path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/remove_block' + defaults: + _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::handleRemoveBlockRequest' + requirements: + _panels_storage_access: update + _permission: 'access panels in-place editing' + _method: 'DELETE' + panels_ipe.block_content_types: path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/block_content/types' defaults: @@ -46,7 +57,15 @@ panels_ipe.block_content.form: _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::getBlockContentForm' requirements: _panels_storage_access: read - _permission: 'access panels in-place editing' + _permission: 'access panels in-place editing+administer blocks' + +panels_ipe.block_content_existing.form: + path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/block_content/{type}/block/{block_content_uuid}/form' + defaults: + _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::getBlockContentForm' + requirements: + _panels_storage_access: read + _permission: 'access panels in-place editing+administer blocks' panels_ipe.layouts: path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/layouts' @@ -68,7 +87,16 @@ panels_ipe.layout.form: panels_ipe.layout.update: path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/layouts/{layout_id}' defaults: - _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::updateLayout' + _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::handleUpdateLayoutRequest' + requirements: + _panels_storage_access: update + _permission: 'access panels in-place editing' + _method: 'PUT' + +panels_ipe.layout.update_tempstore: + path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/layouts/{layout_id}/tempstore' + defaults: + _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::handleUpdateLayoutTempStorageRequest' requirements: _panels_storage_access: update _permission: 'access panels in-place editing' @@ -77,8 +105,21 @@ panels_ipe.layout.update: panels_ipe.layout.save: path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/layouts/{layout_id}' defaults: - _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::createLayout' + _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::handleCreateLayoutRequest' requirements: _panels_storage_access: update _permission: 'access panels in-place editing' _method: 'POST' + +# @todo Add/consolidate routes for all Block CRUD operations. + +panels_ipe.block.read: + path: '/admin/panels_ipe/variant/{panels_storage_type}/{panels_storage_id}/block/{block_uuid}' + defaults: + _controller: '\Drupal\panels_ipe\Controller\PanelsIPEPageController::getBlock' + requirements: + _panels_storage_access: read + _permission: 'access panels in-place editing' + _method: 'GET' + options: + _admin_route: FALSE diff --git a/panels_ipe/src/Controller/PanelsIPEPageController.php b/panels_ipe/src/Controller/PanelsIPEPageController.php index cd9a2ef..5558155 100644 --- a/panels_ipe/src/Controller/PanelsIPEPageController.php +++ b/panels_ipe/src/Controller/PanelsIPEPageController.php @@ -7,20 +7,22 @@ namespace Drupal\panels_ipe\Controller; -use Drupal\Component\Serialization\Json; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\AppendCommand; use Drupal\Core\Block\BlockManagerInterface; use Drupal\Core\Controller\ControllerBase; -use Drupal\Core\Form\FormState; +use Drupal\Core\Plugin\Context\ContextHandlerInterface; use Drupal\Core\Render\Element; use Drupal\Core\Render\RendererInterface; use Drupal\layout_plugin\Plugin\Layout\LayoutPluginManagerInterface; -use Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant; use Drupal\panels\Storage\PanelsStorageManagerInterface; +use Drupal\panels_ipe\Helpers\RemoveBlockRequestHandler; +use Drupal\panels_ipe\Helpers\UpdateLayoutRequestHandler; +use Drupal\panels_ipe\PanelsIPEBlockRendererTrait; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpFoundation\JsonResponse; use Drupal\user\SharedTempStoreFactory; @@ -30,6 +32,8 @@ use Drupal\user\SharedTempStoreFactory; */ class PanelsIPEPageController extends ControllerBase { + use PanelsIPEBlockRendererTrait; + /** * @var \Drupal\Core\Block\BlockManagerInterface */ @@ -58,19 +62,34 @@ class PanelsIPEPageController extends ControllerBase { protected $tempStore; /** + * @var \Drupal\panels_ipe\Helpers\UpdateLayoutRequestHandler + */ + private $updateLayoutRequestHandler; + + /** + * @var \Drupal\panels_ipe\Helpers\RemoveBlockRequestHandler + */ + private $removeBlockRequestHandler; + + /** * Constructs a new PanelsIPEController. * * @param \Drupal\Core\Block\BlockManagerInterface $block_manager * @param \Drupal\Core\Render\RendererInterface $renderer * @param \Drupal\layout_plugin\Plugin\Layout\LayoutPluginManagerInterface $layout_plugin_manager + * @param \Drupal\panels\Storage\PanelsStorageManagerInterface $panels_storage_manager * @param \Drupal\user\SharedTempStoreFactory $temp_store_factory + * @param \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler */ - public function __construct(BlockManagerInterface $block_manager, RendererInterface $renderer, LayoutPluginManagerInterface $layout_plugin_manager, PanelsStorageManagerInterface $panels_storage_manager, SharedTempStoreFactory $temp_store_factory) { + public function __construct(BlockManagerInterface $block_manager, RendererInterface $renderer, LayoutPluginManagerInterface $layout_plugin_manager, PanelsStorageManagerInterface $panels_storage_manager, SharedTempStoreFactory $temp_store_factory, ContextHandlerInterface $context_handler) { $this->blockManager = $block_manager; $this->renderer = $renderer; $this->layoutPluginManager = $layout_plugin_manager; $this->panelsStorage = $panels_storage_manager; $this->tempStore = $temp_store_factory->get('panels_ipe'); + $this->contextHandler = $context_handler; + $this->updateLayoutRequestHandler = new UpdateLayoutRequestHandler($this->moduleHandler(), $this->panelsStorage, $this->tempStore); + $this->removeBlockRequestHandler = new RemoveBlockRequestHandler($this->moduleHandler(), $this->panelsStorage, $this->tempStore); } /** @@ -82,7 +101,8 @@ class PanelsIPEPageController extends ControllerBase { $container->get('renderer'), $container->get('plugin.manager.layout_plugin'), $container->get('panels.storage_manager'), - $container->get('user.shared_tempstore') + $container->get('user.shared_tempstore'), + $container->get('context.handler') ); } @@ -110,49 +130,16 @@ class PanelsIPEPageController extends ControllerBase { } /** - * Saves the current Panels display in the tempstore or real storage.. - * - * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display - * The Panels display to be saved. - * @param bool $temp - * Whether or not to save to temp store. - * - * @return \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant - * The Panels display that was saved. - * - * @throws \Drupal\user\TempStoreException - * If there are any issues manipulating the entry in the temp store. - */ - protected function savePanelsDisplay(PanelsDisplayVariant $panels_display, $temp = TRUE) { - $temp_store_key = $panels_display->id(); - - // Save configuration to temp store. - if ($temp) { - $this->tempStore->set($temp_store_key, $panels_display->getConfiguration()); - } - else { - // Check to see if temp store has configuration saved. - if ($variant_config = $this->tempStore->get($temp_store_key)) { - // Delete the existing temp store value. - $this->tempStore->delete($temp_store_key); - } - - // Save to the real storage. - $this->panelsStorage->save($panels_display); - } - - return $panels_display; - } - - /** * Removes any temporary changes to the variant. * - * @param string $panels_display_id - * The id of the current Panels display. - * @param \Symfony\Component\HttpFoundation\Request $request - * The current request. + * @param string $panels_storage_type + * The id of the storage plugin. + * @param string $panels_storage_id + * The id within the storage plugin for the requested Panels display. * * @return \Symfony\Component\HttpFoundation\JsonResponse + * + * @throws \Drupal\user\TempStoreException */ public function cancel($panels_storage_type, $panels_storage_id) { $panels_display = $this->loadPanelsDisplay($panels_storage_type, $panels_storage_id); @@ -212,7 +199,7 @@ class PanelsIPEPageController extends ControllerBase { * @param string $layout_id * The machine name of the requested layout. * - * @return AjaxResponse + * @return \Drupal\Core\Ajax\AjaxResponse */ public function getLayoutForm($panels_storage_type, $panels_storage_id, $layout_id) { $panels_display = $this->loadPanelsDisplay($panels_storage_type, $panels_storage_id); @@ -228,48 +215,25 @@ class PanelsIPEPageController extends ControllerBase { } /** - * Updates the current Panels display based on the changes done in our app. + * Updates (PUT) an existing Layout in this Variant. * - * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display - * The current Panels display. - * @param array $layout_model - * The decoded LayoutModel from our App. + * @param string $panels_storage_type + * The id of the storage plugin. + * @param string $panels_storage_id + * The id within the storage plugin for the requested Panels display. + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. * * @return \Symfony\Component\HttpFoundation\JsonResponse */ - protected function updatePanelsDisplay(PanelsDisplayVariant $panels_display, array $layout_model) { - // Set our weight and region based on the metadata in our Backbone app. - foreach ($layout_model['regionCollection'] as $region) { - $weight = 0; - foreach ($region['blockCollection'] as $block) { - /** @var \Drupal\Core\Block\BlockBase $block_instance */ - $block_instance = $panels_display->getBlock($block['uuid']); - - $block_instance->setConfigurationValue('region', $region['name']); - $block_instance->setConfigurationValue('weight', ++$weight); - - $panels_display->updateBlock($block['uuid'], $block_instance->getConfiguration()); - } - } - - // Remove blocks that need removing. - // @todo We should do this on the fly instead of at on save. - foreach ($layout_model['deletedBlocks'] as $uuid) { - $panels_display->removeBlock($uuid); - } - - // Allow other modules to modify the display before saving based on the - // contents of our $layout_model. - $this->moduleHandler()->invokeAll('panels_ipe_panels_display_presave', [$panels_display, $layout_model]); - - // Save the variant and remove temp storage. - $this->savePanelsDisplay($panels_display, FALSE); - - return new JsonResponse(['deletedBlocks' => []]); + public function handleUpdateLayoutRequest($panels_storage_type, $panels_storage_id, Request $request) { + $panels_display = $this->loadPanelsDisplay($panels_storage_type, $panels_storage_id); + $this->updateLayoutRequestHandler->handleRequest($panels_display, $request); + return $this->updateLayoutRequestHandler->getJsonResponse(); } /** - * Updates (PUT) an existing Layout in this Variant. + * Stores changes to the temporary storage. * * @param string $panels_storage_type * The id of the storage plugin. @@ -280,17 +244,10 @@ class PanelsIPEPageController extends ControllerBase { * * @return \Symfony\Component\HttpFoundation\JsonResponse */ - public function updateLayout($panels_storage_type, $panels_storage_id, Request $request) { + public function handleUpdateLayoutTempStorageRequest($panels_storage_type, $panels_storage_id, Request $request) { $panels_display = $this->loadPanelsDisplay($panels_storage_type, $panels_storage_id); - - // Decode the request. - $content = $request->getContent(); - if (!empty($content) && $layout_model = Json::decode($content)) { - return $this->updatePanelsDisplay($panels_display, $layout_model); - } - else { - return new JsonResponse(['success' => false], 400); - } + $this->updateLayoutRequestHandler->handleRequest($panels_display, $request, TRUE); + return $this->updateLayoutRequestHandler->getJsonResponse(); } /** @@ -305,9 +262,27 @@ class PanelsIPEPageController extends ControllerBase { * * @return \Symfony\Component\HttpFoundation\JsonResponse */ - public function createLayout($panels_storage_type, $panels_storage_id, Request $request) { + public function handleCreateLayoutRequest($panels_storage_type, $panels_storage_id, Request $request) { // For now, creating and updating a layout is the same thing. - return $this->updateLayout($panels_storage_type, $panels_storage_id, $request); + return $this->handleUpdateLayoutRequest($panels_storage_type, $panels_storage_id, $request); + } + + /** + * Removes a block from the layout. + * + * @param string $panels_storage_type + * The id of the storage plugin. + * @param string $panels_storage_id + * The id within the storage plugin for the requested Panels display. + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + */ + public function handleRemoveBlockRequest($panels_storage_type, $panels_storage_id, Request $request) { + $panels_display = $this->loadPanelsDisplay($panels_storage_type, $panels_storage_id); + $this->removeBlockRequestHandler->handleRequest($panels_display, $request, TRUE); + return $this->updateLayoutRequestHandler->getJsonResponse(); } /** @@ -376,7 +351,7 @@ class PanelsIPEPageController extends ControllerBase { // Return the rendered form as a proper Drupal AJAX response. $response = new AjaxResponse(); - $command = new AppendCommand('.ipe-block-plugin-form', $form); + $command = new AppendCommand('.ipe-block-form', $form); $response->addCommand($command); return $response; } @@ -419,27 +394,88 @@ class PanelsIPEPageController extends ControllerBase { * The id within the storage plugin for the requested Panels display. * @param string $type * The requested Block Type. - * @param string $type - * The Block Content UUID, if an entity already exists. + * @param string $block_content_uuid + * The Block Content Entity UUID, if this is an existing Block. + * + * @return \Drupal\Core\Ajax\AjaxResponse * - * @return NotFoundHttpException|Response + * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException */ - public function getBlockContentForm($panels_storage_type, $panels_storage_id, $type) { + public function getBlockContentForm($panels_storage_type, $panels_storage_id, $type, $block_content_uuid = NULL) { $storage = $this->entityTypeManager()->getStorage('block_content'); - // Create a new block of the given type. - $block = $storage->create([ - 'type' => $type - ]); + // Create or load a new block of the given type. + if ($block_content_uuid) { + $block_list = $storage->loadByProperties(['uuid' => $block_content_uuid]); + $block = array_shift($block_list); + + $operation = 'update'; + } + else { + $block = $storage->create([ + 'type' => $type + ]); + + $operation = 'create'; + } + + // Check Block Content entity access for the current operation. + if (!$block->access($operation)) { + throw new AccessDeniedHttpException(); + } // Grab our Block Content Entity form handler. $form = $this->entityFormBuilder()->getForm($block, 'panels_ipe'); // Return the rendered form as a proper Drupal AJAX response. $response = new AjaxResponse(); - $command = new AppendCommand('.ipe-block-type-form', $form); + $command = new AppendCommand('.ipe-block-form', $form); $response->addCommand($command); return $response; } + /** + * Gets a single Block from the current Panels Display. Uses TempStore. + * + * @param string $panels_storage_type + * The id of the storage plugin. + * @param string $panels_storage_id + * The id within the storage plugin for the requested Panels display. + * @param string $block_uuid + * The Block UUID. + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + */ + public function getBlock($panels_storage_type, $panels_storage_id, $block_uuid) { + $panels_display = $this->loadPanelsDisplay($panels_storage_type, $panels_storage_id); + + /** @var \Drupal\Core\Block\BlockBase $block_instance */ + $block_instance = $panels_display->getBlock($block_uuid); + $block_config = $block_instance->getConfiguration(); + + // Assemble data required for our App. + $build = $this->buildBlockInstance($block_instance, $panels_display); + + // Bubble Block attributes to fix bugs with the Quickedit and Contextual + // modules. + $this->bubbleBlockAttributes($build); + + // Add our data attribute for the Backbone app. + $build['#attributes']['data-block-id'] = $block_uuid; + + $plugin_definition = $block_instance->getPluginDefinition(); + + $block_model = [ + 'uuid' => $block_uuid, + 'label' => $block_instance->label(), + 'id' => $block_instance->getPluginId(), + 'region' => $block_config['region'], + 'provider' => $block_config['provider'], + 'plugin_id' => $plugin_definition['id'], + 'html' => $this->renderer->render($build), + ]; + + return new JsonResponse($block_model); + } + } diff --git a/panels_ipe/src/Exception/EmptyRequestContentException.php b/panels_ipe/src/Exception/EmptyRequestContentException.php new file mode 100644 index 0000000..7b18cd1 --- /dev/null +++ b/panels_ipe/src/Exception/EmptyRequestContentException.php @@ -0,0 +1,8 @@ +t('Create and Place'); + if (!$this->entity->isNew()) { + $button_value = $this->t('Update'); + } + // Override normal BlockContentForm actions as we need to be AJAX // compatible, and also need to communicate with our App. $actions['submit'] = [ '#type' => 'button', - '#value' => $this->t('Create and Place'), + '#value' => $button_value, + '#name' => 'panels_ipe_submit', '#ajax' => [ 'callback' => '::submitForm', 'wrapper' => 'panels-ipe-block-type-form-wrapper', @@ -41,6 +48,11 @@ class PanelsIPEBlockContentForm extends BlockContentForm { public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); + $form['is_new'] = [ + '#type' => 'value', + '#value' => $this->entity->isNew(), + ]; + // Wrap our form so that our submit callback can re-render the form. $form['#prefix'] = '
        '; $form['#suffix'] = '
        '; @@ -52,8 +64,11 @@ class PanelsIPEBlockContentForm extends BlockContentForm { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - // Return early if there are any errors. - if ($form_state->hasAnyErrors()) { + $triggering_element = $form_state->getTriggeringElement(); + + // Return early if there are any errors or if a button we're not aware of + // submitted the form. + if ($form_state->hasAnyErrors() || $triggering_element['#name'] !== 'panels_ipe_submit') { return $form; } @@ -63,7 +78,12 @@ class PanelsIPEBlockContentForm extends BlockContentForm { parent::save($form, $form_state); // Inform the App that we've created a new Block Content entity. - $form['#attached']['drupalSettings']['panels_ipe']['new_block_content'] = $this->entity->uuid(); + if ($form_state->getValue('is_new')) { + $form['#attached']['drupalSettings']['panels_ipe']['new_block_content'] = $this->entity->uuid(); + } + else { + $form['#attached']['drupalSettings']['panels_ipe']['edit_block_content'] = $this->entity->uuid(); + } return $form; } diff --git a/panels_ipe/src/Form/PanelsIPEBlockPluginForm.php b/panels_ipe/src/Form/PanelsIPEBlockPluginForm.php index 78fc6d4..cfa87d2 100644 --- a/panels_ipe/src/Form/PanelsIPEBlockPluginForm.php +++ b/panels_ipe/src/Form/PanelsIPEBlockPluginForm.php @@ -15,9 +15,10 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\Context\ContextHandlerInterface; use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait; use Drupal\Core\Plugin\ContextAwarePluginInterface; -use Drupal\Core\Render\RendererInterface; use Drupal\Core\Render\Element; +use Drupal\Core\Render\RendererInterface; use Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant; +use Drupal\panels_ipe\PanelsIPEBlockRendererTrait; use Drupal\user\SharedTempStoreFactory; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -31,17 +32,14 @@ class PanelsIPEBlockPluginForm extends FormBase { use ContextAwarePluginAssignmentTrait; + use PanelsIPEBlockRendererTrait; + /** * @var \Drupal\Component\Plugin\PluginManagerInterface $blockManager */ protected $blockManager; /** - * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface $contextHandler - */ - protected $contextHandler; - - /** * @var \Drupal\Core\Render\RendererInterface $renderer */ protected $renderer; @@ -263,17 +261,17 @@ class PanelsIPEBlockPluginForm extends FormBase { return $form; } - $block_instance = $this->getBlockInstance($form_state); - - // Submit the block configuration form. - $this->submitBlock($block_instance, $form, $form_state); - // If a temporary configuration for this variant exists, use it. $temp_store_key = $this->panelsDisplay->id(); if ($variant_config = $this->tempStore->get($temp_store_key)) { $this->panelsDisplay->setConfiguration($variant_config); } + $block_instance = $this->getBlockInstance($form_state); + + // Submit the block configuration form. + $this->submitBlock($block_instance, $form, $form_state); + // Set the block region appropriately. $block_config = $block_instance->getConfiguration(); $block_config['region'] = $form_state->getValue(array('settings', 'region')); @@ -290,20 +288,29 @@ class PanelsIPEBlockPluginForm extends FormBase { $this->tempStore->set($this->panelsDisplay->id(), $this->panelsDisplay->getConfiguration()); // Assemble data required for our App. - $build = $this->buildBlockInstance($block_instance); - $form['build'] = $build; + $build = $this->buildBlockInstance($block_instance, $this->panelsDisplay); + + // Bubble Block attributes to fix bugs with the Quickedit and Contextual + // modules. + $this->bubbleBlockAttributes($build); // Add our data attribute for the Backbone app. $build['#attributes']['data-block-id'] = $uuid; + $plugin_definition = $block_instance->getPluginDefinition(); + $block_model = [ 'uuid' => $uuid, 'label' => $block_instance->label(), 'id' => $block_instance->getPluginId(), 'region' => $block_config['region'], - 'html' => $this->renderer->render($build) + 'provider' => $block_config['provider'], + 'plugin_id' => $plugin_definition['id'], + 'html' => $this->renderer->render($build), ]; + $form['build'] = $build; + // Add Block metadata and HTML as a drupalSetting. $form['#attached']['drupalSettings']['panels_ipe']['updated_block'] = $block_model; @@ -334,7 +341,10 @@ class PanelsIPEBlockPluginForm extends FormBase { $this->submitBlock($block_instance, $form, $form_state); // Gather a render array for the block. - $build = $this->buildBlockInstance($block_instance); + $build = $this->buildBlockInstance($block_instance, $this->panelsDisplay); + + // Disable any nested forms from the render array. + $build['content'] = $this->removeFormWrapperRecursive($build['content']); // Add the preview to the backside of the card and inform JS that we need to // be flipped. @@ -409,41 +419,4 @@ class PanelsIPEBlockPluginForm extends FormBase { return $content; } - /** - * Compiles a render array for the given Block instance based on the form. - * - * @param \Drupal\Core\Block\BlockBase $block_instance - * The Block instance you want to render. - * - * @return array $build - * The Block render array. - */ - protected function buildBlockInstance($block_instance) { - // Get the new block configuration. - $configuration = $block_instance->getConfiguration(); - - // Add context to the block. - if ($block_instance instanceof ContextAwarePluginInterface) { - $this->contextHandler->applyContextMapping($block_instance, $this->panelsDisplay->getContexts()); - } - - // Build the block content. - $content = $block_instance->build(); - - // Disable any nested forms from the render array. - $content = $this->removeFormWrapperRecursive($content); - - // Compile the render array. - $build = [ - '#theme' => 'block', - '#configuration' => $configuration, - '#plugin_id' => $block_instance->getPluginId(), - '#base_plugin_id' => $block_instance->getBaseId(), - '#derivative_plugin_id' => $block_instance->getDerivativeId(), - 'content' => $content, - ]; - - return $build; - } - } diff --git a/panels_ipe/src/Helpers/RemoveBlockRequestHandler.php b/panels_ipe/src/Helpers/RemoveBlockRequestHandler.php new file mode 100644 index 0000000..708f673 --- /dev/null +++ b/panels_ipe/src/Helpers/RemoveBlockRequestHandler.php @@ -0,0 +1,23 @@ +removeBlock($decoded_request); + + if ($save_to_temp_store) { + $this->savePanelsDisplayToTempStore($panels_display); + } + else { + $this->savePanelsDisplay($panels_display); + } + } + +} diff --git a/panels_ipe/src/Helpers/RequestHandlerBase.php b/panels_ipe/src/Helpers/RequestHandlerBase.php new file mode 100644 index 0000000..4b04a5b --- /dev/null +++ b/panels_ipe/src/Helpers/RequestHandlerBase.php @@ -0,0 +1,143 @@ +moduleHandler = $module_handler; + $this->panelsStore = $panels_store; + $this->tempStore = $temp_store; + } + + /** + * @inheritdoc + */ + public function handleRequest(PanelsDisplayVariant $panels_display, Request $request, $save_to_temp_store = FALSE) { + $this->setResponse([]); + + try { + $this->handle($panels_display, self::decodeRequest($request), $save_to_temp_store); + } + catch (EmptyRequestContentException $e) { + $this->setResponse(['success' => FALSE], 400); + } + } + + /** + * Handles the decoded request by making some change to the Panels Display. + * + * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display + * @param mixed $decoded_request + * @param bool $save_to_temp_store + * + * @throws \Drupal\panels_ipe\Exception\EmptyRequestContentException + */ + protected abstract function handle(PanelsDisplayVariant $panels_display, $decoded_request, $save_to_temp_store = FALSE); + + /** + * Attempts to decode the incoming request's content as JSON. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @return mixed + * + * @throws \Drupal\panels_ipe\Exception\EmptyRequestContentException + */ + protected static function decodeRequest(Request $request) { + if (empty($request->getContent())) { + throw new EmptyRequestContentException(); + } + + return Json::decode($request->getContent()); + } + + /** + * Helper function for invoking hooks for all enabled modules. + * + * @param $hook + * @param array $arguments + */ + protected function invokeHook($hook, array $arguments) { + $this->moduleHandler->invokeAll($hook, $arguments); + } + + /** + * Deletes TempStore and saves the current Panels display. + * + * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display + * The Panels display to be saved. + * + * @throws \Drupal\user\TempStoreException + * If there are any issues manipulating the entry in the temp store. + */ + protected function savePanelsDisplay(PanelsDisplayVariant $panels_display) { + $this->deletePanelsDisplayTempStore($panels_display); + $this->panelsStore->save($panels_display); + } + + /** + * Saves the given Panels Display to TempStore. + * + * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display + * @throws \Drupal\user\TempStoreException + */ + protected function savePanelsDisplayToTempStore(PanelsDisplayVariant $panels_display) { + $this->tempStore->set($panels_display->id(), $panels_display->getConfiguration()); + } + + /** + * Deletes the given Panels Display from TempStore. + * + * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display + * @throws \Drupal\user\TempStoreException + */ + protected function deletePanelsDisplayTempStore(PanelsDisplayVariant $panels_display) { + $this->tempStore->delete($panels_display->id()); + } + + /** + * Returns the current response data as a JSON Response. + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + */ + public function getJsonResponse() { + return new JsonResponse($this->response, $this->responseStatusCode); + } + + /** + * Updates our response and response status code properties. + * + * @param array $response + * @param int $response_status_code + */ + protected function setResponse(array $response, $response_status_code = 200) { + $this->response = $response; + $this->responseStatusCode = $response_status_code; + } + +} diff --git a/panels_ipe/src/Helpers/RequestHandlerInterface.php b/panels_ipe/src/Helpers/RequestHandlerInterface.php new file mode 100644 index 0000000..da0474d --- /dev/null +++ b/panels_ipe/src/Helpers/RequestHandlerInterface.php @@ -0,0 +1,24 @@ +updateLayout($panels_display, $decodedRequest, $save_to_temp_store); + } + + /** + * Changes the layout for the given Panels Display. + * + * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display + * @param $layout_model + * @param bool $save_to_temp_store + */ + private function updateLayout(PanelsDisplayVariant $panels_display, $layout_model, $save_to_temp_store = FALSE) { + $panels_display = self::updatePanelsDisplay($panels_display, $layout_model); + + $this->invokeHook('panels_ipe_panels_display_presave', [ + $panels_display, + $layout_model + ]); + + if ($save_to_temp_store) { + $this->savePanelsDisplayToTempStore($panels_display); + } + else { + $this->savePanelsDisplay($panels_display); + } + } + + /** + * Updates the current Panels display based on the changes done in our app. + * + * @param \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant $panels_display + * The current Panels display. + * @param array $layout_model + * The decoded LayoutModel from our App. + * + * @return \Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant + */ + private static function updatePanelsDisplay(PanelsDisplayVariant $panels_display, array $layout_model) { + // Set our weight and region based on the metadata in our Backbone app. + foreach ($layout_model['regionCollection'] as $region) { + $weight = 0; + foreach ($region['blockCollection'] as $block) { + /** @var \Drupal\Core\Block\BlockBase $block_instance */ + $block_instance = $panels_display->getBlock($block['uuid']); + + $block_instance->setConfigurationValue('region', $region['name']); + $block_instance->setConfigurationValue('weight', ++$weight); + + $panels_display->updateBlock($block['uuid'], $block_instance->getConfiguration()); + } + } + + return $panels_display; + } + +} diff --git a/panels_ipe/src/PanelsIPEBlockRendererTrait.php b/panels_ipe/src/PanelsIPEBlockRendererTrait.php new file mode 100644 index 0000000..51f499a --- /dev/null +++ b/panels_ipe/src/PanelsIPEBlockRendererTrait.php @@ -0,0 +1,84 @@ +getConfiguration(); + + // Add context to the block. + if ($this->contextHandler && $block_instance instanceof ContextAwarePluginInterface) { + $this->contextHandler->applyContextMapping($block_instance, $panels_display->getContexts()); + } + + // Build the block content. + $content = $block_instance->build(); + + // Compile the render array. + $build = [ + '#theme' => 'block', + '#attributes' => [], + '#contextual_links' => [], + '#configuration' => $configuration, + '#plugin_id' => $block_instance->getPluginId(), + '#base_plugin_id' => $block_instance->getBaseId(), + '#derivative_plugin_id' => $block_instance->getDerivativeId(), + 'content' => $content, + ]; + + return $build; + } + + /** + * Bubble block attributes up if possible. This allows modules like + * Quickedit to function. + * + * @see \Drupal\block\BlockViewBuilder::preRender for reference. + * + * @param array $build + * The Block render array. + */ + protected function bubbleBlockAttributes(&$build) { + // Bubble block attributes up if possible. This allows modules like + // Quickedit to function. + // See \Drupal\block\BlockViewBuilder::preRender() for reference. + if ($build['content'] !== NULL && !Element::isEmpty($build['content'])) { + foreach (['#attributes', '#contextual_links'] as $property) { + if (isset($build['content'][$property])) { + $build[$property] += $build['content'][$property]; + unset($build['content'][$property]); + } + } + } + } + +} diff --git a/panels_ipe/src/Plugin/DisplayBuilder/InPlaceEditorDisplayBuilder.php b/panels_ipe/src/Plugin/DisplayBuilder/InPlaceEditorDisplayBuilder.php index 64b6dd7..8d8bf39 100644 --- a/panels_ipe/src/Plugin/DisplayBuilder/InPlaceEditorDisplayBuilder.php +++ b/panels_ipe/src/Plugin/DisplayBuilder/InPlaceEditorDisplayBuilder.php @@ -114,12 +114,15 @@ class InPlaceEditorDisplayBuilder extends StandardDisplayBuilder { /** @var \Drupal\Core\Block\BlockPluginInterface[] $blocks */ foreach ($blocks as $block_uuid => $block) { $configuration = $block->getConfiguration(); + $plugin_definition = $block->getPluginDefinition(); $setting = [ 'uuid' => $block_uuid, 'label' => $block->label(), 'id' => $block->getPluginId(), + 'provider' => $configuration['provider'], + 'plugin_id' => $plugin_definition['id'], ]; - $settings['regions'][$region]['blocks'][$block_uuid] = NestedArray::mergeDeep($configuration, $setting); + $settings['regions'][$region]['blocks'][$block_uuid] = $setting; } } diff --git a/plugins/cache/simple.inc b/plugins/cache/simple.inc deleted file mode 100644 index 6b76678..0000000 --- a/plugins/cache/simple.inc +++ /dev/null @@ -1,161 +0,0 @@ - t("Simple cache"), - 'description' => t('Simple caching is a time-based cache. This is a hard limit, and once cached it will remain that way until the time limit expires.'), - 'cache get' => 'panels_simple_cache_get_cache', - 'cache set' => 'panels_simple_cache_set_cache', - 'cache clear' => 'panels_simple_cache_clear_cache', - 'settings form' => 'panels_simple_cache_settings_form', - 'settings form submit' => 'panels_simple_cache_settings_form_submit', - 'defaults' => array( - 'lifetime' => 15, - 'granularity' => 'none', - ), -); - -/** - * Get cached content. - */ -function panels_simple_cache_get_cache($conf, $display, $args, $contexts, $pane = NULL) { - $cid = panels_simple_cache_get_id($conf, $display, $args, $contexts, $pane); - $cache = cache_get($cid, 'cache_panels'); - if (!$cache) { - return FALSE; - } - - if ((time() - $cache->created) > $conf['lifetime']) { - return FALSE; - } - - return $cache->data; -} - -/** - * Set cached content. - */ -function panels_simple_cache_set_cache($conf, $content, $display, $args, $contexts, $pane = NULL) { - $cid = panels_simple_cache_get_id($conf, $display, $args, $contexts, $pane); - cache_set($cid, $content, 'cache_panels'); -} - -/** - * Clear cached content. - * - * Cache clears are always for an entire display, regardless of arguments. - */ -function panels_simple_cache_clear_cache($display) { - $cid = 'panels_simple_cache'; - - // If the panel is stored in the database it'll have a numeric did value. - if (is_numeric($display->did)) { - $cid .= ':' . $display->did; - } - // Exported panels won't have a numeric did but may have a usable cache_key. - elseif (!empty($display->cache_key)) { - $cid .= ':' . str_replace('panel_context:', '', $display->cache_key); - } - // Alternatively use the css_id. - elseif (!empty($display->css_id)) { - $cid .= ':' . $display->css_id; - } - // Failover to just appending the did, which may be the completely unusable - // string 'new'. - else { - $cid .= ':' . $display->did; - } - - cache_clear_all($cid, 'cache_panels', TRUE); -} - -/** - * Figure out an id for our cache based upon input and settings. - */ -function panels_simple_cache_get_id($conf, $display, $args, $contexts, $pane) { - $id = 'panels_simple_cache'; - - // If the panel is stored in the database it'll have a numeric did value. - if (is_numeric($display->did)) { - $id .= ':' . $display->did; - } - // Exported panels won't have a numeric did but may have a usable cache_key. - elseif (!empty($display->cache_key)) { - $id .= ':' . str_replace('panel_context:', '', $display->cache_key); - } - // Alternatively use the css_id. - elseif (!empty($display->css_id)) { - $id .= ':' . $display->css_id; - } - // Failover to just appending the did, which may be the completely unusable - // string 'new'. - else { - $id .= ':' . $display->did; - } - - if ($pane) { - $id .= ':' . $pane->pid; - } - - if (user_access('view pane admin links')) { - $id .= ':admin'; - } - - switch ($conf['granularity']) { - case 'args': - foreach ($args as $arg) { - $id .= ':' . $arg; - } - break; - - case 'context': - if (!is_array($contexts)) { - $contexts = array($contexts); - } - foreach ($contexts as $context) { - if (isset($context->argument)) { - $id .= ':' . $context->argument; - } - } - } - if (module_exists('locale')) { - global $language; - $id .= ':' . $language->language; - } - - if(!empty($pane->configuration['use_pager']) && !empty($_GET['page'])) { - $id .= ':p' . check_plain($_GET['page']); - } - - return $id; -} - -function panels_simple_cache_settings_form($conf, $display, $pid) { - $options = drupal_map_assoc(array(15, 30, 60, 120, 180, 240, 300, 600, 900, 1200, 1800, 3600, 7200, 14400, 28800, 43200, 86400, 172800, 259200, 345600, 604800), 'format_interval'); - $form['lifetime'] = array( - '#title' => t('Lifetime'), - '#type' => 'select', - '#options' => $options, - '#default_value' => $conf['lifetime'], - ); - - $form['granularity'] = array( - '#title' => t('Granularity'), - '#type' => 'select', - '#options' => array( - 'args' => t('Arguments'), - 'context' => t('Context'), - 'none' => t('None'), - ), - '#description' => t('If "arguments" are selected, this content will be cached per individual argument to the entire display; if "contexts" are selected, this content will be cached per unique context in the pane or display; if "neither" there will be only one cache for this pane.'), - '#default_value' => $conf['granularity'], - ); - - return $form; -} - diff --git a/plugins/display_renderers/editor.inc b/plugins/display_renderers/editor.inc deleted file mode 100644 index 6bfdf84..0000000 --- a/plugins/display_renderers/editor.inc +++ /dev/null @@ -1,5 +0,0 @@ - 'panels_renderer_editor', -); diff --git a/plugins/display_renderers/panels_renderer_editor.class.php b/plugins/display_renderers/panels_renderer_editor.class.php deleted file mode 100644 index 4876ff3..0000000 --- a/plugins/display_renderers/panels_renderer_editor.class.php +++ /dev/null @@ -1,1995 +0,0 @@ - &$this->display, - 'renderer' => &$this, - 'content_types' => $this->cache->content_types, - 'no_redirect' => TRUE, - 'display_title' => !empty($this->cache->display_title), - 'cache key' => $this->display->cache_key, - ); - - $output = drupal_build_form('panels_edit_display_form', $form_state); - if (empty($form_state['executed']) || !empty($form_state['clicked_button']['preview'])) { - return $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 = '
        '; - $output .= $this->render_layout(); - $output .= '
        '; - - 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); - - $output = "
        "; - $output .= $panel_buttons; - $output .= "

        " . check_plain($this->plugins['layout']['regions'][$region_id]) . "

        "; - $output .= $content; - $output .= "
        "; - - 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 = '' . t('Missing content type') . ''; - $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); - } - - $grabber_class = 'grab-title grabber'; - // If there are region locks, add them. - if (!empty($pane->locks['type'])) { - if ($pane->locks['type'] == 'regions') { - $settings['Panels']['RegionLock'][$pane->pid] = $pane->locks['regions']; - drupal_add_js($settings, 'setting'); - } - else if ($pane->locks['type'] == 'immovable') { - $grabber_class = 'grab-title not-grabber'; - } - } - - $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 = '
        '; - - if (empty($block->title)) { - $block->title = t('No title'); - } - - $output .= '
        '; - if ($buttons) { - $output .= '' . $buttons . ''; - } - $output .= '' . $title . ''; - $output .= '
        '; // grabber - - $output .= '
        '; - $output .= '
        ' . $block->title . '
        '; - $output .= '
        ' . filter_xss_admin(render($block->content)) . '
        '; - $output .= '
        '; // panel-pane-collapsible - - $output .= '
        '; // 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['title'] = array( - 'title' => $style_title, - 'attributes' => array('class' => array('panels-text')), - ); - - $style_links['change'] = array( - 'title' => t('Change'), - 'href' => $this->get_url('style-type', $type, $id), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - - $function = $type != 'pane' ? 'settings form' : 'pane settings form'; - if (panels_plugin_get_function('styles', $style, $function)) { - $style_links['settings'] = array( - 'title' => t('Settings'), - 'href' => $this->get_url('style-settings', $type, $id), - 'attributes' => array('class' => array('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(); - - if (user_access('administer panels styles')) { - $style_links = $this->get_style_links('display'); - $links[] = array( - 'title' => '' . t('Style') . '' . theme_links(array('links' => $style_links, 'attributes' => array(), 'heading' => array())), - 'html' => TRUE, - 'attributes' => array('class' => array('panels-sub-menu')), - ); - } - - if (user_access('use panels caching features')) { - $links[] = array( - 'title' => '
        ', - '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' => array('panels-text')), - ); - $cache_links[] = array( - 'title' => t('Change'), - 'href' => $this->get_url('cache-method', 'display'), - 'attributes' => array('class' => array('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' => array('ctools-use-modal')), - ); - } - - $links[] = array( - 'title' => '' . t('Caching') . '' . theme_links(array('links' => $cache_links, 'attributes' => array(), 'heading' => array())), - 'html' => TRUE, - 'attributes' => array('class' => array('panels-sub-menu')), - ); - } - - return theme('ctools_dropdown', array('title' => t('Display settings'), 'links' => $links, 'class' => 'panels-display-links')); - } - - /** - * Render the links to display when editing a region. - */ - function get_region_links($region_id) { - if (!empty($this->no_edit_links)) { - return ''; - } - $links = array(); - $links[] = array( - 'title' => t('Add content'), - 'href' => $this->get_url('select-content', $region_id), - 'attributes' => array( - 'class' => array('ctools-use-modal'), - ), - ); - - if (user_access('administer panels styles')) { - $links[] = array( - 'title' => '
        ', - 'html' => TRUE, - ); - - $style_links = $this->get_style_links('region', $region_id); - - $links[] = array( - 'title' => '' . t('Style') . '' . theme_links(array('links' => $style_links, 'attributes' => array(), 'heading' => array())), - 'html' => TRUE, - 'attributes' => array('class' => array('panels-sub-menu')), - ); - } - - return theme('ctools_dropdown', array('title' => theme('image', array('path' => ctools_image_path('icon-addcontent.png', 'panels'))), 'links' => $links, 'image' => TRUE, 'class' => 'pane-add-link panels-region-links-' . $region_id)); - } - - /** - * Render the links to display when editing a pane. - */ - function get_pane_links($pane, $content_type) { - if (!empty($this->no_edit_links)) { - return ''; - } - $links = array(); - - if (!empty($pane->shown)) { - $links['top']['disabled'] = array( - 'title' => t('Disable this pane'), - 'href' => $this->get_url('hide', $pane->pid), - 'attributes' => array('class' => array('use-ajax')), - ); - } - else { - $links['top']['enable'] = array( - 'title' => t('Enable this pane'), - 'href' => $this->get_url('show', $pane->pid), - 'attributes' => array('class' => array('use-ajax')), - ); - } - - if (isset($this->display->title_pane) && $this->display->title_pane == $pane->pid) { - $links['top']['panels-set-title'] = array( - 'title' => t('✓Panel title'), - 'html' => TRUE, - ); - } - else { - $links['top']['panels-set-title'] = array( - 'title' => t('Panel title'), - 'href' => $this->get_url('panel-title', $pane->pid), - 'attributes' => array('class' => array('use-ajax')), - ); - } - - $subtype = ctools_content_get_subtype($content_type, $pane->subtype); - - if (ctools_content_editable($content_type, $subtype, $pane->configuration)) { - $links['top']['settings'] = array( - 'title' => isset($content_type['edit text']) ? $content_type['edit text'] : t('Settings'), - 'href' => $this->get_url('edit-pane', $pane->pid), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - } - - if (user_access('administer advanced pane settings')) { - $links['top']['css'] = array( - 'title' => t('CSS properties'), - 'href' => $this->get_url('pane-css', $pane->pid), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - } - - if (user_access('administer panels styles')) { - $links['style'] = $this->get_style_links('pane', $pane->pid); - } - - if (user_access('administer pane access')) { - $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' => array('ctools-use-modal', 'panels-italic')), - ); - } - } - if (empty($visibility_links)) { - $visibility_links['no_rules'] = array( - 'title' => t('No rules'), - 'attributes' => array('class' => array('panels-text')), - ); - } - - $visibility_links['add_rule'] = array( - 'title' => t('Add new rule'), - 'href' => $this->get_url('access-add-test', $pane->pid), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - - $visibility_links['settings'] = array( - 'title' => t('Settings'), - 'href' => $this->get_url('access-settings', $pane->pid), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - - $links['visibility'] = $visibility_links; - } - - if (user_access('use panels locks')) { - $lock_type = !empty($pane->locks['type']) ? $pane->locks['type'] : 'none'; - switch ($lock_type) { - case 'immovable': - $lock_method = t('Immovable'); - break; - case 'regions': - $lock_method = t('Regions'); - break; - case 'none': - default: - $lock_method = t('No lock'); - break; - } - - $lock_links['lock'] = array( - 'title' => $lock_method, - 'attributes' => array('class' => array('panels-text')), - ); - $lock_links['change'] = array( - 'title' => t('Change'), - 'href' => $this->get_url('lock', $pane->pid), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - - $links['lock'] = $lock_links; - } - - if (panels_get_caches() && user_access('use panels caching features')) { - $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['title'] = array( - 'title' => $cache_method, - 'attributes' => array('class' => array('panels-text')), - ); - $cache_links['change'] = array( - 'title' => t('Change'), - 'href' => $this->get_url('cache-method', $pane->pid), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - if (panels_plugin_get_function('cache', $info, 'settings form')) { - $cache_links['settings'] = array( - 'title' => t('Settings'), - 'href' => $this->get_url('cache-settings', $pane->pid), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - } - - $links['cache'] = $cache_links; - } - - $links['bottom']['remove'] = array( - 'title' => t('Remove'), - 'href' => '#', - 'attributes' => array( - 'class' => array('pane-delete'), - 'id' => "pane-delete-panel-pane-$pane->pid", - ), - ); - - // Allow others to add/remove links from pane context menu. - // Grouped by 'top', 'style', 'visibility', 'lock', 'cache' and 'bottom' - drupal_alter('get_pane_links', $links, $pane, $content_type); - - $dropdown_links = $links['top']; - foreach (array( - 'style' => 'Style', - 'visibility' => 'Visibility rules', - 'lock' => 'Locking', - 'cache' => 'Caching' - ) as $category => $label) { - $dropdown_links[] = array( - 'title' => '
        ', - 'html' => TRUE, - ); - $dropdown_links[] = array( - 'title' => '' . t($label) . '' . theme_links(array('links' => $links[$category], 'attributes' => array(), 'heading' => array())), - 'html' => TRUE, - 'attributes' => array('class' => array('panels-sub-menu')), - ); - } - - $dropdown_links[] = array( - 'title' => '
        ', - 'html' => TRUE, - ); - $dropdown_links = array_merge($dropdown_links, $links['bottom']); - - return theme('ctools_dropdown', array('title' => theme('image', array('path' => ctools_image_path('icon-configure.png', 'panels'))), 'links' => $dropdown_links, 'image' => 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']['regions'])) { - ctools_modal_render(t('Error'), t('Invalid input')); - } - - $title = t('Add content to !s', array('!s' => $this->plugins['layout']['regions'][$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 = theme('panels_add_content_modal', array('renderer' => $this, 'categories' => $categories, 'category' => $category, 'region' => $region)); - } - $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 (!empty($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[$category_key]['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; - } - - /** - * 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); - - // Determine if we are adding a different pane than previously cached. This - // is used to load the different pane into cache so that multistep forms - // have the correct context instead of a previously cached version that - // does not match the pane currently being added. - $is_different_pane = FALSE; - if (isset($this->cache) && isset($this->cache->new_pane)) { - $diff_type = $type_name != $this->cache->new_pane->type; - $diff_subtype = $subtype_name != $this->cache->new_pane->subtype; - - $is_different_pane = $diff_type || $diff_subtype; - } - - if (!isset($step) || !isset($this->cache->new_pane) || $is_different_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, - 'display 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'])) { - // References get blown away with AJAX caching. This will fix that. - $pane = $form_state['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, - 'display 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'])) { - // References get blown away with AJAX caching. This will fix that. - $this->cache->display->content[$pid] = $form_state['pane']; - - // Conditionally overwrite the context for this panel if present in the form state. - if (!empty($form_state['display_cache']->display->context)) { - $this->cache->display->context = $form_state['display_cache']->display->context; - } - - 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($form_state['executed'])) { - $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($form_state['executed'])) { - $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']['regions'][$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, - 'pane' => ($type == 'pane') ? $this->display->content[$pid] : NULL, - 'title' => $title, - 'ajax' => TRUE, - 'type' => $type, - ); - - $output = ctools_modal_form_wrapper('panels_edit_style_type_form', $form_state); - if (empty($form_state['executed'])) { - $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']['regions'][$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($form_state['executed'])) { - $this->commands = $output; - return; - } - - if (isset($this->cache->style)) { - unset($this->cache->style); - } - - // Copy settings from form state back into the cache. - if(!empty($form_state['values']['settings'])) { - $this->cache->display->content[$pid]->style['settings'] = $form_state['values']['settings']; - } - - 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($form_state['executed'])) { - $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 CSS for a pane. - * - * @param $pid - * The pane id to edit. - */ - function ajax_lock($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 lock on !subtype_title', array('!subtype_title' => $subtype['title'])), - ); - - $output = ctools_modal_form_wrapper('panels_edit_configure_pane_lock_form', $form_state); - if (empty($form_state['executed'])) { - $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($form_state['executed'])) { - $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($form_state['executed'])) { - // 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'); - } - - $this->commands = $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($form_state['executed'])) { - $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[] = ajax_command_replace("#panel-pane-$pane->pid", $this->render_pane($pane)); - $this->commands[] = ajax_command_changed("#panel-pane-$pane->pid", "div.grab-title 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[] = ajax_command_append("#panel-region-$pane->panel", $this->render_pane($pane)); - $this->commands[] = ajax_command_changed("#panel-pane-$pane->pid", "div.grab-title span.text"); - } - - /** - * Create a command to update the links on a display after a change was made. - */ - function command_update_display_links() { - $this->commands[] = 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[] = 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['display cache']->new_pane = $form_state['pane']; - panels_edit_cache_set($form_state['display 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, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $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' => '
        ', - '#suffix' => '
        ', - '#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, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $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' => '
        ', - '#suffix' => '
        ', - '#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, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $display = &$form_state['display']; - $style = $form_state['style']; - $type = $form_state['type']; - - $styles = panels_get_styles(); - - $function = ($type == 'pane' ? 'render pane' : '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' => '
        ', - '#suffix' => '
        ', - '#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, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $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' => '
        ', - '#suffix' => '
        ', - '#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, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $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']; -} - -/** - * Configure lock on a pane form. - */ -function panels_edit_configure_pane_lock_form($form, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $display = &$form_state['display']; - $pane = &$form_state['pane']; - - if (empty($pane->locks)) { - $pane->locks = array('type' => 'none', 'regions' => array()); - } - - $form['type'] = array( - '#type' => 'radios', - '#title' => t('Lock type'), - '#options' => array( - 'none' => t('No lock'), - 'immovable' => t('Immovable'), - 'regions' => t('Regions'), - ), - '#default_value' => $pane->locks['type'], - ); - - $layout = panels_get_layout($display->layout); - $regions = panels_get_regions($layout, $display); - - $form['regions'] = array( - '#type' => 'checkboxes', - '#title' => t('Regions'), - '#options' => $regions, - '#description' => t('Select which regions this pane can be moved to.'), - '#dependency' => array( - 'radio:type' => array('regions'), - ), - '#default_value' => $pane->locks['regions'], - ); - - $form['#after_build'][] = 'panels_edit_configure_pane_lock_form_after_build'; - $form['next'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); - - return $form; -} - -function panels_edit_configure_pane_lock_form_after_build($element, $form_state) { - $region = $form_state['pane']->panel; - $element['regions'][$region]['#required'] = TRUE; - $element['regions'][$region]['#disabled'] = TRUE; - $element['regions'][$region]['#value'] = TRUE; - $element['regions'][$region]['#checked'] = TRUE; - $element['regions'][$region]['#attributes']['disabled'] = TRUE; - return $element; -} - -/** - * FAPI submission function for the lock configure form. - * - * All this does is set up $pane properly. The caller is responsible for - * actually storing this somewhere. - */ -function panels_edit_configure_pane_lock_form_submit($form, &$form_state) { - $pane = &$form_state['pane']; - $display = $form_state['display']; - - // We set this to true but forms do not submit disabled checkboxes - // and fapi is ignoring the #value directive probably because it - // is checkboxes: - $region = $form_state['pane']->panel; - $form_state['values']['regions'][$region] = $region; - - $pane->locks['type'] = $form_state['values']['type']; - $pane->locks['regions'] = array_filter($form_state['values']['regions']); -} - -/** - * Form to control basic visibility settings. - */ -function panels_edit_configure_access_settings_form($form, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $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, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $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, &$form_state) { - ctools_form_include($form_state, 'plugins', 'panels'); - form_load_include($form_state, 'php', 'panels', '/plugins/display_renderers/panels_renderer_editor.class'); - $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')) { - $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_simple.class.php b/plugins/display_renderers/panels_renderer_simple.class.php deleted file mode 100644 index a4779b8..0000000 --- a/plugins/display_renderers/panels_renderer_simple.class.php +++ /dev/null @@ -1,32 +0,0 @@ -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($external_settings = NULL) { - $this->prep_run = TRUE; - } -} diff --git a/plugins/display_renderers/panels_renderer_standard.class.php b/plugins/display_renderers/panels_renderer_standard.class.php deleted file mode 100644 index db71b6b..0000000 --- a/plugins/display_renderers/panels_renderer_standard.class.php +++ /dev/null @@ -1,645 +0,0 @@ -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['regions'])) { - $this->plugins['layout']['regions'] = 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 - $first = $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; - } - } - - // If the pane's subtype is unique, get it so that - // hook_ctools_content_subtype_alter() and/or - // hook_ctools_block_info() will be called. - if ($pane->type != $pane->subtype) { - $content_type = ctools_content_get_subtype($pane->type, $pane->subtype); - } - else { - $content_type = 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($content_type['render last'])) { - $last[$pid] = $pane; - } - // If it wants to render first, add it to the $first array. This is used - // by panes that need to do some processing before other panes are - // rendered. - else if (!empty($content_type['render first'])) { - $first[$pid] = $pane; - } - // Otherwise, render it in the normal order. - else { - $normal[$pid] = $pane; - } - } - $this->prepared['panes'] = $first + $normal + $last; - - // Allow other modules the alter the prepared panes array. - drupal_alter('panels_panes_prepared', $this->prepared['panes'], $this); - - 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']['regions'] 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']['regions'] 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() { - // Let the display refer back to the renderer. - $this->display->renderer_handler = $this; - - // 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, array('css_id' => check_plain($this->display->css_id), 'content' => $this->rendered['regions'], 'settings' => $this->display->layout_settings, 'display' => $this->display, 'layout' => $this->plugins['layout'], 'renderer' => $this)); - 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'])) { - $css = $this->plugins['layout']['css']; - if (!is_array($css)) { - $css = array($css); - } - foreach($css as $file) { - if (file_exists(path_to_theme() . '/' . $file)) { - $this->add_css(path_to_theme() . '/' . $file); - } - else { - $this->add_css($this->plugins['layout']['path'] . '/' . $file); - } - } - } - - if ($this->admin && isset($this->plugins['layout']['admin css'])) { - $admin_css = $this->plugins['layout']['admin css']; - if (!is_array($admin_css)) { - $admin_css = array($admin_css); - } - foreach($admin_css as $file) { - $this->add_css($this->plugins['layout']['path'] . '/' . $file); - } - } - } - - /** - * Add CSS information to the renderer. - * - * To facilitate previews over Views, CSS can now be added in a manner - * that does not necessarily mean just using drupal_add_css. Therefore, - * during the panel rendering process, this method can be used to add - * css and make certain that ti gets to the proper location. - * - * The arguments should exactly match drupal_add_css(). - * - * @see drupal_add_css - */ - function add_css($filename) { - switch ($this->meta_location) { - case 'standard': - drupal_add_css($filename); - break; - case 'inline': - $url = base_path() . $filename; - $this->prefix .= ''."\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) { - module_invoke_all('panels_pane_prerender', $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'], array('content' => $content, 'pane' => $pane, 'display' => $this->display, 'style' => $style, 'settings' => $pane->style['settings'])); - - // This could be null if no theme function existed. - if (isset($output)) { - return $output; - } - } - } - - // fallback - return theme('panels_pane', array('content' => $content, 'pane' => $pane, 'display' => $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(); - } - - $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 { - if ($caching) { - // This is created before rendering so that calls to drupal_add_js - // and drupal_add_css will be captured. - $cache = new panels_cache_object(); - } - - $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, $this, $this->display); - } - if ($caching && isset($cache)) { - $cache->set_content($content); - panels_set_cached_content($cache, $this->display, $this->display->args, $this->display->context, $pane); - $content = $cache->content; - } - } - - // If there's content, check if we've css configuration to add. - if (!empty($content)) { - // Pass long the css_id that is usually available. - if (!empty($pane->css['css_id'])) { - $content->css_id = check_plain($pane->css['css_id']); - } - - // Pass long the css_class that is usually available. - if (!empty($pane->css['css_class'])) { - $content->css_class = check_plain($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; - } - - $output = theme($style['render region'], array('display' => $this->display, 'owner_id' => $owner_id, 'panes' => $panes, 'settings' => $style_settings, 'region_id' => $region_id, 'style' => $style)); - return $output; - } -} diff --git a/plugins/display_renderers/simple.inc b/plugins/display_renderers/simple.inc deleted file mode 100644 index a370216..0000000 --- a/plugins/display_renderers/simple.inc +++ /dev/null @@ -1,8 +0,0 @@ - 'panels_renderer_simple', -); diff --git a/plugins/display_renderers/standard.inc b/plugins/display_renderers/standard.inc deleted file mode 100644 index 2e86b31..0000000 --- a/plugins/display_renderers/standard.inc +++ /dev/null @@ -1,5 +0,0 @@ - 'panels_renderer_standard', -); diff --git a/plugins/export_ui/panels_layouts.inc b/plugins/export_ui/panels_layouts.inc deleted file mode 100644 index 20bf44d..0000000 --- a/plugins/export_ui/panels_layouts.inc +++ /dev/null @@ -1,23 +0,0 @@ - 'panels_layout', - 'access' => 'administer panels layouts', - - 'menu' => array( - 'menu prefix' => 'admin/structure/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', - ), -); - diff --git a/plugins/export_ui/panels_layouts_ui.class.php b/plugins/export_ui/panels_layouts_ui.class.php deleted file mode 100644 index ecf3b7b..0000000 --- a/plugins/export_ui/panels_layouts_ui.class.php +++ /dev/null @@ -1,245 +0,0 @@ -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(); - - 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 = panels_edit_display_form($form, $form_state); - - // 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); - - // While we short circuited the main submit hook, we need to keep this one. - panels_edit_display_settings_form_submit($form, $form_state); - $form_state['item']->settings = $form_state['display']->layout_settings; - } - - function edit_form_validate(&$form, &$form_state) { - parent::edit_form_validate($form, $form_state); - - // While we short circuited the main validate hook, we need to keep this one. - panels_edit_display_settings_form_validate($form, $form_state); - } - - 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'); - - $ops = theme('links__ctools_dropbutton', array('links' => $operations, 'attributes' => array('class' => array('links', 'inline')))); - - $this->rows[$item->name] = array( - 'data' => array( - array('data' => check_plain($type), 'class' => array('ctools-export-ui-type')), - array('data' => check_plain($item->name), 'class' => array('ctools-export-ui-name')), - array('data' => check_plain($item->admin_title), 'class' => array('ctools-export-ui-title')), - array('data' => $category, 'class' => array('ctools-export-ui-category')), - array('data' => $ops, 'class' => array('ctools-export-ui-operations')), - ), - 'title' => check_plain($item->admin_description), - 'class' => array(!empty($item->disabled) ? 'ctools-export-ui-disabled' : 'ctools-export-ui-enabled'), - ); - } - - function list_table_header() { - return array( - array('data' => t('Type'), 'class' => array('ctools-export-ui-type')), - array('data' => t('Name'), 'class' => array('ctools-export-ui-name')), - array('data' => t('Title'), 'class' => array('ctools-export-ui-title')), - array('data' => t('Category'), 'class' => array('ctools-export-ui-category')), - array('data' => t('Operations'), 'class' => array('ctools-export-ui-operations')), - ); - } -} diff --git a/plugins/layouts/flexible/flexible-admin.css b/plugins/layouts/flexible/flexible-admin.css deleted file mode 100644 index 744e46e..0000000 --- a/plugins/layouts/flexible/flexible-admin.css +++ /dev/null @@ -1,87 +0,0 @@ - -#panels-dnd-main .panel-flexible-edit-layout div.panels-display .pane-add-link, -.panel-flexible-edit-layout .panel-pane { - display: none; -} - -.panel-flexible-edit-layout div.panels-display h2.label { - padding-right: 0; -} - -.panel-flexible-edit-layout .panels-flexible-column-inside { -/* margin: 5px; */ - border: 1px dotted green; -} - -.panels-flexible-column-inside { -/* overflow: hidden; */ -} - -.panel-flexible-edit-layout .panels-flexible-column > .flexible-title { - color: green; -} - -.panel-flexible-edit-layout .panels-flexible-row-inside { - margin: 5px; - border: 1px dotted blue; -} - -.panel-flexible-edit-layout .panels-flexible-row > .flexible-title { - color: blue; -} - -.panel-flexible-no-edit-layout .flexible-layout-only { - display: none; -} - -.panel-flexible-edit-layout .flexible-title { - text-align: center; - width: 5em; - margin-left: auto; - margin-right: auto; -} - -.panel-flexible-no-edit-layout .panels-flexible-splitter { - display: none; -} - -.panels-flexible-splitter span { - display: none; -} - -.panels-flexible-splitter { - width: 11px; - float: left; - margin-left: -7px; - margin-right: -6px; - cursor: e-resize; /* in case col-resize isn't supported */ - cursor: col-resize; - height: 30px; - position: relative; - z-index: 1; - background: url(grippie-vertical.png) center center no-repeat #eee; - border: 1px solid #ccc; -} - -.flexible-splitting { - border: 2px dotted yellow !important; - margin: -2px !important; -} - -.flexible-splitter-hover-box { - position: absolute; - z-index: 1000; - background: white; - color: black; - border: 1px solid black; - width: 60px; - height: 2em; - text-align: center; - line-height: 2em; -} - -#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 deleted file mode 100644 index 910b3c8..0000000 --- a/plugins/layouts/flexible/flexible-admin.js +++ /dev/null @@ -1,421 +0,0 @@ -(function ($) { - -Drupal.flexible = Drupal.flexible || {}; - -Drupal.flexible.splitters = []; - -/** - * Fix the height of all splitters to be the same as the items they are - * splitting. - */ -Drupal.flexible.fixHeight = function() { - for (i in Drupal.flexible.splitters) { - Drupal.flexible.splitters[i].fixHeight(); - } -} - -Drupal.behaviors.flexibleAdmin = { - attach: function(context) { - // Show/hide layout manager button - $('input#panels-flexible-toggle-layout:not(.panels-flexible-processed)', context) - .addClass('panels-flexible-processed') - .click(function() { - $('.panel-flexible-admin') - .toggleClass('panel-flexible-no-edit-layout') - .toggleClass('panel-flexible-edit-layout'); - - if ($('.panel-flexible-admin').hasClass('panel-flexible-edit-layout')) { - $(this).val(Drupal.t('Hide layout designer')); - Drupal.flexible.fixHeight(); - } - else { - $(this).val(Drupal.t('Show layout designer')); - } - return false; - }); - - // Window splitter behavior. - $('div.panels-flexible-splitter:not(.panels-splitter-processed)') - .addClass('panels-splitter-processed') - .each(function() { - Drupal.flexible.splitters.push(new Drupal.flexible.splitter($(this))); - }); - - Drupal.flexible.fixHeight(); - } -}; - -Drupal.flexible.splitter = function($splitter) { - var splitter = this; - - this.fixHeight = function() { - // Set the splitter height to the shorter of the two: - $splitter.height(Math.max(this.left.outerHeight(), this.right.outerHeight())); - } - - function splitterStart(event) { - // Bind motion events. - $(document) - .bind("mousemove", splitterMove) - .bind("mouseup", splitterEnd); - - // Calculate some data about our split regions: - splitter.getSizes(); - - // The X coordinate where we clicked. - splitter.startX = event.pageX; - - // The current sizes of the left/right panes. - splitter.currentLeft = parseFloat(splitter.left_width) * parseFloat(splitter.left_scale); - splitter.currentRight = parseFloat(splitter.right_width) * parseFloat(splitter.right_scale); - - // The starting sizes of the left right panes. - splitter.startLeft = splitter.currentLeft; - splitter.startRight = splitter.currentRight; - - if (splitter.left_width_type == splitter.right_width_type) { - // If they're the same type, add the two together so we know how - // much space we have for splitting. - splitter.max = splitter.startLeft + splitter.startRight; - - // calculate unit size and min/max width. - if (splitter.left_width_type == '%') { - splitter.left_total = splitter.left.width() / (splitter.left_width / 100); - // One pixel is equivalent to what percentage of the total? - splitter.left_unit = (1 / splitter.left_total) * 100; - splitter.left_min = 5; // minimum % we'll use. - } - else { - splitter.left_unit = 1; - splitter.left_min = 25; // minimum pixels we'll use. - } - if (splitter.right_width_type == '%') { - splitter.right_total = splitter.right.width() / (splitter.right_width / 100); - // One pixel is equivalent to what percentage of the total? - splitter.right_unit = (1 / splitter.right_total) * 100; - splitter.right_min = 5; // minimum % we'll use. - } - else { - splitter.right_unit = 1; - splitter.right_min = 25; // minimum pixels we'll use. - } - } - else { - // Figure out the parent blob's width and set the max to that - splitter.parent = $splitter.parent().parent(); - - if (splitter.left_width_type != 'px') { - // Only the 'px' side can resize. - splitter.left_unit = 0; - splitter.right_unit = 1; - splitter.right_min = 25; - splitter.right_padding = parseInt(splitter.parent.css('padding-right')); - splitter.right_parent = parseInt(splitter.right.parent().css('margin-right')); - splitter.max = splitter.right.width() + splitter.left.parent().width() - - (splitter.left.siblings(':not(.panels-flexible-splitter)').length * 25) - 25; - } - else { - splitter.right_unit = 0; - splitter.left_unit = 1; - splitter.left_min = 25; - splitter.left_padding = parseInt(splitter.parent.css('padding-left')); - splitter.left_parent = parseInt(splitter.left.parent().css('margin-left')); - if (splitter.right_id) { - splitter.max = splitter.left.width() + splitter.right.parent().width() - - (splitter.right.siblings(':not(.panels-flexible-splitter)').length * 25) - 25; - } - else { - var subtract = 0; - splitter.left.siblings(':not(.panels-flexible-splitter)').each(function() { subtract += $(this).width()}); - splitter.max = splitter.left.parent().width() - subtract; - } - } - } - - var offset = $(splitter.splitter).offset(); - - // Create boxes to display widths left and right of the mouse pointer. - // Create left box only if left box is mobile. - if (splitter.left_unit) { - splitter.left_box = $('
         
        '); - $('body').append(splitter.left_box); - splitter.left_box.css('top', offset.top); - splitter.left_box.css('left', event.pageX - 65); - - if (splitter.left_width_type == '%') { - var left = splitter.currentLeft / splitter.left_scale; - splitter.left_box.html(left.toFixed(2) + splitter.left_width_type); - } - else { - // make sure pixel values are always whole integers. - splitter.currentLeft = parseInt(splitter.currentLeft); - splitter.left_box.html(splitter.currentLeft + splitter.left_width_type); - } - } - - // Create the right box if the right side is mobile. - if (splitter.right_unit) { - splitter.right_box = $('
        '); - $('body').append(splitter.right_box); - splitter.right_box.css('top', offset.top); - splitter.right_box.css('left', event.pageX + 5); - if (splitter.right_width_type == '%') { - var right = splitter.currentRight / splitter.right_scale; - splitter.right_box.html(right.toFixed(2) + splitter.right_width_type); - } - else { - // make sure pixel values are always whole integers. - splitter.currentRight = parseInt(splitter.currentRight); - splitter.right_box.html(splitter.currentRight + splitter.right_width_type); - } - } - - return false; - }; - - function splitterMove(event) { - var diff = splitter.startX - event.pageX; - var moved = 0; - - if (event.keyCode == 37) diff = 10; - if (event.keyCode == 39) diff = -10; - - // Bah, javascript has no logical xor operator - if ((splitter.left_unit && !splitter.right_unit) || - (!splitter.left_unit && splitter.right_unit)) { - // This happens when one side is fixed and the other side is fluid. The - // fixed side actually adjusts while the fluid side does not. However, - // in order to move the fluid side we have to adjust the padding - // on our parent object. - if (splitter.left_unit) { - // Only the left box is allowed to move. - splitter.currentLeft = splitter.startLeft - diff; - - if (splitter.currentLeft < splitter.left_min) { - splitter.currentLeft = splitter.left_min; - } - if (splitter.currentLeft > splitter.max) { - splitter.currentLeft = splitter.max; - } - - // If the shift key is pressed, go with 1% or 10px boundaries. - if (event.shiftKey) { - splitter.currentLeft = parseInt(splitter.currentLeft / 10) * 10; - } - moved = (splitter.startLeft - splitter.currentLeft); - } - else { - // Only the left box is allowed to move. - splitter.currentRight = splitter.startRight + diff; - - if (splitter.currentRight < splitter.right_min) { - splitter.currentRight = splitter.right_min; - } - if (splitter.currentRight > splitter.max) { - splitter.currentRight = splitter.max; - } - - // If the shift key is pressed, go with 1% or 10px boundaries. - if (event.shiftKey) { - splitter.currentRight = parseInt(splitter.currentRight / 10) * 10; - } - moved = (splitter.currentRight - splitter.startRight); - } - } - else { - // If they are both the same type, do this.. - // Adjust the left side by the amount we moved. - var left = -1 * diff * splitter.left_unit; - - splitter.currentLeft = splitter.startLeft + left; - - if (splitter.currentLeft < splitter.left_min) { - splitter.currentLeft = splitter.left_min; - } - if (splitter.currentLeft > splitter.max - splitter.right_min) { - splitter.currentLeft = splitter.max - splitter.right_min; - } - - // If the shift key is pressed, go with 1% or 10px boundaries. - if (event.shiftKey) { - if (splitter.left_width_type == '%') { - splitter.currentLeft = parseInt(splitter.currentLeft / splitter.left_scale) * splitter.left_scale; - } - else { - splitter.currentLeft = parseInt(splitter.currentLeft / 10) * 10; - } - } - - // Now automatically make the right side to be the other half. - splitter.currentRight = splitter.max - splitter.currentLeft; - - // recalculate how far we've moved into pixels so we can adjust our visible - // boxes. - moved = (splitter.startLeft - splitter.currentLeft) / splitter.left_unit; - } - - if (splitter.left_unit) { - splitter.left_box.css('left', splitter.startX - 65 - moved); - if (splitter.left_width_type == '%') { - var left = splitter.currentLeft / splitter.left_scale; - splitter.left_box.html(left.toFixed(2) + splitter.left_width_type); - } - else { - splitter.left_box.html(parseInt(splitter.currentLeft) + splitter.left_width_type); - } - - // Finally actually move the left side - splitter.left.css('width', splitter.currentLeft + splitter.left_width_type); - } - else { - // if not moving the left side, adjust the parent padding instead. - splitter.parent.css('padding-right', (splitter.right_padding + moved) + 'px'); - splitter.right.parent().css('margin-right', (splitter.right_parent - moved) + 'px'); - } - - if (splitter.right_unit) { - splitter.right_box.css('left', splitter.startX + 5 - moved); - if (splitter.right_width_type == '%') { - var right = splitter.currentRight / splitter.right_scale; - splitter.right_box.html(right.toFixed(2) + splitter.right_width_type); - } - else { - splitter.right_box.html(parseInt(splitter.currentRight) + splitter.right_width_type); - } - - // Finally actually move the right side - splitter.right.css('width', splitter.currentRight + splitter.right_width_type); - } - else { - // if not moving the right side, adjust the parent padding instead. - splitter.parent.css('padding-left', (splitter.left_padding - moved) + 'px'); - splitter.left.parent().css('margin-left', (splitter.left_parent + moved) + 'px'); - } - return false; - }; - - function splitterKeyPress(event) { - splitterStart(event); - splitterMove(event); - splitterEnd(event); - }; - - function splitterEnd(event) { - if (splitter.left_unit) { - splitter.left_box.remove(); - } - - if (splitter.right_unit) { - splitter.right_box.remove(); - } - - - splitter.left.css("-webkit-user-select", "text"); // let Safari select text again - splitter.right.css("-webkit-user-select", "text"); // let Safari select text again - - if (splitter.left_unit) { - splitter.left_width = splitter.currentLeft / parseFloat(splitter.left_scale); - } - - if (splitter.right_unit) { - splitter.right_width = splitter.currentRight / parseFloat(splitter.right_scale); - } - - splitter.putSizes(); - Drupal.flexible.fixHeight(); - - $(document) - .unbind("mousemove", splitterMove) - .unbind("mouseup", splitterEnd); - - // Store the data on the server. - Drupal.ajax['flexible-splitter-ajax'].options.data = { - 'left': splitter.left_id, - 'left_width': splitter.left_width, - 'right': splitter.right_id, - 'right_width': splitter.right_width - }; - - $('.panel-flexible-edit-layout').trigger('UpdateFlexibleSplitter'); - }; - - this.getSizes = function() { - splitter.left_width = $splitter.children('.panels-flexible-splitter-left-width').html(); - splitter.left_scale = $splitter.children('.panels-flexible-splitter-left-scale').html(); - splitter.left_width_type = $splitter.children('.panels-flexible-splitter-left-width-type').html(); - splitter.right_width = $splitter.children('.panels-flexible-splitter-right-width').html(); - splitter.right_scale = $splitter.children('.panels-flexible-splitter-right-scale').html(); - splitter.right_width_type = $splitter.children('.panels-flexible-splitter-right-width-type').html(); - }; - - this.putSizes = function() { - $(splitter.left_class + '-width').html(splitter.left_width); - if (splitter.left_class != splitter.right_class) { - $(splitter.right_class + '-width').html(splitter.right_width); - } - } - - splitter.splitter = $splitter; - splitter.left_class = $splitter.children('.panels-flexible-splitter-left').html(); - splitter.left_id = $splitter.children('.panels-flexible-splitter-left-id').html(); - splitter.left = $(splitter.left_class); - splitter.right_class = $splitter.children('.panels-flexible-splitter-right').html(); - splitter.right_id = $splitter.children('.panels-flexible-splitter-right-id').html(); - splitter.right = $(splitter.right_class); - - $splitter - .bind("mousedown", splitterStart) - .bind("keydown", splitterKeyPress); - -}; - -$(function() { - /** - * Provide an AJAX response command to allow the server to request - * height fixing. - */ - Drupal.ajax.prototype.commands.flexible_fix_height = function(ajax, command, status) { - Drupal.flexible.fixHeight(); - }; - - /** - * Provide an AJAX response command to allow the server to change width on existing splitters. - */ - Drupal.ajax.prototype.commands.flexible_set_width = function(ajax, command, status) { - $(command.selector).html(command.width); - }; - - /** - * Provide an AJAX response command to fix the first/last bits of a - * group. - */ - Drupal.ajax.prototype.commands.flexible_fix_firstlast = function(ajax, data, status) { - $(data.selector + ' > div > .' + data.base) - .removeClass(data.base + '-first') - .removeClass(data.base + '-last'); - - $(data.selector + ' > div > .' + data.base + ':first') - .addClass(data.base + '-first'); - $(data.selector + ' > div > .' + data.base + ':last') - .addClass(data.base + '-last'); - }; - - // Create a generic ajax callback for use with the splitters which - // background AJAX to store their data. - var element_settings = { - url: Drupal.settings.flexible.resize, - event: 'UpdateFlexibleSplitter', - keypress: false, - // No throbber at all. - progress: { 'type': 'none' } - }; - - Drupal.ajax['flexible-splitter-ajax'] = new Drupal.ajax('flexible-splitter-ajax', $('.panel-flexible-admin').get(0), element_settings); - - // Prevent ajax goo from doing odd things to our layout. - Drupal.ajax['flexible-splitter-ajax'].beforeSend = function() { }; - Drupal.ajax['flexible-splitter-ajax'].beforeSerialize = function() { }; - -}); - -})(jQuery); diff --git a/plugins/layouts/flexible/flexible.css b/plugins/layouts/flexible/flexible.css deleted file mode 100644 index 0d1fbe6..0000000 --- a/plugins/layouts/flexible/flexible.css +++ /dev/null @@ -1,4 +0,0 @@ - -.panel-flexible .panel-separator { - margin: 0 0 1em 0; -} diff --git a/plugins/layouts/flexible/flexible.inc b/plugins/layouts/flexible/flexible.inc deleted file mode 100644 index f49886b..0000000 --- a/plugins/layouts/flexible/flexible.inc +++ /dev/null @@ -1,1802 +0,0 @@ - t('Flexible'), - 'category' => t('Builders'), - 'icon' => 'flexible.png', - 'theme' => 'panels_flexible', - 'admin theme' => 'panels_flexible_admin', - 'css' => 'flexible.css', - 'admin css' => 'flexible-admin.css', - 'settings form' => 'panels_flexible_settings_form', - 'settings submit' => 'panels_flexible_settings_submit', - 'settings validate' => 'panels_flexible_settings_validate', - 'regions 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', - ), -); - -/** - * 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_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); - } - - return $layouts; -} - -/** - * Convert settings from old style to new, or provide defaults for - * empty settings. - * @param $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; - if ($settings) { - return $settings; - } - } - - if (empty($settings)) { - // set up a default - $settings = array( - 'items' => array( - // The 'canvas' is a special row that does not get rendered - // normally, but is used to contain the columns. - 'canvas' => array( - 'type' => 'row', - 'contains' => 'column', - 'children' => array('main'), - 'parent' => NULL, - ), - 'main' => array( - 'type' => 'column', - 'width' => 100, - 'width_type' => '%', - 'children' => array('main-row'), - 'parent' => 'canvas', - ), - 'main-row' => array( - 'type' => 'row', - 'contains' => 'region', - 'children' => array('center'), - 'parent' => 'main', - ), - 'center' => array( - 'type' => 'region', - 'title' => t('Center'), - 'width' => 100, - 'width_type' => '%', - 'parent' => 'main-row', - ), - ), - ); - } - else if (!isset($settings['items'])) { - // Convert an old style flexible to a new style flexible. - $old = $settings; - $settings = array(); - $settings['items']['canvas'] = array( - 'type' => 'row', - 'contains' => 'column', - 'children' => array(), - 'parent' => NULL, - ); - // add the left sidebar column, row and region if it exists. - if (!empty($old['sidebars']['left'])) { - $settings['items']['canvas']['children'][] = 'sidebar-left'; - $settings['items']['sidebar-left'] = array( - 'type' => 'column', - 'width' => $old['sidebars']['left_width'], - 'width_type' => $old['sidebars']['width_type'], - 'children' => array('sidebar-left-row'), - 'parent' => 'canvas', - ); - $settings['items']['sidebar-left-row'] = array( - 'type' => 'row', - 'contains' => 'region', - 'children' => array('sidebar_left'), - 'parent' => 'sidebar-left', - ); - $settings['items']['sidebar_left'] = array( - 'type' => 'region', - 'title' => t('Left sidebar'), - 'width' => 100, - 'width_type' => '%', - 'parent' => 'sidebar-left-row', - ); - } - - $settings['items']['canvas']['children'][] = 'main'; - - if (!empty($old['sidebars']['right'])) { - $settings['items']['canvas']['children'][] = 'sidebar-right'; - $settings['items']['sidebar-right'] = array( - 'type' => 'column', - 'width' => $old['sidebars']['right_width'], - 'width_type' => $old['sidebars']['width_type'], - 'children' => array('sidebar-right-row'), - 'parent' => 'canvas', - ); - $settings['items']['sidebar-right-row'] = array( - 'type' => 'row', - 'contains' => 'region', - 'children' => array('sidebar_right'), - 'parent' => 'sidebar-right', - ); - $settings['items']['sidebar_right'] = array( - 'type' => 'region', - 'title' => t('Right sidebar'), - 'width' => 100, - 'width_type' => '%', - 'parent' => 'sidebar-right-row', - ); - } - - // Add the main column. - $settings['items']['main'] = array( - 'type' => 'column', - 'width' => 100, - 'width_type' => '%', - 'children' => array(), - 'parent' => 'canvas', - ); - - // Add rows and regions. - for ($row = 1; $row <= intval($old['rows']); $row++) { - // Create entry for the row - $settings['items']["row_$row"] = array( - 'type' => 'row', - 'contains' => 'region', - 'children' => array(), - 'parent' => 'main', - ); - // Add the row to the parent's children - $settings['items']['main']['children'][] = "row_$row"; - - for ($col = 1; $col <= intval($old["row_$row"]['columns']); $col++) { - // Create entry for the region - $settings['items']["row_${row}_$col"] = array( - 'type' => 'region', - 'width' => $old["row_$row"]["width_$col"], - 'width_type' => '%', - 'parent' => "row_$row", - ); - // Add entry for the region to the row's children - $settings['items']["row_$row"]['children'][] = "row_${row}_$col"; - - // Apply the proper title to the region - if (!empty($old["row_$row"]['names'][$col - 1])) { - $settings['items']["row_${row}_$col"]['title'] = $old["row_$row"]['names'][$col - 1]; - } - else { - $settings['items']["row_${row}_$col"]['title'] = t("Row @row, Column @col", array('@row' => $row, '@col' => $col)); - } - } - } - } - else if (isset($settings['canvas'])) { - // Convert the old 'canvas' to the new canvas row. - $settings['items']['canvas'] = array( - 'type' => 'row', - 'contains' => 'column', - 'children' => $settings['canvas'], - 'parent' => NULL, - ); - unset($settings['canvas']); - } -} - -/** - * Define the actual list of columns and rows for this flexible panel. - */ -function panels_flexible_panels($display, $settings, $layout) { - $items = array(); - panels_flexible_convert_settings($settings, $layout); - foreach ($settings['items'] as $id => $item) { - // Remove garbage values. - if (!isset($item['type'])) { - unset($items[$id]); - } - else if ($item['type'] == 'region') { - $items[$id] = $item['title']; - } - } - - return $items; -} - -/** - * 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 panels_flexible_create_renderer($admin, $css_id, $content, $settings, &$display, $layout, $handler) { - $renderer = new stdClass; - $renderer->settings = $settings; - $renderer->content = $content; - $renderer->css_id = $css_id; - $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 = $css_id ? 'id="' . $css_id . '"' : ''; - $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($vars) { - $css_id = $vars['css_id']; - $content = $vars['content']; - $settings = $vars['settings']; - $display = $vars['display']; - $layout = $vars['layout']; - $handler = $vars['renderer']; - - panels_flexible_convert_settings($settings, $layout); - - $renderer = panels_flexible_create_renderer(FALSE, $css_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 (!empty($renderer->css_cache_name) && empty($display->editing_layout)) { - ctools_include('css'); - // Generate an id based upon rows + columns: - $filename = ctools_css_retrieve($renderer->css_cache_name); - if (!$filename) { - $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 { - drupal_add_css($filename); - } - } - 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. We also do this if we've been told we're in the layout - // editor so that it always gets fresh CSS. - drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE)); - } - - // Also store the CSS on the display in case the live preview or something - // needs it - $display->add_css = $css; - - $output = "
        base['canvas'] . " clearfix\" $renderer->id_str>\n"; - $output .= "
        base['canvas'] . "-inside\">\n"; - - $output .= panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], $renderer->base['canvas']); - - // Wrap the whole thing up nice and snug - $output .= "
        \n
        \n"; - - return $output; -} - -/** - * Draw the flexible layout. - */ -function theme_panels_flexible_admin($vars) { - $css_id = $vars['css_id']; - $content = $vars['content']; - $settings = $vars['settings']; - $display = $vars['display']; - $layout = $vars['layout']; - $handler = $vars['renderer']; - - // 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(array('css_id' => $css_id, 'content' => $content, 'settings' => $settings, 'display' => $display, 'layout' => $layout, 'renderer' => $handler)); - } - - panels_flexible_convert_settings($settings, $layout); - $renderer = panels_flexible_create_renderer(TRUE, $css_id, $content, $settings, $display, $layout, $handler); - - $css = panels_flexible_render_css($renderer); - - // For the administrative view, add CSS directly to head. - drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE)); - - if (empty($display->editing_layout)) { - $output = ''; - if (user_access('administer panels layouts')) { - $output .= ''; - $output .= ''; - } - $output .= "
        base['canvas'] . " clearfix panel-flexible-admin panel-flexible-no-edit-layout\" $renderer->id_str>\n"; - } - else { - $output = "
        base['canvas'] . " clearfix panel-flexible-admin panel-flexible-edit-layout\" $renderer->id_str>\n"; - } - $output .= "
        base['canvas'] . "-inside \">\n"; - - $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 .= "
        \n
        \n"; - - 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; -} - -/** - * Render a piece of a flexible layout. - */ -function panels_flexible_render_items($renderer, $list, $owner_id) { - $output = ''; - $groups = array('left' => '', 'middle' => '', 'right' => ''); - $max = count($list) - 1; - $prev = NULL; - - foreach ($list as $position => $id) { - $item = $renderer->settings['items'][$id]; - $location = isset($renderer->positions[$id]) ? $renderer->positions[$id] : 'middle'; - - if ($renderer->admin && $item['type'] != 'row' && $prev ) { - $groups[$location] .= panels_flexible_render_splitter($renderer, $prev, $id); - } - - switch ($item['type']) { - case 'column': - $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'], $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] : " "; - $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max); - break; - } - - // If all items are fixed then we have a special splitter on the right to - // control the overall width. - if (!empty($renderer->admin) && $max == $position && $location == 'left') { - $groups[$location] .= panels_flexible_render_splitter($renderer, $id, NULL); - } - $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) { - if ($group_count > 1 || $renderer->admin) { - $output .= '
        ' . $content . '
        '; - } - else { - $output .= $content; - } - } - } - - return $output; -} - -/** - * Render a column in the flexible layout. - */ -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 = '
        ' . "\n"; - - if (!empty($renderer->admin)) { - $output .= panels_flexible_render_item_links($renderer, $id, $item); - } - - $output .= '
        \n"; - $output .= $content; - $output .= '
        ' . "\n"; - $output .= '
        ' . "\n"; - - return $output; -} -/** - * Render a splitter div to place between the $left and $right items. - * - * If the right ID is NULL that means there isn't actually a box to the - * right, but we need a splitter anyway. We'll mostly use info about the - * left, but pretend it's 'fluid' so that the javascript won't actually - * modify the right item. - */ -function panels_flexible_render_splitter($renderer, $left_id, $right_id) { - $left = $renderer->settings['items'][$left_id]; - - $left_class = $renderer->base[$left['type']] . '-' . $left_id; - if ($right_id) { - $right = $renderer->settings['items'][$right_id]; - $right_class = $renderer->base[$left['type']] . '-' . $right_id; - } - else { - $right = $left; - $right_class = $left_class; - } - - $output = '
        '; - - // Name the left object - $output .= ''; - $output .= '.' . $left_class; - $output .= ''; - - $output .= ''; - $output .= $left_id; - $output .= ''; - - $output .= ''; - $output .= $left['width']; - $output .= ''; - - $output .= ''; - $output .= isset($renderer->scale[$left_id]) ? $renderer->scale[$left_id] : 1; - $output .= ''; - - $output .= ''; - $output .= $left['width_type']; - $output .= ''; - - // Name the right object - $output .= ''; - $output .= '.' . $right_class; - $output .= ''; - - $output .= ''; - $output .= $right_id; - $output .= ''; - - $output .= ''; - $output .= $right['width']; - $output .= ''; - - $output .= ''; - $output .= isset($renderer->scale[$right_id]) ? $renderer->scale[$right_id] : 1; - $output .= ''; - - $output .= ''; - // If there is no right, make it fluid. - $output .= $right_id ? $right['width_type'] : '%'; - $output .= ''; - - $output .= '
        '; - return $output; -} - -/** - * Render the dropdown links for an item. - */ -function panels_flexible_render_item_links($renderer, $id, $item) { - $links = array(); - $remove = ''; - $add = ''; - if ($item['type'] == 'column') { - $title = t('Column'); - $settings = t('Column settings'); - if (empty($item['children'])) { - $remove = t('Remove column'); - $add = t('Add row'); - } - else { - $add = t('Add row to top'); - $add2 = t('Add row to bottom'); - } - } - 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 ($id != 'canvas') { - $remove = t('Remove row'); - } - $add = $item['contains'] == 'region' ? t('Add region') : t('Add column'); - } - else { - $add = $item['contains'] == 'region' ? t('Add region to left') : t('Add column to left'); - $add2 = $item['contains'] == 'region' ? t('Add region to right') : t('Add column to right'); - } - } - else if ($item['type'] == 'region') { - $title = t('Region'); - $settings = t('Region settings'); - $remove = t('Remove region'); - } - - if (!empty($settings)) { - $links[] = array( - 'title' => $settings, - 'href' => $renderer->handler->get_url('layout', 'settings', $id), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - } - if ($add) { - $links[] = array( - 'title' => $add, - 'href' => $renderer->handler->get_url('layout', 'add', $id), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - } - if (isset($add2)) { - $links[] = array( - 'title' => $add2, - 'href' => $renderer->handler->get_url('layout', 'add', $id, 'right'), - 'attributes' => array('class' => array('ctools-use-modal')), - ); - } - if ($remove) { - $links[] = array( - 'title' => $remove, - 'href' => $renderer->handler->get_url('layout', 'remove', $id), - 'attributes' => array('class' => array('use-ajax')), - ); - } - - return theme('ctools_dropdown', array('title' => $title, 'links' => $links, 'class' => 'flexible-layout-only flexible-links flexible-title flexible-links-' . $id)); -} -/** - * Provide CSS for a flexible layout. - */ -function panels_flexible_render_css($renderer) { - 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'); -} - -/** - * Render the CSS for a group of items to be displayed together. - * - * Columns and regions, when displayed as a group, need to cooperate in - * 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, $id) { - $css = array(); - - // Start off with some generic CSS to properly pad regions - $css[$owner_id . ' .' . $renderer->item_class['region']] = array( - 'padding' => '0', - ); - - $css[$owner_id . ' .' . $renderer->item_class['region'] . '-inside'] = array( - 'padding-right' => $renderer->region_separation, - 'padding-left' => $renderer->region_separation, - ); - - $css[$owner_id . ' .' . $renderer->item_class['region'] . '-inside-first'] = array( - 'padding-left' => '0', - ); - - $css[$owner_id . ' .' . $renderer->item_class['region'] . '-inside-last'] = array( - 'padding-right' => '0', - ); - - $css[$owner_id . ' .' . $renderer->item_class['column']] = array( - 'padding' => '0', - ); - - $css[$owner_id . ' .' . $renderer->item_class['column'] . '-inside'] = array( - 'padding-right' => $renderer->column_separation, - 'padding-left' => $renderer->column_separation, - ); - - $css[$owner_id . ' .' . $renderer->item_class['column'] . '-inside-first'] = array( - 'padding-left' => '0', - ); - - $css[$owner_id . ' .' . $renderer->item_class['column'] . '-inside-last'] = array( - 'padding-right' => '0', - ); - - // And properly pad rows too - $css[$owner_id . ' .' . $renderer->item_class['row']] = array( - 'padding' => '0 0 ' . $renderer->row_separation . ' 0', - 'margin' => '0', - ); - - $css[$owner_id . ' .' . $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); -} - -/** - * Construct an array with all of the CSS properties for a group. - * - * 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, $item_id) { - if ($type != 'row') { - // Go through our items and break up into right/center/right groups so we - // can figure out our offsets. - - // right == any items on the right that are 'fixed'. - // middle == all fluid items. - // right == any items on the right that are 'fixed'. - $left = $middle = $right = array(); - $left_total = $right_total = $middle_total = 0; - $current = 'left'; - foreach ($list as $id) { - if ($renderer->settings['items'][$id]['width_type'] == 'px') { - // fixed - if ($current == 'left') { - $left[] = $id; - $renderer->positions[$id] = 'left'; - $left_total += $renderer->settings['items'][$id]['width']; - } - else { - $current = 'right'; - $right[] = $id; - $renderer->positions[$id] = 'right'; - $right_total += $renderer->settings['items'][$id]['width']; - } - } - else { - // fluid - if ($current != 'right') { - $current = 'middle'; - $middle[] = $id; - $renderer->positions[$id] = 'middle'; - $middle_total += $renderer->settings['items'][$id]['width']; - } - // fall through: if current is 'right' and we ran into a 'fluid' then - // it gets *dropped* because that is invalid. - } - } - - // Go through our right sides and create CSS. - foreach ($left as $id) { - $class = "." . $renderer->base[$type] . "-$id"; - $css[$class] = array( - 'position' => 'relative', - 'float' => 'left', - 'background-color' => 'transparent', - 'width' => $renderer->settings['items'][$id]['width'] . "px", - ); - } - - // Do the same for right. - $right_pixels = 0; - - foreach ($right as $id) { - $class = "." . $renderer->base[$type] . "-$id"; - $css[$class] = array( - 'float' => 'left', - 'width' => $renderer->settings['items'][$id]['width'] . "px", - ); - } - - $max = count($middle) - 1; - - if ($middle_total) { - // 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 = $renderer->scale_base / $middle_total; - foreach ($middle as $position => $id) { - $class = "." . $renderer->base[$type] . "-$id"; - $css[$class] = array( - 'float' => 'left', - 'width' => number_format($renderer->settings['items'][$id]['width'] * $scale, 4, '.', '') . "%", - ); - - // Store this so we can use it later. - // @todo: Store the scale, not the new width, so .js can adjust - // bi-directionally. - $renderer->scale[$id] = $scale; - } - } - - // If there is any total remaining, we need to offset the splitter - // by this much too. - if ($left_total) { - // Add this even if it's 0 so we can handle removals. - $css["$owner_id-inside"]['padding-left'] = '0px'; - 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"; - // Make this one very specific to the admin CSS so that preview - // does not stomp it. - $css[".panel-flexible-admin $owner_id-inside"]['padding-left'] = '0px'; - } - else { - $css["$owner_id-inside"]['margin-left'] = '-' . $left_total . 'px'; - $css["$owner_id-inside"]['padding-left'] = $left_total . 'px'; - // IE hack - $css["* html $owner_id-inside"]['left'] = $left_total . "px"; - } - } - if ($right_total) { - $css["$owner_id-middle"]['margin-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. - foreach ($list as $id) { - $item = $renderer->settings['items'][$id]; - if (empty($item['children'])) { - continue; - } - - if ($type == 'column') { - // Columns can only contain rows. - $child_type = 'row'; - } - else { - $child_type = isset($item['contains']) ? $item['contains'] : 'region'; - } - - $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($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 = array(); - - if ($id != 'canvas') { - $siblings = $settings['items'][$item['parent']]['children']; - } - - - switch ($item['type']) { - case 'column': - $title = t('Configure column'); - break; - case 'row': - if ($id == 'canvas') { - $title = t('Configure canvas'); - } - else { - $title = t('Configure row'); - } - break; - case 'region': - $title = t('Configure region'); - break; - } - - $form_state = array( - 'display' => &$handler->display, - 'item' => &$item, - 'id' => $id, - 'siblings' => $siblings, - 'settings' => &$settings, - 'ajax' => TRUE, - 'title' => $title, - 'op' => 'edit', - ); - - $output = ctools_modal_form_wrapper('panels_flexible_config_item_form', $form_state); - if (!empty($form_state['executed'])) { - // If the width type changed then other nearby items will have - // to have their widths adjusted. - 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 = $renderer->base[$item['type']] . '-' . $id; - if ($item['type'] == 'region') { - $output[] = ajax_command_replace(".$class h2.label", - '

        ' . check_plain($item['title']) . '

        '); - } - - // Rerender our links in case something changed. - $output[] = 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[] = ajax_command_css($selector, $data); - } - } - - $output[] = ctools_modal_command_dismiss(); - } - - $handler->commands = $output; -} - -/** - * Configure a row, column or region on the flexible page. - * - * @param $form_state - * @return - */ -function panels_flexible_config_item_form($form, &$form_state) { - $display = &$form_state['display']; - $item = &$form_state['item']; - $siblings = &$form_state['siblings']; - $settings = &$form_state['settings']; - $id = &$form_state['id']; - - if ($item['type'] == 'region') { - $form['title'] = array( - '#title' => t('Region title'), - '#type' => 'textfield', - '#default_value' => $item['title'], - '#required' => TRUE, - ); - } - - if ($id == 'canvas') { - $form['class'] = array( - '#title' => t('Canvas class'), - '#type' => 'textfield', - '#default_value' => isset($item['class']) ? $item['class'] : '', - '#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.'), - ); - - $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['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['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') { - // 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.'); - } - } - } - - $form['save'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); - - return $form; -} - -/** - * Submit handler for editing a flexible item. - */ -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 ($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($handler, $id, $location = 'left') { - ctools_include('modal'); - ctools_include('ajax'); - $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.')); - } - - $parent = &$settings['items'][$id]; - - switch ($parent['type']) { - case 'column': - $title = t('Add row'); - // Create the new item with defaults. - $item = array( - 'type' => 'row', - 'contains' => 'region', - 'children' => array(), - 'parent' => $id, - ); - break; - case 'row': - switch ($parent['contains']) { - case 'region': - $title = $location == 'left' ? t('Add region to left') : t('Add region to right'); - $item = array( - 'type' => 'region', - 'title' => '', - 'width' => 100, - 'width_type' => '%', - 'parent' => $id, - ); - break; - case 'column': - $title = $location == 'left' ? t('Add column to left') : t('Add column to right'); - $item = array( - 'type' => 'column', - 'width' => 100, - 'width_type' => '%', - 'parent' => $id, - 'children' => array(), - ); - break; - } - // Create the new item with defaults. - break; - case 'region': - // Cannot add items to regions. - break; - } - - $form_state = array( - 'display' => &$handler->display, - 'parent' => &$parent, - 'item' => &$item, - 'id' => $id, - 'settings' => &$settings, - 'ajax' => TRUE, - 'title' => $title, - 'location' => $location, - ); - - $output = ctools_modal_form_wrapper('panels_flexible_add_item_form', $form_state); - if (!empty($form_state['executed'])) { - // If the width type changed then other nearby items will have - // to have their widths adjusted. - panels_edit_cache_set($handler->cache); - $output = array(); - - $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); - - $content = ''; - if ($item['type'] == 'region') { - $handler->plugins['layout']['regions'][$form_state['key']] = $item['title']; - - $content = $handler->render_region($form_state['key'], array()); - - // Manually add the hidden field that our region uses to store pane info. - $content .= ''; - - } - else { - // We need to make sure the left/middle/right divs exist inside this - // so that more stuff can be added inside it as needed. - foreach (array('left', 'middle', 'right') as $position) { - if (!empty($content) || $renderer->admin) { - $content .= '
        '; - } - } - - } - - // render the item - $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'], $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 - // of the nearby item as it probably got adjusted. - // The blocks of code in this else look very similar but are not actually - // duplicated because the order changes based on left or right. - switch ($position) { - case 'left': - if ($location == 'left') { - $item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']); - $output[] = 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[] = 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[] = 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[] = 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[] = 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[] = ajax_command_append('#panels-dnd-main .' . $parent_class . '-middle', $item_output); - } - break; - - } - - // Send our fix height command. - $output[] = array('command' => 'flexible_fix_height'); - - if (!empty($form_state['sibling'])) { - $sibling_width = '#panels-dnd-main .' . $renderer->base[$item['type']] . '-' . $form_state['sibling'] . '-width'; - $output[] = array( - 'command' => 'flexible_set_width', - 'selector' => $sibling_width, - 'width' => $settings['items'][$form_state['sibling']]['width'], - ); - } - foreach ($css as $selector => $data) { - $output[] = ajax_command_css($selector, $data); - } - - // Rerender our parent item links: - $output[] = ajax_command_replace('.flexible-links-' . $id, - panels_flexible_render_item_links($renderer, $id, $parent)); - - $output[] = array( - 'command' => 'flexible_fix_firstlast', - 'selector' => '.' . $parent_class . '-inside', - 'base' => 'panels-flexible-' . $item['type'], - ); - - $output[] = ctools_modal_command_dismiss(); - } - - $handler->commands = $output; -} -/** - * Form to add a row, column or region to a flexible layout. - * @param $form_state - * @return - */ -function panels_flexible_add_item_form($form, &$form_state) { - $display = &$form_state['display']; - $item = &$form_state['item']; - $parent = &$form_state['parent']; - $settings = &$form_state['settings']; - $location = &$form_state['location']; - $id = &$form_state['id']; - - if ($item['type'] == 'region') { - $form['title'] = array( - '#title' => t('Region title'), - '#type' => 'textfield', - '#default_value' => $item['title'], - '#required' => TRUE, - ); - } - - $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 - // fluid. - $restrict = FALSE; - - if (!empty($parent['children'])) { - if ($location == 'left') { - $sibling = reset($parent['children']); - } - else { - $sibling = end($parent['children']); - } - if ($settings['items'][$sibling]['width_type'] == 'px') { - $restrict = TRUE; - $item['width_type'] = 'px'; - } - } - - $form['width_type'] = array( - '#type' => 'select', - '#title' => t('Width'), - '#default_value' => $item['width_type'], - '#options' => array( - '%' => t('Fluid'), - 'px' => t('Fixed'), - ), - '#disabled' => $restrict, - ); - if ($restrict) { - // This forces the value because disabled items don't always send - // their data back. - $form['width_type']['#value'] = $item['width_type']; - $form['width_type']['#description'] = t('Items cannot be set to fluid if there are fixed items already on that side.'); - } - } - else { - $form['contains'] = array( - '#type' => 'select', - '#title' => t('Contains'), - '#default_value' => $item['contains'], - '#options' => array( - 'region' => t('Regions'), - 'column' => t('Columns'), - ), - ); - } - - $form['save'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); - - return $form; -} - -/** - * Submit handler for editing a flexible item. - */ -function panels_flexible_add_item_form_submit(&$form, &$form_state) { - $item = &$form_state['item']; - $parent = &$form_state['parent']; - $location = &$form_state['location']; - $settings = &$form_state['settings']; - - $item['class'] = $form_state['values']['class']; - - if ($item['type'] == 'region') { - $item['title'] = $form_state['values']['title']; - } - - if ($item['type'] != 'row') { - $item['width_type'] = $form_state['values']['width_type']; - } - else { - $item['contains'] = $form_state['values']['contains']; - } - - if ($item['type'] == 'region') { - // derive the region key from the title - $key = preg_replace("/[^a-z0-9]/", '_', drupal_strtolower($item['title'])); - while (isset($settings['items'][$key])) { - $key .= '_'; - } - $form_state['key'] = $key; - } - else { - $form_state['key'] = $key = max(array_keys($settings['items'])) + 1; - } - - $form_state['sibling'] = NULL; - if ($item['type'] != 'row' && !empty($parent['children'])) { - // Figure out what the width should be and adjust our sibling if - // necessary. - if ($location == 'left') { - $form_state['sibling'] = reset($parent['children']); - } - else { - $form_state['sibling'] = end($parent['children']); - - } - - // If there is no sibling, or the sibling is of a different type, - // the default 100 will work for either fixed or fluid. - if ($form_state['sibling'] && $settings['items'][$form_state['sibling']]['width_type'] == $item['width_type']) { - // steal half of the sibling's space. - $width = $settings['items'][$form_state['sibling']]['width'] / 2; - $settings['items'][$form_state['sibling']]['width'] = $width; - $item['width'] = $width; - } - } - - // Place the item. - $settings['items'][$key] = $item; - if ($location == 'left') { - array_unshift($parent['children'], $key); - } - else { - $parent['children'][] = $key; - } -} - -/** - * AJAX responder to remove an existing row, column or region from a flexible - * layout. - */ -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])) { - 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 = '.' . $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. - $offset = array_search($id, $siblings); - - // Only bother with this stuff if our item is fluid, since fixed is - // as fixed does. - if ($item['type'] != 'row') { - if (isset($siblings[$offset + 1])) { - $next = $siblings[$offset + 1]; - } - if (isset($siblings[$offset - 1])) { - $prev = $siblings[$offset - 1]; - } - - if ($item['width_type'] == '%') { - // First, try next. - if (isset($next) && $settings['items'][$next]['width_type'] == '%') { - $settings['items'][$next]['width'] += $item['width']; - } - // If that failed, try the previous one. - else if (isset($prev) && $settings['items'][$prev]['width_type'] == '%') { - $settings['items'][$prev]['width'] += $item['width']; - } - } - // Not sure what happens if they both failed. Maybe nothing. - } - - // Remove the item. - array_splice($siblings, $offset, 1); - - unset($settings['items'][$id]); - - // Save our new state. - panels_edit_cache_set($handler->cache); - $class = $renderer->base[$item['type']] . '-' . $id; - $output = array(); - - $output[] = 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'], $item['parent']); - foreach ($css as $selector => $data) { - $output[] = ajax_command_css($selector, $data); - } - } - - // There are potentially two splitters linked to this item to be removed. - if (!empty($prev)) { - $output[] = 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[] = 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-dnd-main .' . $renderer->base[$item['type']] . '-' . $prev; - $output[] = ajax_command_after($prev_class, $splitter); - // Send our fix height command. - $output[] = array('command' => 'flexible_fix_height'); - } - // Rerender our parent item links: - $output[] = ajax_command_replace('.flexible-links-' . $item['parent'], - panels_flexible_render_item_links($renderer, $item['parent'], $settings['items'][$item['parent']])); - - $output[] = array( - 'command' => 'flexible_fix_firstlast', - 'selector' => $parent_class . '-inside', - 'base' => 'panels-flexible-' . $item['type'], - ); - - $handler->commands = $output; -} - -/** - * AJAX responder to store resize information when the user adjusts the - * splitter. - */ -function panels_ajax_flexible_edit_resize($handler) { - ctools_include('ajax'); - $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']) { - $settings['items'][$_POST['right']]['width'] = $_POST['right_width']; - } - - // Save our new state. - panels_edit_cache_set($handler->cache); - - $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($form_state['executed'])) { - // 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, &$form_state) { - $form['markup'] = array( - '#prefix' => '
        ', - '#suffix' => '
        ', - '#value' => t('If you save this layout for reuse it will appear in the list of reusable layouts at admin/structure/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' => 'machine_name', - '#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!'), - '#machine_name' => array( - 'exists' => 'panels_flexible_edit_name_exists', - 'source' => array('admin_title'), - ), - ); - - $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.')); - } -} - -/** - * Test for #machine_name type to see if an export exists. - */ -function panels_flexible_edit_name_exists($name, $element, &$form_state) { - ctools_include('export'); - $plugin = $form_state['plugin']; - - return (empty($form_state['item']->export_ui_allow_overwrite) && ctools_export_crud_load($plugin['schema'], $name)); -} diff --git a/plugins/layouts/flexible/flexible.png b/plugins/layouts/flexible/flexible.png deleted file mode 100644 index 14b4779..0000000 Binary files a/plugins/layouts/flexible/flexible.png and /dev/null differ diff --git a/plugins/layouts/flexible/grippie-vertical.png b/plugins/layouts/flexible/grippie-vertical.png deleted file mode 100644 index 7d5b7ea..0000000 Binary files a/plugins/layouts/flexible/grippie-vertical.png and /dev/null differ diff --git a/plugins/layouts/onecol/onecol.css b/plugins/layouts/onecol/onecol.css deleted file mode 100644 index 83f284a..0000000 --- a/plugins/layouts/onecol/onecol.css +++ /dev/null @@ -1,22 +0,0 @@ - -.panel-1col { -/* overflow: hidden; */ -} - -.panel-2col .panel-col-first .inside { - margin: 0; -} - - -.panel-1col .panel-col { - width: 100%; -} - -#panels-edit-display .panel-pane, -#panels-edit-display .helperclass { - margin: .5em; -} - -.panel-2col .panel-separator { - margin: 0 0 1em 0; -} diff --git a/plugins/layouts/onecol/onecol.inc b/plugins/layouts/onecol/onecol.inc deleted file mode 100644 index 4043566..0000000 --- a/plugins/layouts/onecol/onecol.inc +++ /dev/null @@ -1,14 +0,0 @@ - t('Single column'), - 'category' => t('Columns: 1'), - 'icon' => 'onecol.png', - 'theme' => 'panels_onecol', - 'css' => 'onecol.css', - 'regions' => array('middle' => t('Middle column')), -); diff --git a/plugins/layouts/onecol/onecol.png b/plugins/layouts/onecol/onecol.png deleted file mode 100644 index 176ed69..0000000 Binary files a/plugins/layouts/onecol/onecol.png and /dev/null differ diff --git a/plugins/layouts/onecol/panels-onecol.html.twig b/plugins/layouts/onecol/panels-onecol.html.twig deleted file mode 100644 index 0906cd7..0000000 --- a/plugins/layouts/onecol/panels-onecol.html.twig +++ /dev/null @@ -1,19 +0,0 @@ -{# -/** - * @file - * Template for a 3 column panel layout. - * - * This template provides a very simple "one column" panel display layout. - * - * Variables: - * - $id: An optional CSS id to use for the layout. - * - $content: An array of content, each item in the array is keyed to one - * panel of the layout. This layout supports the following sections: - * $content['middle']: The only panel in the layout. - */ -#} -
        -
        -
        {{ content.middle }}
        -
        -
        diff --git a/plugins/layouts/threecol_25_50_25/panels-threecol-25-50-25.tpl.php b/plugins/layouts/threecol_25_50_25/panels-threecol-25-50-25.tpl.php deleted file mode 100644 index 588e593..0000000 --- a/plugins/layouts/threecol_25_50_25/panels-threecol-25-50-25.tpl.php +++ /dev/null @@ -1,29 +0,0 @@ - -
        > -
        -
        -
        - -
        -
        -
        - -
        -
        -
        -
        diff --git a/plugins/layouts/threecol_25_50_25/threecol_25_50_25.css b/plugins/layouts/threecol_25_50_25/threecol_25_50_25.css deleted file mode 100644 index 2bffe57..0000000 --- a/plugins/layouts/threecol_25_50_25/threecol_25_50_25.css +++ /dev/null @@ -1,35 +0,0 @@ - -.panel-3col { -/* overflow: hidden; */ -} - -.panel-3col .panel-col-first { - float: left; - width: 25%; -} - -.panel-3col .panel-col-first .inside { - margin: 0 .5em 1em 0; -} - -.panel-3col .panel-col { - float: left; - width: 50%; -} - -.panel-3col .panel-col .inside { - margin: 0 .5em 1em .5em; -} - -.panel-3col .panel-col-last { - float: left; - width: 25%; -} - -.panel-3col .panel-col-last .inside { - margin: 0 0 1em .5em; -} - -.panel-3col .panel-separator { - margin: 0 0 1em 0; -} 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 deleted file mode 100644 index 78ab682..0000000 --- a/plugins/layouts/threecol_25_50_25/threecol_25_50_25.inc +++ /dev/null @@ -1,20 +0,0 @@ - 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'), - 'css' => 'threecol_25_50_25.css', - 'regions' => array( - 'left' => t('Left side'), - 'middle' => t('Middle column'), - 'right' => t('Right side') - ), -); - diff --git a/plugins/layouts/threecol_25_50_25/threecol_25_50_25.png b/plugins/layouts/threecol_25_50_25/threecol_25_50_25.png deleted file mode 100644 index ad6832a..0000000 Binary files a/plugins/layouts/threecol_25_50_25/threecol_25_50_25.png and /dev/null differ 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 deleted file mode 100644 index 48aa523..0000000 --- a/plugins/layouts/threecol_25_50_25_stacked/panels-threecol-25-50-25-stacked.tpl.php +++ /dev/null @@ -1,46 +0,0 @@ - -
        > - -
        -
        -
        - - -
        -
        -
        -
        - -
        -
        -
        - -
        -
        -
        -
        - - -
        -
        -
        - -
        diff --git a/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.css b/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.css deleted file mode 100644 index 1aa00da..0000000 --- a/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.css +++ /dev/null @@ -1,45 +0,0 @@ - -.panel-3col-stacked { -/* overflow: hidden; */ -} - -.panel-3col-stacked .panel-col-top, -.panel-3col-stacked .panel-col-bottom { - width: 100%; - clear: both; -} - -.panel-3col-stacked .panel-col-top .inside { - margin-bottom: .5em; -} - -.panel-3col-stacked .panel-col-first { - float: left; - width: 25%; -} - -.panel-3col-stacked .panel-col .inside { - margin: 0 .5em 1em .5em; -} - -.panel-3col-stacked .panel-col { - float: left; - width: 50%; -} - -.panel-3col-stacked .panel-col .inside { - margin: 0 .5em 1em .5em; -} - -.panel-3col-stacked .panel-col-last { - float: left; - width: 25%; -} - -.panel-3col-stacked .panel-col-last .inside { - margin: 0 0 1em .5em; -} - -.panel-3col-stacked .panel-separator { - margin: 0 0 1em 0; -} 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 deleted file mode 100644 index a5a450d..0000000 --- a/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.inc +++ /dev/null @@ -1,17 +0,0 @@ - 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', - 'regions' => array( - 'top' => t('Top'), - 'left' => t('Left side'), - 'middle' => t('Middle column'), - 'right' => t('Right side'), - 'bottom' => t('Bottom'), - ), -); diff --git a/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.png b/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.png deleted file mode 100644 index 14b4779..0000000 Binary files a/plugins/layouts/threecol_25_50_25_stacked/threecol_25_50_25_stacked.png and /dev/null differ diff --git a/plugins/layouts/threecol_33_34_33/panels-threecol-33-34-33.tpl.php b/plugins/layouts/threecol_33_34_33/panels-threecol-33-34-33.tpl.php deleted file mode 100644 index 73a7437..0000000 --- a/plugins/layouts/threecol_33_34_33/panels-threecol-33-34-33.tpl.php +++ /dev/null @@ -1,31 +0,0 @@ - - -
        > -
        -
        -
        - -
        -
        -
        - -
        -
        -
        -
        diff --git a/plugins/layouts/threecol_33_34_33/threecol_33_34_33.css b/plugins/layouts/threecol_33_34_33/threecol_33_34_33.css deleted file mode 100644 index da2d931..0000000 --- a/plugins/layouts/threecol_33_34_33/threecol_33_34_33.css +++ /dev/null @@ -1,35 +0,0 @@ - -.panel-3col-33 { -/* overflow: hidden; */ -} - -.panel-3col-33 .panel-col-first { - float: left; - width: 33%; -} - -.panel-3col-33 .panel-col-first .inside { - margin: 0 .5em 1em 0; -} - -.panel-3col-33 .panel-col { - float: left; - width: 33%; -} - -.panel-3col-33 .panel-col .inside { - margin: 0 .5em 1em .5em; -} - -.panel-3col-33 .panel-col-last { - float: left; - width: 33%; -} - -.panel-3col-33 .panel-col-last .inside { - margin: 0 0 1em .5em; -} - -.panel-3col-33 .panel-separator { - margin: 0 0 1em 0; -} 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 deleted file mode 100644 index ea4809a..0000000 --- a/plugins/layouts/threecol_33_34_33/threecol_33_34_33.inc +++ /dev/null @@ -1,15 +0,0 @@ - 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', - 'regions' => array( - 'left' => t('Left side'), - 'middle' => t('Middle column'), - 'right' => t('Right side') - ), -); diff --git a/plugins/layouts/threecol_33_34_33/threecol_33_34_33.png b/plugins/layouts/threecol_33_34_33/threecol_33_34_33.png deleted file mode 100644 index 468f8bb..0000000 Binary files a/plugins/layouts/threecol_33_34_33/threecol_33_34_33.png and /dev/null differ 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 deleted file mode 100644 index 276e2bf..0000000 --- a/plugins/layouts/threecol_33_34_33_stacked/panels-threecol-33-34-33-stacked.tpl.php +++ /dev/null @@ -1,46 +0,0 @@ - -
        > - -
        -
        -
        - - -
        -
        -
        -
        - -
        -
        -
        - -
        -
        -
        -
        - - -
        -
        -
        - -
        diff --git a/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.css b/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.css deleted file mode 100644 index 57b87ee..0000000 --- a/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.css +++ /dev/null @@ -1,45 +0,0 @@ - -.panel-3col-33-stacked { -/* overflow: hidden; */ -} - -.panel-3col-33-stacked .panel-col-top, -.panel-3col-33-stacked .panel-col-bottom { - width: 100%; - clear: both; -} - -.panel-3col-33-stacked .panel-col-top .inside { - margin-bottom: 1em; -} - -.panel-3col-33-stacked .panel-col-first { - float: left; - width: 33%; -} - -.panel-3col-33-stacked .panel-col-first .inside { - margin: 0 .5em 1em 0; -} - -.panel-3col-33-stacked .panel-col { - float: left; - width: 33%; -} - -.panel-3col-33-stacked .panel-col .inside { - margin: 0 .5em 1em .5em; -} - -.panel-3col-33-stacked .panel-col-last { - float: left; - width: 33%; -} - -.panel-3col-33-stacked .panel-col-last .inside { - margin: 0 0 1em .5em; -} - -.panel-3col-33-stacked .panel-separator { - margin: 0 0 1em 0; -} diff --git a/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.inc b/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.inc deleted file mode 100644 index 3ac011b..0000000 --- a/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.inc +++ /dev/null @@ -1,17 +0,0 @@ - t('Three column 33/34/33 stacked'), - 'category' => t('Columns: 3'), - 'icon' => 'threecol_33_34_33_stacked.png', - 'theme' => 'panels_threecol_33_34_33_stacked', - 'css' => 'threecol_33_34_33_stacked.css', - 'regions' => array( - 'top' => t('Top'), - 'left' => t('Left side'), - 'middle' => t('Middle column'), - 'right' => t('Right side'), - 'bottom' => t('Bottom') - ), -); diff --git a/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.png b/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.png deleted file mode 100644 index ffd1351..0000000 Binary files a/plugins/layouts/threecol_33_34_33_stacked/threecol_33_34_33_stacked.png and /dev/null differ diff --git a/plugins/layouts/twocol/panels-twocol.html.twig b/plugins/layouts/twocol/panels-twocol.html.twig deleted file mode 100644 index d1796ef..0000000 --- a/plugins/layouts/twocol/panels-twocol.html.twig +++ /dev/null @@ -1,25 +0,0 @@ -{# -/** - * @file - * Template for a 2 column panel layout. - * - * This template provides a two column panel display layout, with - * each column roughly equal in width. - * - * Variables: - * - $id: An optional CSS id to use for the layout. - * - $content: An array of content, each item in the array is keyed to one - * panel of the layout. This layout supports the following sections: - * - $content['left']: Content in the left column. - * - $content['right']: Content in the right column. - */ -#} -
        -
        -
        {{ content.left }}
        -
        - -
        -
        {{ content.right }}
        -
        -
        diff --git a/plugins/layouts/twocol/twocol.css b/plugins/layouts/twocol/twocol.css deleted file mode 100644 index 6e53eca..0000000 --- a/plugins/layouts/twocol/twocol.css +++ /dev/null @@ -1,37 +0,0 @@ - -.panel-2col { -/* overflow: hidden; */ -} - -.panel-2col .panel-col-first { - float: left; - width: 50%; -} -* html .panel-2col .panel-col-first { - width: 49.9%; -} - -.panel-2col .panel-col-first .inside { - margin: 0 .5em 1em 0; -} - -.panel-2col .panel-col-last { - float: left; - width: 50%; -} -* html .panel-2col .panel-col-last { - width: 49.9%; -} - -.panel-2col .panel-col-last .inside { - margin: 0 0 1em .5em; -} - -#panels-edit-display .panel-pane, -#panels-edit-display .helperclass { - margin: .5em; -} - -.panel-2col .panel-separator { - margin: 0 0 1em 0; -} diff --git a/plugins/layouts/twocol/twocol.inc b/plugins/layouts/twocol/twocol.inc deleted file mode 100644 index 1faedca..0000000 --- a/plugins/layouts/twocol/twocol.inc +++ /dev/null @@ -1,14 +0,0 @@ - t('Two column'), - 'category' => t('Columns: 2'), - 'icon' => 'twocol.png', - 'theme' => 'panels_twocol', - 'css' => 'twocol.css', - 'regions' => array( - 'left' => t('Left side'), - 'right' => t('Right side') - ), -); diff --git a/plugins/layouts/twocol/twocol.png b/plugins/layouts/twocol/twocol.png deleted file mode 100644 index 9d2965e..0000000 Binary files a/plugins/layouts/twocol/twocol.png and /dev/null differ diff --git a/plugins/layouts/twocol_bricks/panels-twocol-bricks.tpl.php b/plugins/layouts/twocol_bricks/panels-twocol-bricks.tpl.php deleted file mode 100644 index e626844..0000000 --- a/plugins/layouts/twocol_bricks/panels-twocol-bricks.tpl.php +++ /dev/null @@ -1,53 +0,0 @@ - -
        > -
        -
        -
        -
        -
        -
        -
        - -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        - -
        -
        -
        -
        -
        -
        -
        -
        diff --git a/plugins/layouts/twocol_bricks/twocol_bricks.css b/plugins/layouts/twocol_bricks/twocol_bricks.css deleted file mode 100644 index 4997ce6..0000000 --- a/plugins/layouts/twocol_bricks/twocol_bricks.css +++ /dev/null @@ -1,46 +0,0 @@ - -.panel-2col-bricks { -/* overflow: hidden; */ - margin-top: 0; - padding-top: 0; -} - -.panel-2col-bricks .panel-col-top, -.panel-2col-bricks .panel-col-middle, -.panel-2col-bricks .panel-col-bottom { - width: 99.9%; - clear: both; -} - -.panel-2col-bricks .panel-col-top .inside, -.panel-2col-bricks .panel-col-middle .inside { - margin-bottom: .5em; -} - -.panel-2col-bricks .panel-col-first { - float: left; - width: 50%; -} -* html .panel-2col-bricks .panel-col-first { - width: 49.9%; -} - -.panel-2col-bricks .panel-col-first .inside { - margin: 0 .5em .5em 0; -} - -.panel-2col-bricks .panel-col-last { - float: left; - width: 50%; -} -* html .panel-2col-bricks .panel-col-last { - width: 49.9%; -} - -.panel-2col-bricks .panel-col-last .inside { - margin: 0 0 .5em .5em; -} - -.panel-2col-bricks .panel-separator { - margin: 0 0 1em 0; -} diff --git a/plugins/layouts/twocol_bricks/twocol_bricks.inc b/plugins/layouts/twocol_bricks/twocol_bricks.inc deleted file mode 100644 index 0cb6f1a..0000000 --- a/plugins/layouts/twocol_bricks/twocol_bricks.inc +++ /dev/null @@ -1,25 +0,0 @@ - t('Two column bricks'), - 'category' => t('Columns: 2'), - 'icon' => 'twocol_bricks.png', - 'theme' => 'panels_twocol_bricks', - 'css' => 'twocol_bricks.css', - 'regions' => array( - 'top' => t('Top'), - 'left_above' => t('Left above'), - 'right_above' => t('Right above'), - 'middle' => t('Middle'), - 'left_below' => t('Left below'), - 'right_below' => t('Right below'), - 'bottom' => t('Bottom'), - ), -); - diff --git a/plugins/layouts/twocol_bricks/twocol_bricks.png b/plugins/layouts/twocol_bricks/twocol_bricks.png deleted file mode 100644 index 450395c..0000000 Binary files a/plugins/layouts/twocol_bricks/twocol_bricks.png and /dev/null differ diff --git a/plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php b/plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php deleted file mode 100644 index 901d27c..0000000 --- a/plugins/layouts/twocol_stacked/panels-twocol-stacked.tpl.php +++ /dev/null @@ -1,40 +0,0 @@ - -
        > - -
        -
        -
        - - -
        -
        -
        -
        -
        -
        -
        -
        - - -
        -
        -
        - -
        diff --git a/plugins/layouts/twocol_stacked/twocol_stacked.css b/plugins/layouts/twocol_stacked/twocol_stacked.css deleted file mode 100644 index 56eb3e3..0000000 --- a/plugins/layouts/twocol_stacked/twocol_stacked.css +++ /dev/null @@ -1,41 +0,0 @@ - -.panel-2col-stacked { -/* overflow: hidden; */ - margin-top: 0; - padding-top: 0; -} - -.panel-2col-stacked .panel-col-top, -.panel-2col-stacked .panel-col-bottom { - width: 99.9%; - clear: both; -} - -.panel-2col-stacked .panel-col-top .inside { - margin-bottom: .5em; -} - -.panel-2col-stacked .panel-col-first { - float: left; - width: 50%; -} -* html .panel-2col-stacked .panel-col-first { - width: 49.9%; -} - -.panel-2col-stacked .panel-col-first .inside { - margin: 0 .5em 1em 0; -} - -.panel-2col-stacked .panel-col-last { - float: left; - width: 49.9%; -} - -.panel-2col-stacked .panel-col-last .inside { - margin: 0 0 1em .5em; -} - -.panel-2col-stacked .panel-separator { - margin: 0 0 1em 0; -} diff --git a/plugins/layouts/twocol_stacked/twocol_stacked.inc b/plugins/layouts/twocol_stacked/twocol_stacked.inc deleted file mode 100644 index 5916a15..0000000 --- a/plugins/layouts/twocol_stacked/twocol_stacked.inc +++ /dev/null @@ -1,16 +0,0 @@ - t('Two column stacked'), - 'category' => t('Columns: 2'), - 'icon' => 'twocol_stacked.png', - 'theme' => 'panels_twocol_stacked', - 'css' => 'twocol_stacked.css', - 'regions' => array( - 'top' => t('Top'), - 'left' => t('Left side'), - 'right' => t('Right side'), - 'bottom' => t('Bottom') - ), -); diff --git a/plugins/layouts/twocol_stacked/twocol_stacked.png b/plugins/layouts/twocol_stacked/twocol_stacked.png deleted file mode 100644 index 30ab8b6..0000000 Binary files a/plugins/layouts/twocol_stacked/twocol_stacked.png and /dev/null differ diff --git a/plugins/views/panels.views.inc b/plugins/views/panels.views.inc deleted file mode 100644 index 2418da5..0000000 --- a/plugins/views/panels.views.inc +++ /dev/null @@ -1,27 +0,0 @@ - array( - 'panels_fields' => array( - 'title' => t('Panel fields'), - 'help' => t('Displays the fields in a panel rather than using a template.'), - 'handler' => 'panels_views_plugin_row_fields', - 'path' => drupal_get_path('module', 'panels') . '/plugins/views', - 'theme' => 'views_view_fields', - 'theme path' => drupal_get_path('module', 'views') . '/theme', - 'register theme' => FALSE, - 'uses fields' => TRUE, - 'uses options' => TRUE, - 'type' => 'normal', - 'help topic' => 'style-row-panels-fields', - 'parent' => 'fields', - ), - ), - ); - - return $plugins; -} diff --git a/plugins/views/panels_views_plugin_row_fields.inc b/plugins/views/panels_views_plugin_row_fields.inc deleted file mode 100644 index 4a404ab..0000000 --- a/plugins/views/panels_views_plugin_row_fields.inc +++ /dev/null @@ -1,163 +0,0 @@ - 'twocol'); - $options['regions'] = array('default' => array()); - - return $options; - } - - /** - * Provide a form for setting options. - */ - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - - ctools_include('plugins', 'panels'); - $layouts = panels_get_layouts(); - $options = array(); - foreach ($layouts as $name => $layout) { - if (empty($layout['builder'])) { - $options[$name] = $layout['title']; - } - if ($name == $this->options['layout']) { - $current_layout = $layout; - } - } - - $form['layout'] = array( - '#prefix' => '
        ', - '#type' => 'select', - '#options' => $options, - '#title' => t('Panel layout'), - '#default_value' => $this->options['layout'], - ); - - $form['change'] = array( - '#type' => 'submit', - '#value' => t('Change'), - '#submit' => array('panels_change_layout_button'), - '#suffix' => '
        ', - ); - - if (!empty($current_layout)) { - $fields = $this->display->handler->get_field_labels(); - $regions = panels_get_regions($current_layout, panels_new_display()); - foreach ($fields as $id => $title) { - $form['regions'][$id] = array( - '#type' => 'select', - '#title' => $title, - '#options' => $regions, - ); - if (!empty($this->options['regions'][$id]) && !empty($regions[$this->options['regions'][$id]])) { - $form['regions'][$id]['#default_value'] = $this->options['regions'][$id]; - } - } - } - } - - /** - * Perform any necessary changes to the form values prior to storage. - * There is no need for this function to actually store the data. - */ - function options_submit(&$form, &$form_state) { - $form_state['values']['row_options']['inline'] = array_filter($form_state['values']['row_options']['inline']); - } - - /** - * Render a row object. This usually passes through to a theme template - * of some form, but not always. - */ - function render($row) { - ctools_include('plugins', 'panels'); - $layout = panels_get_layout($this->options['layout']); - if (!$layout) { - // Fall back to normal behavior if the layout is somehow invalid. This - // can happen if the layout was removed, for example. - return theme($this->theme_functions(), array('view' => view, 'options' => $this->options, 'row' => $row, 'field_alias' => $this->field_alias)); - } - - // Store a backup copy of the array because we're going to be screwing - // with this a lot. - $fields = $this->view->field; - unset($this->view->field); - - $meta = 'standard'; - // This row style gets run many times; only run this code once. - if (empty($this->region_fields)) { - $this->region_fields = array(); - $regions = panels_get_regions($layout, panels_new_display()); - - // Ensure each region has an empty array. - foreach ($regions as $region_id => $name) { - if (empty($default_region)) { - $default_region = $region_id; - } - - $this->region_fields[$region_id] = array(); - } - - - // Go through all our fields and place them in regions according to the - // settings. - foreach ($fields as $id => $field) { - $region_id = ''; // ensure we don't accidentlly use the last field's region. - if (!empty($this->options['regions'][$id]) && !empty($regions[$this->options['regions'][$id]])) { - $region_id = $this->options['regions'][$id]; - } - else { - // Fallback to putting unknown fields into the first region. - $region_id = $default_region; - } - - // Ensure this works in PHP4 by keeping the reference. - $this->region_fields[$region_id][$id] = &$fields[$id]; - } - - // We don't need to set 'inline' for every record, so we do it inside - // this loop. We do need to set inline if we are in the live preview - // so that the CSS will get transmitted via javascript: - $meta = !empty($this->view->live_preview) ? 'inline' : 'standard'; - } - - // Now that we have distributed our fields, go through the regions and - // render them into the content array. - foreach ($this->region_fields as $region_id => $fields_list) { - $this->view->field = $fields_list; - $content[$region_id] = theme($this->theme_functions(), array('view' => $this->view, 'options' => $this->options, 'row' => $row)); - } - - // Restore our $fields array. - $this->view->field = $fields; - - // Now that we have a rendered content array, render it. - return panels_print_layout($layout, $content, $meta); - } -} - -/** - * Override handler for views_ui_edit_display_form - */ -function panels_change_layout_button($form, &$form_state) { - $display = &$form_state['view']->display[$form_state['display_id']]; - $display->handler->options_submit($form, $form_state); - - views_ui_cache_set($form_state['view']); - $form_state['rerender'] = TRUE; - $form_state['rebuild'] = TRUE; -} diff --git a/src/Plugin/DisplayBuilder/StandardDisplayBuilder.php b/src/Plugin/DisplayBuilder/StandardDisplayBuilder.php index a831912..1e23671 100644 --- a/src/Plugin/DisplayBuilder/StandardDisplayBuilder.php +++ b/src/Plugin/DisplayBuilder/StandardDisplayBuilder.php @@ -11,6 +11,7 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\Context\ContextHandlerInterface; use Drupal\Core\Plugin\ContextAwarePluginInterface; +use Drupal\Core\Render\Element; use Drupal\Core\Session\AccountInterface; use Drupal\layout_plugin\Plugin\Layout\LayoutInterface; use Drupal\panels\Plugin\DisplayVariant\PanelsDisplayVariant; @@ -107,13 +108,28 @@ class StandardDisplayBuilder extends DisplayBuilderBase implements ContainerFact $block_render_array = [ '#theme' => 'block', '#attributes' => [], + '#contextual_links' => [], '#weight' => $weight++, '#configuration' => $block->getConfiguration(), '#plugin_id' => $block->getPluginId(), '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId(), ]; - $block_render_array['content'] = $block->build(); + + // Build the block and bubble its attributes up if possible. This + // allows modules like Quickedit to function. + // See \Drupal\block\BlockViewBuilder::preRender() for reference. + $content = $block->build(); + if ($content !== NULL && !Element::isEmpty($content)) { + foreach (['#attributes', '#contextual_links'] as $property) { + if (isset($content[$property])) { + $block_render_array[$property] += $content[$property]; + unset($content[$property]); + } + } + } + + $block_render_array['content'] = $content; $build[$region][$block_id] = $block_render_array; } diff --git a/templates/panels-add-content-link.tpl.php b/templates/panels-add-content-link.tpl.php deleted file mode 100644 index b426605..0000000 --- a/templates/panels-add-content-link.tpl.php +++ /dev/null @@ -1,10 +0,0 @@ - -
        - -
        -
        diff --git a/templates/panels-add-content-modal.tpl.php b/templates/panels-add-content-modal.tpl.php deleted file mode 100644 index 5041ff0..0000000 --- a/templates/panels-add-content-modal.tpl.php +++ /dev/null @@ -1,38 +0,0 @@ - -
        -
        -
        -
        - - - -
        - -
        -
        - - - - -
        - -
        - - - -
        - $column): ?> -
        -
        - -
        -
        - -
        - -
        diff --git a/templates/panels-dashboard-block.tpl.php b/templates/panels-dashboard-block.tpl.php deleted file mode 100644 index c2a665e..0000000 --- a/templates/panels-dashboard-block.tpl.php +++ /dev/null @@ -1,13 +0,0 @@ - -
        -

        -
        - - - - -
        -
        diff --git a/templates/panels-dashboard-link.tpl.php b/templates/panels-dashboard-link.tpl.php deleted file mode 100644 index 22f1241..0000000 --- a/templates/panels-dashboard-link.tpl.php +++ /dev/null @@ -1,12 +0,0 @@ - -
        -
        - -
        - -
        -
        -
        diff --git a/templates/panels-dashboard.tpl.php b/templates/panels-dashboard.tpl.php deleted file mode 100644 index 109deab..0000000 --- a/templates/panels-dashboard.tpl.php +++ /dev/null @@ -1,11 +0,0 @@ - -
        -
        - -
        - -
        - -
        -
        diff --git a/templates/panels-pane.tpl.php b/templates/panels-pane.tpl.php deleted file mode 100644 index b3ae51b..0000000 --- a/templates/panels-pane.tpl.php +++ /dev/null @@ -1,58 +0,0 @@ -type: the content type inside this pane - * - $pane->subtype: The subtype, if applicable. If a view it will be the - * view name; if a node it will be the nid, etc. - * - $title: The title of the content - * - $content: The actual content - * - $links: Any links associated with the content - * - $more: An optional 'more' link (destination only) - * - $admin_links: Administrative links associated with the content - * - $feeds: Any feed icons or associated with the content - * - $display: The complete panels display object containing all kinds of - * data including the contexts and all of the other panes being displayed. - */ -?> - - - -
        > - - - - - - - > - - - - -
        - -
        - - -
        - -
        - - - - - - - - -
        - - - diff --git a/tests/src/Unit/panels_ipe/RemoveBlockRequestHandlerTest.php b/tests/src/Unit/panels_ipe/RemoveBlockRequestHandlerTest.php new file mode 100644 index 0000000..9eda8c7 --- /dev/null +++ b/tests/src/Unit/panels_ipe/RemoveBlockRequestHandlerTest.php @@ -0,0 +1,45 @@ +sut = new RemoveBlockRequestHandler($this->moduleHandler, $this->panelsStore, $this->tempStore); + } + + /** + * @test + */ + public function removeBlockRequestRemovesTheBlock() { + $this->panelsDisplay->expects($this->once())->method('removeBlock'); + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest('someblock')); + $this->assertEquals(new JsonResponse([]), $this->sut->getJsonResponse()); + } + + /** + * @test + */ + public function panelsDisplayIsSavedAfterBlockRemoval() { + $this->panelsStore->expects($this->once())->method('save'); + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest('someblock')); + } + + /** + * @test + */ + public function panelsDisplayIsSavedToTempstoreAfterBlockRemoval() { + $this->tempStore->expects($this->once())->method('set'); + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest('someblock'), TRUE); + } + +} diff --git a/tests/src/Unit/panels_ipe/RequestHandlerTestBase.php b/tests/src/Unit/panels_ipe/RequestHandlerTestBase.php new file mode 100644 index 0000000..7923e45 --- /dev/null +++ b/tests/src/Unit/panels_ipe/RequestHandlerTestBase.php @@ -0,0 +1,60 @@ +moduleHandler = $this->getMockForAbstractClass(ModuleHandlerInterface::class); + $this->panelsStore = $this->getMockForAbstractClass(PanelsStorageManagerInterface::class); + $this->tempStore = $this->getMockBuilder(SharedTempstore::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->panelsDisplay = $this->getMockBuilder(PanelsDisplayVariant::class) + ->disableOriginalConstructor() + ->getMock(); + } + + protected function createRequest($content = NULL) { + return new Request([], [], [], [], [], [], $content); + } + + /** + * @test + */ + public function emptyRequestResultsInFailedResponse() { + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest()); + + $expected = new JsonResponse(['success' => FALSE], 400); + $this->assertEquals($expected, $this->sut->getJsonResponse()); + } +} diff --git a/tests/src/Unit/panels_ipe/UpdateLayoutRequestHandlerTest.php b/tests/src/Unit/panels_ipe/UpdateLayoutRequestHandlerTest.php new file mode 100644 index 0000000..a068468 --- /dev/null +++ b/tests/src/Unit/panels_ipe/UpdateLayoutRequestHandlerTest.php @@ -0,0 +1,99 @@ +sut = new UpdateLayoutRequestHandler($this->moduleHandler, $this->panelsStore, $this->tempStore); + } + + private function getLayoutModel() { + return [ + 'regionCollection' => [ + [ + 'name' => 'some_region', + 'blockCollection' => [ + ['uuid' => 'someBlock'], + ['uuid' => 'someOtherBlock'], + ], + ], + ], + ]; + } + + private function setPanelsDisplayExpectations() { + $block = $this->getMockBuilder(BlockBase::class) + ->disableOriginalConstructor() + ->getMock(); + $block->expects($this->exactly(4))->method('setConfigurationValue'); + $block->expects($this->exactly(2)) + ->method('getConfiguration') + ->willReturn([]); + + $this->panelsDisplay->method('getBlock') + ->willReturn($block); + } + + /** + * @test + */ + public function successfulSaveOperationResultsInEmptyJsonResponse() { + $this->setPanelsDisplayExpectations(); + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest(Json::encode($this->getLayoutModel()))); + $this->assertEquals(new JsonResponse([]), $this->sut->getJsonResponse()); + } + + /** + * @test + */ + public function successfulTempStoreSaveOperationResultsInEmptyJsonResponse() { + $this->setPanelsDisplayExpectations(); + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest(Json::encode($this->getLayoutModel())), TRUE); + $this->assertEquals(new JsonResponse([]), $this->sut->getJsonResponse()); + } + + /** + * @test + */ + public function updatedLayoutGetsSaved() { + $this->setPanelsDisplayExpectations(); + $this->panelsStore->expects($this->once())->method('save'); + $this->tempStore->expects($this->once())->method('delete'); + $this->tempStore->expects($this->never())->method('set'); + + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest(Json::encode($this->getLayoutModel()))); + } + + /** + * @test + */ + public function updatedLayoutGetsSavedToTempStore() { + $this->setPanelsDisplayExpectations(); + $this->panelsStore->expects($this->never())->method('save'); + $this->tempStore->expects($this->never())->method('delete'); + $this->tempStore->expects($this->once())->method('set'); + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest(Json::encode($this->getLayoutModel())), TRUE); + } + + /** + * @test + */ + public function hookPreSaveGetsCalledBeforeSave() { + $this->setPanelsDisplayExpectations(); + $this->moduleHandler->expects($this->once())->method('invokeAll'); + $this->sut->handleRequest($this->panelsDisplay, $this->createRequest(Json::encode($this->getLayoutModel())), TRUE); + } + +}