From 2e526eb6f138e21d000d54074542215176404c35 Mon Sep 17 00:00:00 2001 From: Colin Tucker Date: Fri, 22 Sep 2017 10:47:39 +1000 Subject: [PATCH] Updated for new version --- _config/buttons.yml | 17 + _config/controller.yml | 2 - _config/styles.yml | 24 + admin/client/dist/images/icons/Button.png | Bin 0 -> 332 bytes admin/client/dist/js/bundle.js | 2 +- admin/client/dist/styles/bundle.css | 2 +- admin/client/src/entwine/ListViewExtension.js | 4 +- admin/client/src/images/icons/Button.png | Bin 0 -> 332 bytes admin/client/src/styles/_admin.scss | 6 + client/dist/styles/bundle.css | 2 +- client/src/styles/_variables.scss | 6 + client/src/styles/bundle.scss | 10 + .../styles/components/BaseListComponent.scss | 4 +- .../styles/components/FeatureComponent.scss | 9 + .../extensions/DetailFieldsExtension.scss | 19 + src/Components/BaseComponent.php | 2 +- src/Components/BaseListComponent.php | 48 +- src/Components/FeatureComponent.php | 162 ++++-- src/Components/PageComponent.php | 10 - src/Dev/Installer.php | 18 +- src/Extensions/AreaExtension.php | 5 +- src/Extensions/ControllerExtension.php | 12 - src/Extensions/Lists/ListItemExtension.php | 20 + src/Extensions/Lists/ListViewExtension.php | 537 ++++++++++++++---- .../Model/DetailFieldsExtension.php | 211 +++++++ .../Model/ImageDefaultsExtension.php | 2 +- src/Extensions/Model/ImageResizeExtension.php | 2 +- src/Extensions/Model/LinkToExtension.php | 48 +- src/Extensions/PageExtension.php | 36 +- src/Extensions/Style/ButtonStyle.php | 230 ++++++++ src/Extensions/StyleExtension.php | 3 +- src/Forms/ViewportsField.php | 153 ++--- src/Model/Button.php | 124 ++++ src/Model/Component.php | 64 ++- src/Model/Template.php | 26 - src/Tools/ViewTools.php | 26 +- src/View/Renderable.php | 324 ++++++++++- src/View/RequireFiles.php | 283 --------- .../BaseListComponent/Includes/Alerts.ss | 4 +- .../SilverWare/Components/FeatureComponent.ss | 2 + .../FeatureComponent/Includes/Footer.ss | 2 +- .../FeatureComponent/Includes/Header.ss | 4 +- .../Model/DetailFieldsExtension/Details.ss | 13 + templates/SilverWare/Lists/ListItem.ss | 2 +- templates/SilverWare/Model/Button.ss | 1 + 45 files changed, 1847 insertions(+), 634 deletions(-) create mode 100644 _config/buttons.yml create mode 100644 admin/client/dist/images/icons/Button.png create mode 100644 admin/client/src/images/icons/Button.png create mode 100644 client/src/styles/extensions/DetailFieldsExtension.scss create mode 100644 src/Extensions/Model/DetailFieldsExtension.php create mode 100644 src/Extensions/Style/ButtonStyle.php create mode 100644 src/Model/Button.php delete mode 100644 src/View/RequireFiles.php create mode 100644 templates/SilverWare/Extensions/Model/DetailFieldsExtension/Details.ss create mode 100644 templates/SilverWare/Model/Button.ss diff --git a/_config/buttons.yml b/_config/buttons.yml new file mode 100644 index 0000000..365eb6f --- /dev/null +++ b/_config/buttons.yml @@ -0,0 +1,17 @@ +--- +Name: silverware-buttons +--- + +# Define Button Types: + +SilverWare\Extensions\Style\ButtonStyle: + button_types: + primary: Primary + secondary: Secondary + success: Success + info: Info + warning: Warning + danger: Danger + light: Light + dark: Dark + link: Link diff --git a/_config/controller.yml b/_config/controller.yml index 072de33..0a33a10 100644 --- a/_config/controller.yml +++ b/_config/controller.yml @@ -34,7 +34,6 @@ SilverStripe\CMS\Controllers\ContentController: load_themed_js: true load_themed_css: false load_custom_css: true - load_component_requirements: true disable_cache: true --- @@ -51,5 +50,4 @@ SilverStripe\CMS\Controllers\ContentController: load_themed_js: true load_themed_css: true load_custom_css: true - load_component_requirements: false disable_cache: false diff --git a/_config/styles.yml b/_config/styles.yml index 267d63e..a144070 100644 --- a/_config/styles.yml +++ b/_config/styles.yml @@ -32,6 +32,30 @@ SilverWare\Grid\Frameworks\Bootstrap\Framework: white: bg-white light: bg-light dark: bg-dark + button: + button: btn + primary: btn-primary + secondary: btn-secondary + success: btn-success + danger: btn-danger + warning: btn-warning + info: btn-info + light: btn-light + dark: btn-dark + link: btn-link + outline-primary: btn-outline-primary + outline-secondary: btn-outline-secondary + outline-success: btn-outline-success + outline-danger: btn-outline-danger + outline-warning: btn-outline-warning + outline-info: btn-outline-info + outline-light: btn-outline-light + outline-dark: btn-outline-dark + small: btn-sm + large: btn-lg + content: + content: content + typography: typography feature: body: card-body feature: card diff --git a/admin/client/dist/images/icons/Button.png b/admin/client/dist/images/icons/Button.png new file mode 100644 index 0000000000000000000000000000000000000000..f2abe1e9738571a8c939bb14d0c5fd05129a715a GIT binary patch literal 332 zcmV-S0ki&zP)97Db=`yS`+*R`m_=7r7RT{1NfN@^wzWLZ3shA#`o1S~ zf_srcme+Nyyoo~dP7%RJBbAl*T'+n+"")}})}},findTab:function(e){return this.find(this.getTabId(e))},getTabId:function(e){return"a#tab-"+e.replace(".","_")}}),e(".cms-tree li").entwine({updateBadge:function(t){if(this.find("span.status-number-badge").length){var n="#"+this.attr("id"),i=n+".status-number-badge > a span.jstree-pageicon::before",a='content: "'+(t>0?t:"")+'";';e("head").append('")}}}),e("span.status-number-badge-value").entwine({onmatch:function(){if(this._super(),!this.data("updated")){var e=parseInt(this.attr("title"));this.closest("li").updateBadge(e),this.data("updated",!0)}}})})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(0);n.n(i).a.entwine("silverware.listviewextension",function(e){e(".tabset.silverware-extensions-lists-listviewextension").entwine({onmatch:function(){var t=e(this);this.handlePagination(),this.getPaginateItemsField().entwine({onchange:function(e){t.handlePagination(),this._super(e)}}),this._super()},handlePagination:function(){1==this.getPaginateItemsField().val()?this.getPaginationHolder().show():this.getPaginationHolder().hide()},getPaginateItemsField:function(){return e(this).find("#Form_EditForm_ListPaginateItems")},getPaginationHolder:function(){return e(this).find("#Form_EditForm_ListItemsPerPage_Holder")}})})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(0);n.n(i).a.entwine("silverware.togglegroup",function(e){e(".field.togglegroup").entwine({onmatch:function(){var t=e(this);t.doToggle(),t.getToggleInput().entwine({onclick:function(e){t.doToggle(),this._super(e)}}),this._super()},doToggle:function(){var t=e(this.getToggleInput()),n=this.getToggleMode();this.getFields().toggle(n?t.is(":checked"):!t.is(":checked"))},getToggle:function(){return e(this).find(".group-toggle")},getFields:function(){return e(this).find(".group-fields")},getToggleInput:function(){return this.getToggle().find("input")},getToggleMode:function(){return this.getToggle().data("show-when-checked")}})})}]); \ No newline at end of file +!function(e){function t(i){if(n[i])return n[i].exports;var a=n[i]={i:i,l:!1,exports:{}};return e[i].call(a.exports,a,a.exports,t),a.l=!0,a.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/silverware/admin/client/dist/",t(t.s=1)}([function(e,t){e.exports=jQuery},function(e,t,n){n(2),n(3),n(4),n(5)},function(e,t){},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(0);n.n(i).a.entwine("silverware.numberbadges",function(e){e("div.ss-tabset").entwine({onmatch:function(){this._super();var t=this;if(this.attr("data-number-badges")){var n=e.parseJSON(this.attr("data-number-badges"));e.each(n,function(e,n){if(n){var i=t.findTab(e);i.length&&i.append(''+n+"")}})}},findTab:function(e){return this.find(this.getTabId(e))},getTabId:function(e){return"a#tab-"+e.replace(".","_")}}),e(".cms-tree li").entwine({updateBadge:function(t){if(this.find("span.status-number-badge").length){var n="#"+this.attr("id"),i=n+".status-number-badge > a span.jstree-pageicon::before",a='content: "'+(t>0?t:"")+'";';e("head").append('")}}}),e("span.status-number-badge-value").entwine({onmatch:function(){if(this._super(),!this.data("updated")){var e=parseInt(this.attr("title"));this.closest("li").updateBadge(e),this.data("updated",!0)}}})})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(0);n.n(i).a.entwine("silverware.listviewextension",function(e){e(".tabset.silverware-extensions-lists-listviewextension").entwine({onmatch:function(){var t=e(this);this.handlePagination(),this.getPaginateItemsField().entwine({onchange:function(e){t.handlePagination(),this._super(e)}}),this._super()},handlePagination:function(){1==this.getPaginateItemsField().val()?this.getPaginationHolder().show():this.getPaginationHolder().hide()},getPaginateItemsField:function(){return e(this).find("#Form_EditForm_List_PaginateItems")},getPaginationHolder:function(){return e(this).find("#Form_EditForm_List_ItemsPerPage_Holder")}})})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=n(0);n.n(i).a.entwine("silverware.togglegroup",function(e){e(".field.togglegroup").entwine({onmatch:function(){var t=e(this);t.doToggle(),t.getToggleInput().entwine({onclick:function(e){t.doToggle(),this._super(e)}}),this._super()},doToggle:function(){var t=e(this.getToggleInput()),n=this.getToggleMode();this.getFields().toggle(n?t.is(":checked"):!t.is(":checked"))},getToggle:function(){return e(this).find(".group-toggle")},getFields:function(){return e(this).find(".group-fields")},getToggleInput:function(){return this.getToggle().find("input")},getToggleMode:function(){return this.getToggle().data("show-when-checked")}})})}]); \ No newline at end of file diff --git a/admin/client/dist/styles/bundle.css b/admin/client/dist/styles/bundle.css index fd80536..15c5068 100644 --- a/admin/client/dist/styles/bundle.css +++ b/admin/client/dist/styles/bundle.css @@ -1 +1 @@ -.cms-tree.jstree li.hidden{display:none}.cms-tree .is-cached>a span.jstree-pageicon:after{top:0;left:0;width:16px;height:16px;content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAA/0lEQVQ4T2P4//8/RRirICkYqyApGKsgKRirICkYqyApGKsgKRirIDZ8ehMjz5nNjGuQ8a7FzHuxKsaGT6xnjHpwyvLc/3dt/0EYqHkfEDtjVYyOD61k5Dm6hmXltyfZX3+/qvi/eirT4R0LmZ1Bclg1oOO9Sxljrx3Uufjtaer/W8dMbu1bxrgFhrFqQMfrZjAdXtjNeHxWC+PpTw+D/4PwjgWM23cuZHTAqgEbXj+dUefAKsGT7++5/d80i3H35tmMTiBxrIqx4RUTGWL2LBe+uHISw8FVkxlcYOIYCnHhBZ0Mk4D42MIuBldkcRRF+PDMZoZTs1oY3FHF/zMAAFD514LNcp7PAAAAAElFTkSuQmCC);position:absolute}.cms-tree .is-disabled>a span.jstree-pageicon:after{top:0;left:0;width:16px;height:16px;content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAA3ElEQVQ4T6XPoQvCQBTH8YHLc7ewP2RlfVFY3f6DgcH/wqg2o0kQFCZaLTOY5HCr1qF1hgtL4/kOTMevHIYPj325x90cIvoLjDZgtAGjDRhtwGgDRhsw2oDRBozI3nEmbMfkb050h4dNWz58FqJ557n6FMXwyjJ18v1Gd7hg2vCNzzRV9yShKo7pEkV0i2OlO1wwrfnZPElKSXVdU9u21HUd6Q4XTAu+6RCG/TEIqBSCSp678bjXHS6Y5vyvK9dtrr6vHkIMleep5WjU6A4XkBkfnvKNTOqpv4nI+QKF/8YumbPwcAAAAABJRU5ErkJggg==);position:absolute}.cms-tree .heading-h1>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNCREIzRDE2RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNCREIzRDE1RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+MOUuyAAAALdJREFUeNpi/P//PwMlgImBQjDwBrCATWFi0gZSjUji9VAaXewqlC0AxB/+/fvHwAAKREZGRhB2AuL1UBqXmCIQNwHxGhAfpBfZC2+AmBVK4xKTgdrOguIFKGCEKu7G4lVGKH0YiDmAuBCXAWxA3AnEL6BiEkBcjmQALODZsBkAk3gFxBegYkZQMSZcBiBLgPzHDqWxikFTLTPUGxAAFbQC4odI2AqHWCoQ34PiC+AYHM0LDAABBgBVkDNh2LuR6gAAAABJRU5ErkJggg==)}.cms-tree .heading-h2>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNCREIzRDFBRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNCREIzRDE5RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+52Ri6wAAAM9JREFUeNpi/P//PwMlgHEYGAAE2kC6EUmsHkqji12FssWA+BMQ/4CZAMJOQLweSuMSUwPi+UC8BohXAbEPSJwJauobIGaF0gw4xJ4C8QogjgfiI0AcCRJkgXkFqrgbmzeh9Fcg3gllPwJiJ3QD2IC4E4hfQMUkgLgcyQAY4AHiUCC+jmwAE9SAV0B8ASpmBBVjQtLMC8QdQMwNxFMYkCRlgJgdSjPgEBMA4nlArAF1KSj+WUDOswLi5UgaI6E0utgvIF6F5h1/xtG8wAAQYAC/dzmnYX+qBAAAAABJRU5ErkJggg==)}.cms-tree .heading-h3>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMDYyNzY3RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMDYyNzY2RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+PvPC7QAAAORJREFUeNrE078LAXEYx/E7vxcyGow2u8EiSonsEn+HskgWUkZ/Ainlv8BgsNgsTJJB2ZTO+6nn6pucksFTr576dJ67577HdhzH+qV81o/1/wE2laZ3jayj/TXbIYIYrri7E0QBC+1eWR4zzDFFRnJ3hQuC2i2PTJ6gjyb2KEsYcFfRi4fv1tR+VlIP+F8HhDDASbMEWsYAqSIqSKFnnoJPB8gdtuqsmXlSNxw1K5kDkghrtz5ka4wwRgFxCbM4GLIemXlqNawQ/eabkXeS40dtbFCXYd9UA0tMUHXXt//+b3wKMAA/FDE+b522vgAAAABJRU5ErkJggg==)}.cms-tree .heading-h4>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMDYyNzZCRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMDYyNzZBRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+mDLyXgAAANNJREFUeNpi/P//PwMlgHEYGAAE2kC6EUmsHkqji13FZQIIOwHxeiiNT0wMiLOBWBomxgQ15w0Qs0JpBjxiZkDsBMTfYAIsMIdAFXdjcyQS2wSIHwDxe2wGsAFxJxC/gIpJAHE5kgEgeV0g3gjEhkAcDsQVMAOYoApeAfEFqJgRVAzmTU0g5gPic0AsDcQyDEiSIA47TBCHmDkQ/4GGiTxUjh/kPCsgXo6kMRJKL0eKqUho4Hmhhc9RYhMLOk4H4i0gNgs5qQ+Yetmg4TEI8gJAgAEA5KsxwoBFcu4AAAAASUVORK5CYII=)}.cms-tree .heading-h5>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMDYyNzZGRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMDYyNzZFRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x3zyIwAAAOZJREFUeNpi/P//PwMlgImBQjDwBjACgTaQbkQSq4fS6GJXcZkAwk5AvB5K4xKzA+I1SHgKSJwFas4bIGaF0gw4xEBqfwBxEZT/GyYIdghUcTc2R0JpfqgBII1/gfgTugFsQNwJxC+gYhJAXI5kADcQSwPxPCDmAOLVIDYLUmyADHgFxBegYkZQMVhMHQdiASBeAMQ+QJwIxMthkjJAzA6lGXCI3QXiKUD8BYhvQl0BMpDBCogfImErHGLIsZYOxAeBmJmUNCMHxPZATbVAfAaI/UGGkQJKgfgYNBDd4OllwHMjQIABAEoVMXBASUGAAAAAAElFTkSuQmCC)}.cms-tree .heading-h6>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMzVENjcwRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMzVENjZGRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/BEAGQAAAOhJREFUeNpi/P//PwMlgHHgDQACUvWIAbEoEL8A4rcgA7SBjEYkBfVQGl3sKhBHAnEwEH+C8ntBTgBhJyBeD6VxiclD+QFIahiYoDa8AWJWKM2AQ8wYiP9CcSgQ84MEWWBhAVXcjS2coLQkEPMCsREQawCxDRDnIxvABsSd0MABAQkgLkcygAeIvwBxCxA7AXEVSAxmABPUgFdAfAEqZgQVg3nzNxB/gHrhLVSOFyYpA8TsUJoBh9g9INYEphtxIG0PxP+A+DlIwgqIHyJhKxxiIMM2AfEtKA4nN/FJATHHIMoLlBoAEGAA/nw/0pIQ3W0AAAAASUVORK5CYII=)}.field.form-group.hidden{display:none}.cms-tree .status-number-badge>a span.jstree-pageicon:before{top:6px;right:-3px;color:#fff;padding:0 2px;font-size:9px;min-width:13px;min-height:13px;font-weight:700;line-height:13px;position:absolute;text-align:center;text-shadow:none;border-radius:7px;background-color:#d40404}ul.nav-tabs span.number-badge{color:#fff;padding:0 4px;font-size:11px;min-width:16px;min-height:16px;margin-left:4px;line-height:16px;border-radius:8px;text-align:center;font-weight:700;display:inline-block;background-color:#d40404}.message.status{margin-bottom:1.8462rem}.field.dimensions .by{display:inline-block;line-height:30px;margin-right:.6154rem}.field.dimensions .by:last-child{display:none}.field.dimensions .by .icon:before{content:"D";font-size:16px;font-style:normal;font-weight:400;font-family:silverstripe}.field.viewports .dropdown,.field.viewports .text{min-width:140px;max-width:140px} \ No newline at end of file +.cms-tree.jstree li.hidden{display:none}.cms-tree .is-cached>a span.jstree-pageicon:after{top:0;left:0;width:16px;height:16px;content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAA/0lEQVQ4T2P4//8/RRirICkYqyApGKsgKRirICkYqyApGKsgKRirIDZ8ehMjz5nNjGuQ8a7FzHuxKsaGT6xnjHpwyvLc/3dt/0EYqHkfEDtjVYyOD61k5Dm6hmXltyfZX3+/qvi/eirT4R0LmZ1Bclg1oOO9Sxljrx3Uufjtaer/W8dMbu1bxrgFhrFqQMfrZjAdXtjNeHxWC+PpTw+D/4PwjgWM23cuZHTAqgEbXj+dUefAKsGT7++5/d80i3H35tmMTiBxrIqx4RUTGWL2LBe+uHISw8FVkxlcYOIYCnHhBZ0Mk4D42MIuBldkcRRF+PDMZoZTs1oY3FHF/zMAAFD514LNcp7PAAAAAElFTkSuQmCC);position:absolute}.cms-tree .is-disabled>a span.jstree-pageicon:after{top:0;left:0;width:16px;height:16px;content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAA3ElEQVQ4T6XPoQvCQBTH8YHLc7ewP2RlfVFY3f6DgcH/wqg2o0kQFCZaLTOY5HCr1qF1hgtL4/kOTMevHIYPj325x90cIvoLjDZgtAGjDRhtwGgDRhsw2oDRBozI3nEmbMfkb050h4dNWz58FqJ557n6FMXwyjJ18v1Gd7hg2vCNzzRV9yShKo7pEkV0i2OlO1wwrfnZPElKSXVdU9u21HUd6Q4XTAu+6RCG/TEIqBSCSp678bjXHS6Y5vyvK9dtrr6vHkIMleep5WjU6A4XkBkfnvKNTOqpv4nI+QKF/8YumbPwcAAAAABJRU5ErkJggg==);position:absolute}.cms-tree .heading-h1>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNCREIzRDE2RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNCREIzRDE1RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+MOUuyAAAALdJREFUeNpi/P//PwMlgImBQjDwBrCATWFi0gZSjUji9VAaXewqlC0AxB/+/fvHwAAKREZGRhB2AuL1UBqXmCIQNwHxGhAfpBfZC2+AmBVK4xKTgdrOguIFKGCEKu7G4lVGKH0YiDmAuBCXAWxA3AnEL6BiEkBcjmQALODZsBkAk3gFxBegYkZQMSZcBiBLgPzHDqWxikFTLTPUGxAAFbQC4odI2AqHWCoQ34PiC+AYHM0LDAABBgBVkDNh2LuR6gAAAABJRU5ErkJggg==)}.cms-tree .heading-h2>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNCREIzRDFBRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNCREIzRDE5RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+52Ri6wAAAM9JREFUeNpi/P//PwMlgHEYGAAE2kC6EUmsHkqji12FssWA+BMQ/4CZAMJOQLweSuMSUwPi+UC8BohXAbEPSJwJauobIGaF0gw4xJ4C8QogjgfiI0AcCRJkgXkFqrgbmzeh9Fcg3gllPwJiJ3QD2IC4E4hfQMUkgLgcyQAY4AHiUCC+jmwAE9SAV0B8ASpmBBVjQtLMC8QdQMwNxFMYkCRlgJgdSjPgEBMA4nlArAF1KSj+WUDOswLi5UgaI6E0utgvIF6F5h1/xtG8wAAQYAC/dzmnYX+qBAAAAABJRU5ErkJggg==)}.cms-tree .heading-h3>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMDYyNzY3RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMDYyNzY2RTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+PvPC7QAAAORJREFUeNrE078LAXEYx/E7vxcyGow2u8EiSonsEn+HskgWUkZ/Ainlv8BgsNgsTJJB2ZTO+6nn6pucksFTr576dJ67577HdhzH+qV81o/1/wE2laZ3jayj/TXbIYIYrri7E0QBC+1eWR4zzDFFRnJ3hQuC2i2PTJ6gjyb2KEsYcFfRi4fv1tR+VlIP+F8HhDDASbMEWsYAqSIqSKFnnoJPB8gdtuqsmXlSNxw1K5kDkghrtz5ka4wwRgFxCbM4GLIemXlqNawQ/eabkXeS40dtbFCXYd9UA0tMUHXXt//+b3wKMAA/FDE+b522vgAAAABJRU5ErkJggg==)}.cms-tree .heading-h4>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMDYyNzZCRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMDYyNzZBRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+mDLyXgAAANNJREFUeNpi/P//PwMlgHEYGAAE2kC6EUmsHkqji13FZQIIOwHxeiiNT0wMiLOBWBomxgQ15w0Qs0JpBjxiZkDsBMTfYAIsMIdAFXdjcyQS2wSIHwDxe2wGsAFxJxC/gIpJAHE5kgEgeV0g3gjEhkAcDsQVMAOYoApeAfEFqJgRVAzmTU0g5gPic0AsDcQyDEiSIA47TBCHmDkQ/4GGiTxUjh/kPCsgXo6kMRJKL0eKqUho4Hmhhc9RYhMLOk4H4i0gNgs5qQ+Yetmg4TEI8gJAgAEA5KsxwoBFcu4AAAAASUVORK5CYII=)}.cms-tree .heading-h5>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMDYyNzZGRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMDYyNzZFRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+x3zyIwAAAOZJREFUeNpi/P//PwMlgImBQjDwBjACgTaQbkQSq4fS6GJXcZkAwk5AvB5K4xKzA+I1SHgKSJwFas4bIGaF0gw4xEBqfwBxEZT/GyYIdghUcTc2R0JpfqgBII1/gfgTugFsQNwJxC+gYhJAXI5kADcQSwPxPCDmAOLVIDYLUmyADHgFxBegYkZQMVhMHQdiASBeAMQ+QJwIxMthkjJAzA6lGXCI3QXiKUD8BYhvQl0BMpDBCogfImErHGLIsZYOxAeBmJmUNCMHxPZATbVAfAaI/UGGkQJKgfgYNBDd4OllwHMjQIABAEoVMXBASUGAAAAAAElFTkSuQmCC)}.cms-tree .heading-h6>a span.jstree-pageicon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2lpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wUmlnaHRzPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvcmlnaHRzLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcFJpZ2h0czpNYXJrZWQ9IkZhbHNlIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNDMzVENjcwRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNDMzVENjZGRTM1NjExRTA4OENEQUVCOTM3NUM0NUQ5IiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ1dWlkOkFDMUYyRTgzMzI0QURGMTFBQUI4QzUzOTBEODVCNUIzIiBzdFJlZjpkb2N1bWVudElEPSJ1dWlkOkM5RDM0OTY2NEEzQ0REMTFCMDhBQkJCQ0ZGMTcyMTU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/BEAGQAAAOhJREFUeNpi/P//PwMlgHHgDQACUvWIAbEoEL8A4rcgA7SBjEYkBfVQGl3sKhBHAnEwEH+C8ntBTgBhJyBeD6VxiclD+QFIahiYoDa8AWJWKM2AQ8wYiP9CcSgQ84MEWWBhAVXcjS2coLQkEPMCsREQawCxDRDnIxvABsSd0MABAQkgLkcygAeIvwBxCxA7AXEVSAxmABPUgFdAfAEqZgQVg3nzNxB/gHrhLVSOFyYpA8TsUJoBh9g9INYEphtxIG0PxP+A+DlIwgqIHyJhKxxiIMM2AfEtKA4nN/FJATHHIMoLlBoAEGAA/nw/0pIQ3W0AAAAASUVORK5CYII=)}.field.fieldgroup>.hidden,.field.form-group.hidden{display:none}.cms-tree .status-number-badge>a span.jstree-pageicon:before{top:6px;right:-3px;color:#fff;padding:0 2px;font-size:9px;min-width:13px;min-height:13px;font-weight:700;line-height:13px;position:absolute;text-align:center;text-shadow:none;border-radius:7px;background-color:#d40404}ul.nav-tabs span.number-badge{color:#fff;padding:0 4px;font-size:11px;min-width:16px;min-height:16px;margin-left:4px;line-height:16px;border-radius:8px;text-align:center;font-weight:700;display:inline-block;background-color:#d40404}.message.status{margin-bottom:1.8462rem}.field.dimensions .by{display:inline-block;line-height:30px;margin-right:.6154rem}.field.dimensions .by:last-child{display:none}.field.dimensions .by .icon:before{content:"D";font-size:16px;font-style:normal;font-weight:400;font-family:silverstripe}.field.viewports .dropdown,.field.viewports .text{min-width:140px;max-width:140px} \ No newline at end of file diff --git a/admin/client/src/entwine/ListViewExtension.js b/admin/client/src/entwine/ListViewExtension.js index 593b4f2..bac0383 100644 --- a/admin/client/src/entwine/ListViewExtension.js +++ b/admin/client/src/entwine/ListViewExtension.js @@ -36,11 +36,11 @@ $.entwine('silverware.listviewextension', function($) { }, getPaginateItemsField: function() { - return $(this).find('#Form_EditForm_ListPaginateItems'); + return $(this).find('#Form_EditForm_List_PaginateItems'); }, getPaginationHolder: function() { - return $(this).find('#Form_EditForm_ListItemsPerPage_Holder'); + return $(this).find('#Form_EditForm_List_ItemsPerPage_Holder'); } }); diff --git a/admin/client/src/images/icons/Button.png b/admin/client/src/images/icons/Button.png new file mode 100644 index 0000000000000000000000000000000000000000..f2abe1e9738571a8c939bb14d0c5fd05129a715a GIT binary patch literal 332 zcmV-S0ki&zP)97Db=`yS`+*R`m_=7r7RT{1NfN@^wzWLZ3shA#`o1S~ zf_srcme+Nyyoo~dP7%RJBbAl*T .hidden { + display: none; + } + } + } // Badge Styles: diff --git a/client/dist/styles/bundle.css b/client/dist/styles/bundle.css index 36e2ce0..b56d823 100644 --- a/client/dist/styles/bundle.css +++ b/client/dist/styles/bundle.css @@ -1 +1 @@ -.baselistcomponent .items>article.item{margin-bottom:2rem}.baselistcomponent .items>article.item>div.image{position:relative;margin-bottom:1rem}.baselistcomponent .items>article.item>div.image a{display:block}.baselistcomponent .items>article.item>div.image div.image-overlay{top:0;left:0;right:0;bottom:0;width:100%;height:100%;opacity:0;position:absolute;transition:.5s ease;background-color:rgba(0,0,0,.5)}.baselistcomponent .items>article.item>div.image div.image-overlay>i{top:50%;left:50%;color:#fff;position:absolute;font-size:32px;transform:translate(-50%,-50%)}.baselistcomponent .items>article.item>div.image:hover div.image-overlay,.baselistcomponent .items>article.item>div.image a:focus div.image-overlay{opacity:1}.baselistcomponent .items>article.item>section.content header a{color:inherit}.baselistcomponent .items>article.item>section.content div.details{color:#868e96}.baselistcomponent .items>article.item>section.content div.details>span{margin-right:.5rem}.baselistcomponent .items>article.item>section.content div.details a{color:inherit}.baselistcomponent .items>article.item>section.content footer{margin-top:1rem}.baselistcomponent .items>article.item>section.content>div,.featurecomponent article.feature div.icon{margin-bottom:.5rem}.featurecomponent article.feature div.summary>p:last-child{margin-bottom:0}.featurecomponent article.feature footer{margin-top:1rem}.imagecomponent figure{width:100%;margin-bottom:0}.imagecomponent figure>figcaption>p:last-child{margin-bottom:0}.imagecomponent a.image{display:block}@media (min-width:768px){.listcomponent .items>article.item{display:block}.listcomponent .items.image-align-left>article.item,.listcomponent .items.image-align-right>article.item,.listcomponent .items.image-align-stagger>article.item{display:flex}.listcomponent .items.image-align-left>article.item>div.image,.listcomponent .items.image-align-right>article.item>div.image,.listcomponent .items.image-align-stagger>article.item>div.image{margin-bottom:0}.listcomponent .items.image-align-left>article.item.has-image>div.image,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(odd)>div.image{order:1}.listcomponent .items.image-align-left>article.item.has-image>section.content,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(odd)>section.content{flex:1;order:2;margin-left:1rem}.listcomponent .items.image-align-right>article.item.has-image>div.image,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(2n)>div.image,.listcomponent .items>div.image{order:2}.listcomponent .items.image-align-right>article.item.has-image>section.content,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(2n)>section.content,.listcomponent .items>section.content{flex:1;order:1;margin-right:1rem}}.mediacomponent figure{width:100%;margin-bottom:0}.mediacomponent figure>figcaption>p:last-child{margin-bottom:0}.mediacomponent a.text{display:block}.mediacomponent a.text:hover{text-decoration:none}.mediacomponent a.text:hover span{text-decoration:underline}.mediacomponent a.text>i.fa{color:#868e96}.mediacomponent a.image{display:block}.mediacomponent a.image+a.text{margin-top:.5rem}.mediacomponent .rich>iframe{margin:0!important}.mediacomponent .video{width:100%;height:0;display:block;position:relative}.mediacomponent .video.four-three{padding-bottom:75%}.mediacomponent .video.sixteen-nine{padding-bottom:56.25%}.mediacomponent .video>iframe{top:0;left:0;border:0;width:100%;height:100%;position:absolute}.pagecomponent.page-title-hidden .content-container>article>header{display:none}.pagecomponent .content-container>article>div{margin-bottom:1rem}.scrolltotopbutton{opacity:0;outline:0;right:1rem;bottom:1rem;width:4rem;height:4rem;z-index:1000;position:fixed;display:block;overflow:hidden;visibility:hidden;white-space:nowrap;text-align:center;font-size:16px;line-height:4rem;box-shadow:0 0 20px rgba(0,0,0,.2);transition:opacity .3s 0s,visibility 0s .3s,color .15s ease-in-out 0s,background-color .15s ease-in-out 0s}.scrolltotopbutton.is-visible{opacity:1;visibility:visible}.scrolltotopbutton.fade-out{opacity:.5}.scrolltotopbutton:hover{opacity:1}@media (min-width:768px){.scrolltotopbutton{width:5rem;height:5rem;right:2rem;bottom:2rem;line-height:5rem}}@media (min-width:992px){.scrolltotopbutton{width:6rem;height:6rem;font-size:20px;line-height:6rem}}.tilecomponent .items{display:flex;flex-flow:row wrap;align-items:stretch}.tilecomponent .items>article.item{display:flex;flex-direction:column;flex:1 100%;padding:2%}.tilecomponent .items>article.item>section.content{margin-top:auto}.tilecomponent .items>article.item>section.content header>*{font-size:1rem;line-height:1.2}.tilecomponent .items>article.item>section.content div.details>span{display:block;margin-right:0;margin-bottom:.5rem}@media (min-width:576px){.tilecomponent .items>article.item{flex:1 50%}}@media (min-width:768px){.tilecomponent .items>article.item{flex:1 33.3%}}@media (min-width:992px){.tilecomponent .items>article.item{flex:1 25%}}.component.link{text-decoration:none}.component.link,.component.link>i.fa{display:inline-block}.component.link:active,.component.link:focus,.component.link:hover{text-decoration:none}.show-icons.hide-text .component.link{overflow:hidden;text-align:center;transition:color .15s ease-in-out 0s,background-color .15s ease-in-out 0s}.show-icons.hide-text .component.link>i.fa{display:block}.show-icons.hide-text .component.link.size-16{width:16px;height:16px;font-size:8px;line-height:16px}.show-icons.hide-text .component.link.size-16>i.fa{height:16px;line-height:16px}.show-icons.hide-text .component.link.size-24{width:24px;height:24px;font-size:12px;line-height:24px}.show-icons.hide-text .component.link.size-24>i.fa{height:24px;line-height:24px}.show-icons.hide-text .component.link.size-32{width:32px;height:32px;font-size:16px;line-height:32px}.show-icons.hide-text .component.link.size-32>i.fa{height:32px;line-height:32px}.show-icons.hide-text .component.link.size-48{width:48px;height:48px;font-size:24px;line-height:48px}.show-icons.hide-text .component.link.size-48>i.fa{height:48px;line-height:48px}.show-icons.hide-text .component.link.size-64{width:64px;height:64px;font-size:32px;line-height:64px}.show-icons.hide-text .component.link.size-64>i.fa{height:64px;line-height:64px}.show-icons.hide-text .component.link.size-96{width:96px;height:96px;font-size:48px;line-height:96px}.show-icons.hide-text .component.link.size-96>i.fa{height:96px;line-height:96px}.show-icons.hide-text .component.link.size-128{width:128px;height:128px;font-size:64px;line-height:128px}.show-icons.hide-text .component.link.size-128>i.fa{height:128px;line-height:128px}.hide-icons .component.link>i.fa,.hide-text .component.link>span.text{display:none} \ No newline at end of file +.baselistcomponent .items>article.item{margin-bottom:2rem}.baselistcomponent .items>article.item>div.image{position:relative;margin-bottom:1rem}.baselistcomponent .items>article.item>div.image a{display:block}.baselistcomponent .items>article.item>div.image div.image-overlay{top:0;left:0;right:0;bottom:0;width:100%;height:100%;opacity:0;position:absolute;transition:.5s ease;background-color:rgba(33,37,41,.5)}.baselistcomponent .items>article.item>div.image div.image-overlay>i{top:50%;left:50%;color:#fff;position:absolute;font-size:32px;transform:translate(-50%,-50%)}.baselistcomponent .items>article.item>div.image:hover div.image-overlay,.baselistcomponent .items>article.item>div.image a:focus div.image-overlay{opacity:1}.baselistcomponent .items>article.item>section.content header a{color:inherit}.baselistcomponent .items>article.item>section.content div.details{color:#868e96}.baselistcomponent .items>article.item>section.content div.details>span{margin-right:.5rem}.baselistcomponent .items>article.item>section.content div.details a{color:inherit}.baselistcomponent .items>article.item>section.content footer{margin-top:1rem}.baselistcomponent .items>article.item>section.content>div{margin-bottom:.5rem}.featurecomponent article.feature>a.feature{display:block;text-decoration:none;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.featurecomponent article.feature>a.feature:focus,.featurecomponent article.feature>a.feature:hover{text-decoration:none}.featurecomponent article.feature div.icon{margin-bottom:.5rem}.featurecomponent article.feature div.summary>p:last-child{margin-bottom:0}.featurecomponent article.feature footer{margin-top:1rem}.imagecomponent figure{width:100%;margin-bottom:0}.imagecomponent figure>figcaption>p:last-child{margin-bottom:0}.imagecomponent a.image{display:block}@media (min-width:768px){.listcomponent .items>article.item{display:block}.listcomponent .items.image-align-left>article.item,.listcomponent .items.image-align-right>article.item,.listcomponent .items.image-align-stagger>article.item{display:flex}.listcomponent .items.image-align-left>article.item>div.image,.listcomponent .items.image-align-right>article.item>div.image,.listcomponent .items.image-align-stagger>article.item>div.image{margin-bottom:0}.listcomponent .items.image-align-left>article.item.has-image>div.image,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(odd)>div.image{order:1}.listcomponent .items.image-align-left>article.item.has-image>section.content,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(odd)>section.content{flex:1;order:2;margin-left:1rem}.listcomponent .items.image-align-right>article.item.has-image>div.image,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(2n)>div.image,.listcomponent .items>div.image{order:2}.listcomponent .items.image-align-right>article.item.has-image>section.content,.listcomponent .items.image-align-stagger>article.item.has-image:nth-child(2n)>section.content,.listcomponent .items>section.content{flex:1;order:1;margin-right:1rem}}.mediacomponent figure{width:100%;margin-bottom:0}.mediacomponent figure>figcaption>p:last-child{margin-bottom:0}.mediacomponent a.text{display:block}.mediacomponent a.text:hover{text-decoration:none}.mediacomponent a.text:hover span{text-decoration:underline}.mediacomponent a.text>i.fa{color:#868e96}.mediacomponent a.image{display:block}.mediacomponent a.image+a.text{margin-top:.5rem}.mediacomponent .rich>iframe{margin:0!important}.mediacomponent .video{width:100%;height:0;display:block;position:relative}.mediacomponent .video.four-three{padding-bottom:75%}.mediacomponent .video.sixteen-nine{padding-bottom:56.25%}.mediacomponent .video>iframe{top:0;left:0;border:0;width:100%;height:100%;position:absolute}.pagecomponent.page-title-hidden .content-container>article>header{display:none}.pagecomponent .content-container>article>div{margin-bottom:1rem}.scrolltotopbutton{opacity:0;outline:0;right:1rem;bottom:1rem;width:4rem;height:4rem;z-index:1000;position:fixed;display:block;overflow:hidden;visibility:hidden;white-space:nowrap;text-align:center;font-size:16px;line-height:4rem;box-shadow:0 0 20px rgba(0,0,0,.2);transition:opacity .3s 0s,visibility 0s .3s,color .15s ease-in-out 0s,background-color .15s ease-in-out 0s}.scrolltotopbutton.is-visible{opacity:1;visibility:visible}.scrolltotopbutton.fade-out{opacity:.5}.scrolltotopbutton:hover{opacity:1}@media (min-width:768px){.scrolltotopbutton{width:5rem;height:5rem;right:2rem;bottom:2rem;line-height:5rem}}@media (min-width:992px){.scrolltotopbutton{width:6rem;height:6rem;font-size:20px;line-height:6rem}}.tilecomponent .items{display:flex;flex-flow:row wrap;align-items:stretch}.tilecomponent .items>article.item{display:flex;flex-direction:column;flex:1 100%;padding:2%}.tilecomponent .items>article.item>section.content{margin-top:auto}.tilecomponent .items>article.item>section.content header>*{font-size:1rem;line-height:1.2}.tilecomponent .items>article.item>section.content div.details>span{display:block;margin-right:0;margin-bottom:.5rem}@media (min-width:576px){.tilecomponent .items>article.item{flex:1 50%}}@media (min-width:768px){.tilecomponent .items>article.item{flex:1 33.3%}}@media (min-width:992px){.tilecomponent .items>article.item{flex:1 25%}}.detail-fields>dl dt{font-weight:500}.detail-fields>dl dd{font-size:.9rem;margin-bottom:1rem}.component.link{text-decoration:none}.component.link,.component.link>i.fa{display:inline-block}.component.link:active,.component.link:focus,.component.link:hover{text-decoration:none}.show-icons.hide-text .component.link{overflow:hidden;text-align:center;transition:color .15s ease-in-out 0s,background-color .15s ease-in-out 0s}.show-icons.hide-text .component.link>i.fa{display:block}.show-icons.hide-text .component.link.size-16{width:16px;height:16px;font-size:8px;line-height:16px}.show-icons.hide-text .component.link.size-16>i.fa{height:16px;line-height:16px}.show-icons.hide-text .component.link.size-24{width:24px;height:24px;font-size:12px;line-height:24px}.show-icons.hide-text .component.link.size-24>i.fa{height:24px;line-height:24px}.show-icons.hide-text .component.link.size-32{width:32px;height:32px;font-size:16px;line-height:32px}.show-icons.hide-text .component.link.size-32>i.fa{height:32px;line-height:32px}.show-icons.hide-text .component.link.size-48{width:48px;height:48px;font-size:24px;line-height:48px}.show-icons.hide-text .component.link.size-48>i.fa{height:48px;line-height:48px}.show-icons.hide-text .component.link.size-64{width:64px;height:64px;font-size:32px;line-height:64px}.show-icons.hide-text .component.link.size-64>i.fa{height:64px;line-height:64px}.show-icons.hide-text .component.link.size-96{width:96px;height:96px;font-size:48px;line-height:96px}.show-icons.hide-text .component.link.size-96>i.fa{height:96px;line-height:96px}.show-icons.hide-text .component.link.size-128{width:128px;height:128px;font-size:64px;line-height:128px}.show-icons.hide-text .component.link.size-128>i.fa{height:128px;line-height:128px}.hide-icons .component.link>i.fa,.hide-text .component.link>span.text{display:none} \ No newline at end of file diff --git a/client/src/styles/_variables.scss b/client/src/styles/_variables.scss index bc9316a..2f00667 100644 --- a/client/src/styles/_variables.scss +++ b/client/src/styles/_variables.scss @@ -3,3 +3,9 @@ $icon-size-list: 16 24 32 48 64 96 128; $icon-size-multiplier: 0.5; + +$image-overlay-background: rgba($gray-900, 0.5); +$image-overlay-foreground: $white; + +$detail-fields-heading-weight: 500; +$detail-fields-text-font-size: 0.9rem; diff --git a/client/src/styles/bundle.scss b/client/src/styles/bundle.scss index 738327b..41909ca 100644 --- a/client/src/styles/bundle.scss +++ b/client/src/styles/bundle.scss @@ -6,6 +6,12 @@ @import "~silverware-theme/styles/variables"; @import "~silverware-theme/styles/mixins"; +// Import Bootstrap Styles: + +@import "~bootstrap/scss/mixins"; +@import "~bootstrap/scss/functions"; +@import "~bootstrap/scss/variables"; + // Import Local Files: @import "variables"; @@ -22,6 +28,10 @@ @import "components/ScrollToTopButton"; @import "components/TileComponent"; +// Import Extension Styles: + +@import "extensions/DetailFieldsExtension"; + // Import Model Styles: @import "model/Link"; diff --git a/client/src/styles/components/BaseListComponent.scss b/client/src/styles/components/BaseListComponent.scss index 6dee026..4bc48c6 100644 --- a/client/src/styles/components/BaseListComponent.scss +++ b/client/src/styles/components/BaseListComponent.scss @@ -29,12 +29,12 @@ opacity: 0; position: absolute; transition: .5s ease; - background-color: rgba(0, 0, 0, 0.5); + background-color: $image-overlay-background; > i { top: 50%; left: 50%; - color: $white; + color: $image-overlay-foreground; position: absolute; font-size: 32px; transform: translate(-50%, -50%); diff --git a/client/src/styles/components/FeatureComponent.scss b/client/src/styles/components/FeatureComponent.scss index a11690d..6b65b11 100644 --- a/client/src/styles/components/FeatureComponent.scss +++ b/client/src/styles/components/FeatureComponent.scss @@ -5,6 +5,15 @@ article.feature { + > a.feature { + display: block; + text-decoration: none; + @include hover-focus { + text-decoration: none; + } + @include transition($btn-transition); + } + div.icon { margin-bottom: $spacer-half; } diff --git a/client/src/styles/extensions/DetailFieldsExtension.scss b/client/src/styles/extensions/DetailFieldsExtension.scss new file mode 100644 index 0000000..6361889 --- /dev/null +++ b/client/src/styles/extensions/DetailFieldsExtension.scss @@ -0,0 +1,19 @@ +/* Detail Fields Extension Styles +===================================================================================================================== */ + +.detail-fields { + + > dl { + + dt { + font-weight: $detail-fields-heading-weight; + } + + dd { + font-size: $detail-fields-text-font-size; + margin-bottom: $spacer; + } + + } + +} diff --git a/src/Components/BaseComponent.php b/src/Components/BaseComponent.php index faddec4..e1c432c 100644 --- a/src/Components/BaseComponent.php +++ b/src/Components/BaseComponent.php @@ -193,7 +193,7 @@ public function getContent() */ public function getContentClassNames() { - $classes = ['content']; + $classes = $this->styles('content'); $this->extend('updateContentClassNames', $classes); diff --git a/src/Components/BaseListComponent.php b/src/Components/BaseListComponent.php index 9cd2249..94328bd 100644 --- a/src/Components/BaseListComponent.php +++ b/src/Components/BaseListComponent.php @@ -19,6 +19,7 @@ use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\DropdownField; +use SilverStripe\Forms\FieldList; use SilverStripe\Forms\TextField; use SilverWare\Colorpicker\Forms\ColorField; use SilverWare\Extensions\Lists\ListSourceExtension; @@ -45,6 +46,7 @@ class BaseListComponent extends BaseComponent const SHOW_ALL = 'all'; const SHOW_LAST = 'last'; const SHOW_FIRST = 'first'; + const SHOW_NONE = 'none'; /** * Define align constants. @@ -153,7 +155,6 @@ public function getCMSFields() // Define Placeholders: - $placeholderNone = _t(__CLASS__ . '.NONE', 'None'); $placeholderDefault = _t(__CLASS__ . '.DROPDOWNDEFAULT', '(default)'); // Create Style Fields: @@ -183,6 +184,10 @@ public function getCMSFields() ] ); + // Add List Style Fields: + + $fields->addFieldsToTab('Root.Style', $this->getListStyleFields()); + // Create Options Fields: $fields->addFieldsToTab( @@ -196,32 +201,32 @@ public function getCMSFields() 'ShowImage', $this->fieldLabel('ShowImage'), $this->getShowOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholderNone), + ), DropdownField::create( 'ShowHeader', $this->fieldLabel('ShowHeader'), $this->getShowOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholderNone), + ), DropdownField::create( 'ShowDetails', $this->fieldLabel('ShowDetails'), $this->getShowOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholderNone), + ), DropdownField::create( 'ShowSummary', $this->fieldLabel('ShowSummary'), $this->getShowOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholderNone), + ), DropdownField::create( 'ShowContent', $this->fieldLabel('ShowContent'), $this->getShowOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholderNone), + ), DropdownField::create( 'ShowFooter', $this->fieldLabel('ShowFooter'), $this->getShowOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholderNone), + ), TextField::create( 'DateFormat', $this->fieldLabel('DateFormat') @@ -258,11 +263,35 @@ public function getCMSFields() ] ); + // Add List Option Fields: + + $fields->addFieldsToTab('Root.Options', $this->getListOptionFields()); + // Answer Field Objects: return $fields; } + /** + * Answers the list style fields for the receiver. + * + * @return FieldList + */ + public function getListStyleFields() + { + return FieldList::create(); + } + + /** + * Answers the list option fields for the receiver. + * + * @return FieldList + */ + public function getListOptionFields() + { + return FieldList::create(); + } + /** * Answers the labels for the fields of the receiver. * @@ -469,6 +498,7 @@ public function getNoDataMessage() public function getShowOptions() { return [ + self::SHOW_NONE => _t(__CLASS__ . '.NONE', 'None'), self::SHOW_FIRST => _t(__CLASS__ . '.FIRST', 'First'), self::SHOW_LAST => _t(__CLASS__ . '.LAST', 'Last'), self::SHOW_ALL => _t(__CLASS__ . '.ALL', 'All') @@ -514,6 +544,10 @@ public function getURL() */ protected function isShown($name, $isFirst, $isMiddle, $isLast) { + if (!$this->{"Show{$name}"} || $this->{"Show{$name}"} == self::SHOW_NONE) { + return false; + } + return ( ($this->{"Show{$name}"} == self::SHOW_FIRST && $isFirst) || ($this->{"Show{$name}"} == self::SHOW_LAST && $isLast) || diff --git a/src/Components/FeatureComponent.php b/src/Components/FeatureComponent.php index 48c71d2..e149d64 100644 --- a/src/Components/FeatureComponent.php +++ b/src/Components/FeatureComponent.php @@ -24,7 +24,9 @@ use SilverStripe\Forms\HTMLEditor\HTMLEditorField; use SilverStripe\Forms\TextField; use SilverWare\Extensions\Model\ImageResizeExtension; +use SilverWare\Extensions\Model\LinkToExtension; use SilverWare\Extensions\Style\AlignmentStyle; +use SilverWare\Extensions\Style\CornerStyle; use SilverWare\FontIcons\Extensions\FontIconExtension; use SilverWare\Forms\FieldSection; use SilverWare\Forms\PageDropdownField; @@ -81,12 +83,13 @@ class FeatureComponent extends BaseComponent */ private static $db = [ 'Summary' => 'HTMLText', - 'CustomHeading' => 'Varchar(255)', - 'CustomSubHeading' => 'Varchar(255)', + 'Heading' => 'Varchar(255)', + 'SubHeading' => 'Varchar(255)', 'HeadingLevel' => 'Varchar(2)', 'SubHeadingLevel' => 'Varchar(2)', 'ButtonLabel' => 'Varchar(128)', 'LinkHeading' => 'Boolean', + 'LinkFeature' => 'Boolean', 'ShowIcon' => 'Boolean', 'ShowImage' => 'Boolean', 'ShowHeader' => 'Boolean', @@ -101,8 +104,7 @@ class FeatureComponent extends BaseComponent * @config */ private static $has_one = [ - 'Image' => Image::class, - 'FeaturedPage' => Page::class + 'Image' => Image::class ]; /** @@ -122,6 +124,7 @@ class FeatureComponent extends BaseComponent * @config */ private static $defaults = [ + 'LinkFeature' => 0, 'LinkHeading' => 1, 'ShowIcon' => 1, 'ShowImage' => 1, @@ -163,11 +166,23 @@ class FeatureComponent extends BaseComponent * @config */ private static $extensions = [ + CornerStyle::class, AlignmentStyle::class, + LinkToExtension::class, FontIconExtension::class, ImageResizeExtension::class ]; + /** + * Defines the style extension classes to apply to this object. + * + * @var array + * @config + */ + private static $apply_styles = [ + AlignmentStyle::class + ]; + /** * Defines the asset folder for uploading images. * @@ -208,21 +223,17 @@ public function getCMSFields() $fields->addFieldsToTab( 'Root.Main', [ - PageDropdownField::create( - 'FeaturedPageID', - $this->fieldLabel('FeaturedPageID') - ), FieldSection::create( 'HeadingSection', $this->fieldLabel('HeadingSection'), [ TextField::create( - 'CustomHeading', - $this->fieldLabel('CustomHeading') + 'Heading', + $this->fieldLabel('Heading') ), TextField::create( - 'CustomSubHeading', - $this->fieldLabel('CustomSubHeading') + 'SubHeading', + $this->fieldLabel('SubHeading') ) ] ), @@ -286,13 +297,17 @@ public function getCMSFields() 'ShowFooter', $this->fieldLabel('ShowFooter') ), - TextField::create( - 'ButtonLabel', - $this->fieldLabel('ButtonLabel') - ), CheckboxField::create( 'LinkHeading', $this->fieldLabel('LinkHeading') + ), + CheckboxField::create( + 'LinkFeature', + $this->fieldLabel('LinkFeature') + ), + TextField::create( + 'ButtonLabel', + $this->fieldLabel('ButtonLabel') ) ] ) @@ -326,10 +341,10 @@ public function fieldLabels($includerelations = true) $labels['ShowSummary'] = _t(__CLASS__ . '.SHOWSUMMARY', 'Show summary'); $labels['ShowFooter'] = _t(__CLASS__ . '.SHOWFOOTER', 'Show footer'); $labels['LinkHeading'] = _t(__CLASS__ . '.LINKHEADING', 'Link heading'); + $labels['LinkFeature'] = _t(__CLASS__ . '.LINKFEATURE', 'Link feature'); $labels['ButtonLabel'] = _t(__CLASS__ . '.BUTTONLABEL', 'Button label'); - $labels['FeaturedPageID'] = _t(__CLASS__ . '.FEATUREDPAGE', 'Featured page'); - $labels['CustomHeading'] = _t(__CLASS__ . '.CUSTOMHEADING', 'Custom heading'); - $labels['CustomSubHeading'] = _t(__CLASS__ . '.CUSTOMSUBHEADING', 'Custom sub-heading'); + $labels['Heading'] = _t(__CLASS__ . '.HEADING', 'Heading'); + $labels['SubHeading'] = _t(__CLASS__ . '.SUBHEADING', 'Sub-heading'); $labels['HeadingLevel'] = _t(__CLASS__ . '.HEADINGLEVEL', 'Heading level'); $labels['SubHeadingLevel'] = _t(__CLASS__ . '.SUBHEADINGLEVEL', 'Sub-heading level'); $labels['HeadingSection'] = _t(__CLASS__ . '.HEADINGS', 'Headings'); @@ -339,7 +354,6 @@ public function fieldLabels($includerelations = true) if ($includerelations) { $labels['Image'] = _t(__CLASS__ . '.has_one_Image', 'Image'); - $labels['EntityPage'] = _t(__CLASS__ . '.has_one_FeaturedPage', 'Featured Page'); } // Answer Field Labels: @@ -384,6 +398,10 @@ public function getWrapperClassNames() $classes[] = $this->style('feature'); + if ($this->CornerStyleClass) { + $classes[] = $this->CornerStyleClass; + } + $this->extend('updateWrapperClassNames', $classes); return $classes; @@ -447,6 +465,20 @@ public function getSubHeadingClassNames() return $classes; } + /** + * Answers an array of link class names for the template. + * + * @return array + */ + public function getLinkClassNames() + { + $classes = ['feature']; + + $this->extend('updateLinkClassNames', $classes); + + return $classes; + } + /** * Answers the heading tag for the receiver. * @@ -468,8 +500,12 @@ public function getHeadingTag() */ public function getHeadingText() { - if ($this->hasPage()) { - return $this->CustomHeading ? $this->CustomHeading : $this->FeaturedPage()->MetaTitle; + if ($this->Heading) { + return $this->Heading; + } + + if ($this->hasLinkPage()) { + return $this->LinkPage()->MetaTitle; } } @@ -494,17 +530,7 @@ public function getSubHeadingTag() */ public function getSubHeadingText() { - return $this->CustomSubHeading; - } - - /** - * Answers true of the feature page exists. - * - * @return boolean - */ - public function hasPage() - { - return $this->FeaturedPage()->isInDB(); + return $this->SubHeading; } /** @@ -514,7 +540,7 @@ public function hasPage() */ public function hasImage() { - return ($this->Image()->exists() || $this->FeaturedPage()->hasMetaImage()); + return ($this->Image()->exists() || $this->LinkPage()->hasMetaImage()); } /** @@ -547,6 +573,46 @@ public function getHeaderShown() return (boolean) $this->ShowHeader; } + /** + * Answers the link for the feature. + * + * @return string + */ + public function getFeatureLink() + { + return $this->getLink(); + } + + /** + * Answers the title for the link. + * + * @return string + */ + public function getLinkTitle() + { + return $this->HeadingText ? $this->HeadingText : $this->Title; + } + + /** + * Answers true if the feature is to be linked. + * + * @return boolean + */ + public function getFeatureLinked() + { + return ($this->LinkFeature && $this->hasLink()); + } + + /** + * Answers true if the heading is to be linked. + * + * @return boolean + */ + public function getHeadingLinked() + { + return ($this->LinkHeading && $this->hasLink() && !$this->LinkFeature); + } + /** * Answers true if the summary is to be shown in the template. * @@ -564,7 +630,7 @@ public function getSummaryShown() */ public function getFooterShown() { - return (boolean) $this->ShowFooter; + return ($this->ShowFooter && $this->hasLink() && !$this->LinkFeature); } /** @@ -574,7 +640,13 @@ public function getFooterShown() */ public function getSummaryText() { - return ($this->Summary) ? $this->Summary : $this->FeaturedPage()->getMetaSummary(); + if ($this->Summary) { + return $this->Summary; + } + + if ($this->hasLinkPage()) { + return $this->LinkPage()->MetaSummary; + } } /** @@ -586,24 +658,10 @@ public function getImageResized() { if ($this->Image()->exists()) { $image = $this->Image(); - } elseif ($this->FeaturedPage()->hasMetaImage()) { - $image = $this->FeaturedPage()->getMetaImage(); + } elseif ($this->LinkPage()->hasMetaImage()) { + $image = $this->LinkPage()->getMetaImage(); } return $this->performImageResize($image); } - - /** - * Answers true if the object is disabled within the template. - * - * @return boolean - */ - public function isDisabled() - { - if (!$this->hasPage()) { - return true; - } - - return parent::isDisabled(); - } } diff --git a/src/Components/PageComponent.php b/src/Components/PageComponent.php index 07978d0..daf9185 100644 --- a/src/Components/PageComponent.php +++ b/src/Components/PageComponent.php @@ -120,16 +120,6 @@ class PageComponent extends BaseComponent 'CurrentPageAttributesHTML' => 'HTMLFragment' ]; - /** - * Defines the default classes to use when rendering this object. - * - * @var array - * @config - */ - private static $default_classes = [ - 'typography' - ]; - /** * Answers a list of field objects for the CMS interface. * diff --git a/src/Dev/Installer.php b/src/Dev/Installer.php index ba40dee..b701326 100644 --- a/src/Dev/Installer.php +++ b/src/Dev/Installer.php @@ -78,7 +78,7 @@ class Installer 'default-locale' => 'en_GB', 'default-database' => '{app-name}', 'default-vendor' => 'vendor', - 'default-repo-name' => '{default-vendor}/app-{app-name}', + 'default-repo-name' => '{vendor}/app-{app-name}', 'default-repo-url' => 'git@bitbucket.org:{repo-name}.git', 'default-user' => 'serverpilot', 'default-host-staging' => '{app-name}-staging.example.com', @@ -220,6 +220,22 @@ public function onPostCreateProject() $data ); + // Obtain Database Name: + + $data['database'] = $this->ask( + 'Database name', + $this->getConfig('default-database'), + $data + ); + + // Obtain Vendor: + + $data['vendor'] = $this->ask( + 'Vendor', + $this->getConfig('default-vendor'), + $data + ); + // Obtain Repository Name: $data['repo-name'] = $this->ask( diff --git a/src/Extensions/AreaExtension.php b/src/Extensions/AreaExtension.php index 2f97da2..0566810 100644 --- a/src/Extensions/AreaExtension.php +++ b/src/Extensions/AreaExtension.php @@ -17,6 +17,7 @@ namespace SilverWare\Extensions; +use SilverStripe\Core\ClassInfo; use SilverStripe\ORM\DataExtension; use SilverWare\Components\AreaComponent; use SilverWare\Model\Panel; @@ -86,10 +87,10 @@ public function getPanelForArea(AreaComponent $area) /** * Answers a list of child panels from the extended object. * - * @return DataList + * @return ArrayList */ public function getChildPanels() { - return $this->owner->AllChildren()->filter('ClassName', Panel::class); + return $this->owner->AllChildren()->filter('ClassName', ClassInfo::subclassesFor(Panel::class)); } } diff --git a/src/Extensions/ControllerExtension.php b/src/Extensions/ControllerExtension.php index 4b0e023..07e542b 100644 --- a/src/Extensions/ControllerExtension.php +++ b/src/Extensions/ControllerExtension.php @@ -280,18 +280,6 @@ protected function initRequirements() } - // Component Requirements Enabled? - - if ($this->owner->config()->load_component_requirements) { - - // Load Component Requirements: - - foreach ($this->owner->getEnabledComponents() as $component) { - $component->loadRequirements(); - } - - } - } } diff --git a/src/Extensions/Lists/ListItemExtension.php b/src/Extensions/Lists/ListItemExtension.php index 090a826..7389429 100644 --- a/src/Extensions/Lists/ListItemExtension.php +++ b/src/Extensions/Lists/ListItemExtension.php @@ -145,6 +145,16 @@ public function getListItemClassNames() return $classes; } + /** + * Answers an string of list item content class names for the HTML template. + * + * @return string + */ + public function getListItemContentClass() + { + return ViewTools::singleton()->array2att($this->owner->getListItemContentClassNames()); + } + /** * Answers an string of list item image class names for the HTML template. * @@ -155,6 +165,16 @@ public function getListItemImageClass() return ViewTools::singleton()->array2att($this->owner->getListItemImageClassNames()); } + /** + * Answers an array of list item content class names for the HTML template. + * + * @return array + */ + public function getListItemContentClassNames() + { + return $this->styles('content', 'content.typography'); + } + /** * Answers an array of list item image class names for the HTML template. * diff --git a/src/Extensions/Lists/ListViewExtension.php b/src/Extensions/Lists/ListViewExtension.php index 366493a..46a5640 100644 --- a/src/Extensions/Lists/ListViewExtension.php +++ b/src/Extensions/Lists/ListViewExtension.php @@ -17,14 +17,18 @@ namespace SilverWare\Extensions\Lists; +use SilverStripe\Control\Controller; use SilverStripe\Core\Config\Config; +use SilverStripe\Core\Convert; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\TextField; use SilverStripe\ORM\DataExtension; +use SilverWare\Colorpicker\Forms\ColorField; use SilverWare\Components\BaseListComponent; use SilverWare\Components\ListComponent; +use SilverWare\FontIcons\Forms\FontIconField; use SilverWare\Forms\DimensionsField; use SilverWare\Forms\FieldSection; use SilverWare\Forms\ViewportsField; @@ -44,25 +48,18 @@ class ListViewExtension extends DataExtension { /** - * Maps field names to field types for this object. + * Define constants. + */ + const FIELD_WRAPPER = 'ListConfig'; + + /** + * Maps field names to field types for the extended object. * * @var array * @config */ private static $db = [ - 'ListTitle' => 'Varchar(255)', - 'ListImageResize' => 'Dimensions', - 'ListImageResizeMethod' => 'Varchar(32)', - 'ListImageLinksTo' => 'Varchar(8)', - 'ListImageAlignment' => 'Viewports', - 'ListTextAlignment' => 'Viewports', - 'ListItemsPerPage' => 'AbsoluteInt', - 'ListHeadingLevel' => 'Varchar(2)', - 'ListButtonLabel' => 'Varchar(128)', - 'ListPaginateItems' => 'Varchar(1)', - 'ListDateFormat' => 'Varchar(32)', - 'ListLinkTitles' => 'Varchar(1)', - 'ListTitleHidden' => 'Varchar(1)' + 'ListConfig' => 'Text' ]; /** @@ -97,7 +94,7 @@ public function updateCMSFields(FieldList $fields) // Create List Component: - $list = Injector::inst()->get($this->owner->getListComponentClass()); + $list = $this->owner->getListComponent(); // Create Style Fields: @@ -108,80 +105,177 @@ public function updateCMSFields(FieldList $fields) $this->owner->fieldLabel('ListView'), [ DropdownField::create( - 'ListHeadingLevel', - $this->owner->fieldLabel('ListHeadingLevel'), + $this->nestName('HeadingLevel'), + $this->owner->fieldLabel('HeadingLevel'), $list->getTitleLevelOptions() )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), ViewportsField::create( - 'ListTextAlignment', - $this->owner->fieldLabel('ListTextAlignment'), + $this->nestName('TextAlignment'), + $this->owner->fieldLabel('TextAlignment'), $list->getTextAlignmentOptions() ), ViewportsField::create( - 'ListImageAlignment', - $this->owner->fieldLabel('ListImageAlignment'), + $this->nestName('ImageAlignment'), + $this->owner->fieldLabel('ImageAlignment'), $list->getImageAlignmentOptions() ), DimensionsField::create( - 'ListImageResize', - $this->owner->fieldLabel('ListImageResize') + $this->nestName('ImageResize'), + $this->owner->fieldLabel('ImageResize') ), DropdownField::create( - 'ListImageResizeMethod', - $this->owner->fieldLabel('ListImageResizeMethod'), + $this->nestName('ImageResizeMethod'), + $this->owner->fieldLabel('ImageResizeMethod'), ImageTools::singleton()->getResizeMethods() )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + FontIconField::create( + $this->nestName('OverlayIcon'), + $this->owner->fieldLabel('OverlayIcon') + ), + ColorField::create( + $this->nestName('OverlayIconColor'), + $this->owner->fieldLabel('OverlayIconColor') + ) ] ) ); + // Add List Style Fields: + + $fields->addFieldsToTab('Root.Style', $this->nest($list->getListStyleFields())); + // Create Options Fields: - $fields->addFieldToTab( + $fields->addFieldsToTab( 'Root.Options', - FieldSection::create( - 'ListViewOptions', - $this->owner->fieldLabel('ListView'), - [ - DropdownField::create( - 'ListPaginateItems', - $this->owner->fieldLabel('ListPaginateItems'), - $this->getToggleOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), - TextField::create( - 'ListItemsPerPage', - $this->owner->fieldLabel('ListItemsPerPage') - ), - DropdownField::create( - 'ListImageLinksTo', - $this->owner->fieldLabel('ListImageLinksTo'), - $list->getImageLinksToOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), - TextField::create( - 'ListTitle', - $this->owner->fieldLabel('ListTitle') - ), - TextField::create( - 'ListDateFormat', - $this->owner->fieldLabel('ListDateFormat') - ), - TextField::create( - 'ListButtonLabel', - $this->owner->fieldLabel('ListButtonLabel') - ), - DropdownField::create( - 'ListLinkTitles', - $this->owner->fieldLabel('ListLinkTitles'), - $this->getToggleOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), - DropdownField::create( - 'ListTitleHidden', - $this->owner->fieldLabel('ListTitleHidden'), - $this->getToggleOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder) - ] - ) + [ + $sourceOpts = FieldSection::create( + 'ListSourceOptions', + $this->owner->fieldLabel('ListSource'), + [ + TextField::create( + $this->nestName('NumberOfItems'), + $this->owner->fieldLabel('NumberOfItems') + ), + DropdownField::create( + $this->nestName('ReverseItems'), + $this->owner->fieldLabel('ReverseItems'), + $this->getToggleOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('ImageItems'), + $this->owner->fieldLabel('ImageItems'), + $this->getToggleOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder) + ] + ), + FieldSection::create( + 'ListViewOptions', + $this->owner->fieldLabel('ListView'), + [ + TextField::create( + $this->nestName('Title'), + $this->owner->fieldLabel('ListTitle') + ), + DropdownField::create( + $this->nestName('ShowImage'), + $this->owner->fieldLabel('ShowImage'), + $list->getShowOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('ShowHeader'), + $this->owner->fieldLabel('ShowHeader'), + $list->getShowOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('ShowDetails'), + $this->owner->fieldLabel('ShowDetails'), + $list->getShowOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('ShowSummary'), + $this->owner->fieldLabel('ShowSummary'), + $list->getShowOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('ShowContent'), + $this->owner->fieldLabel('ShowContent'), + $list->getShowOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('ShowFooter'), + $this->owner->fieldLabel('ShowFooter'), + $list->getShowOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + TextField::create( + $this->nestName('DateFormat'), + $this->owner->fieldLabel('DateFormat') + ), + TextField::create( + $this->nestName('ButtonLabel'), + $this->owner->fieldLabel('ButtonLabel') + ), + DropdownField::create( + $this->nestName('LinkTitles'), + $this->owner->fieldLabel('LinkTitles'), + $this->getToggleOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('TitleHidden'), + $this->owner->fieldLabel('TitleHidden'), + $this->getToggleOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder) + ] + ), + FieldSection::create( + 'ListImageOptions', + $this->owner->fieldLabel('ListImages'), + [ + DropdownField::create( + $this->nestName('LinkImages'), + $this->owner->fieldLabel('LinkImages'), + $this->getToggleOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('ImageLinksTo'), + $this->owner->fieldLabel('ImageLinksTo'), + $list->getImageLinksToOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DropdownField::create( + $this->nestName('OverlayImages'), + $this->owner->fieldLabel('OverlayImages'), + $this->getToggleOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder) + ] + ) + ] ); + + // Add List Option Fields: + + $fields->addFieldsToTab('Root.Options', $this->nest($list->getListOptionFields())); + + // Merge Pagination Fields: + + if ($list->canPaginate()) { + + $sourceOpts->merge([ + DropdownField::create( + $this->nestName('PaginateItems'), + $this->owner->fieldLabel('PaginateItems'), + $this->getToggleOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + TextField::create( + $this->nestName('ItemsPerPage'), + $this->owner->fieldLabel('ItemsPerPage') + ) + ]); + + } + + // Load List Config: + + $this->owner->loadListConfig($fields); } /** @@ -197,20 +291,34 @@ public function updateFieldLabels(&$labels) $labels['Options'] = _t(__CLASS__ . '.OPTIONS', 'Options'); $labels['Enabled'] = _t(__CLASS__ . '.ENABLED', 'Enabled'); $labels['Disabled'] = _t(__CLASS__ . '.DISABLED', 'Disabled'); - $labels['ListView'] = _t(__CLASS__ . '.LISTVIEW', 'List view'); - $labels['ListTitle'] = _t(__CLASS__ . '.LISTTITLE', 'List title'); - $labels['ListDateFormat'] = _t(__CLASS__ . '.DATEFORMAT', 'Date format'); - $labels['ListLinkTitles'] = _t(__CLASS__ . '.LINKTITLES', 'Link titles'); - $labels['ListButtonLabel'] = _t(__CLASS__ . '.BUTTONLABEL', 'Button label'); - $labels['ListHeadingLevel'] = _t(__CLASS__ . '.HEADINGLEVEL', 'Heading level'); - $labels['ListItemsPerPage'] = _t(__CLASS__ . '.ITEMSPERPAGE', 'Items per page'); - $labels['ListPaginateItems'] = _t(__CLASS__ . '.PAGINATEITEMS', 'Paginate items'); - $labels['ListImageLinksTo'] = _t(__CLASS__ . '.IMAGELINKSTO', 'Image links to'); - $labels['ListImageResize'] = _t(__CLASS__ . '.IMAGEDIMENSIONS', 'Image dimensions'); - $labels['ListImageResizeMethod'] = _t(__CLASS__ . '.IMAGERESIZEMETHOD', 'Image resize method'); - $labels['ListImageAlignment'] = _t(__CLASS__ . '.IMAGEALIGNMENT', 'Image alignment'); - $labels['ListTextAlignment'] = _t(__CLASS__ . '.TEXTALIGNMENT', 'Text alignment'); - $labels['ListTitleHidden'] = _t(__CLASS__ . '.HIDELISTTITLE', 'Hide list title'); + $labels['ListView'] = _t(__CLASS__ . '.LISTVIEW', 'List View'); + $labels['ListImages'] = _t(__CLASS__ . '.LISTIMAGES', 'List Images'); + $labels['ListTitle'] = _t(__CLASS__ . '.TITLE', 'List title'); + $labels['ShowImage'] = _t(__CLASS__ . '.SHOWIMAGE', 'Show image'); + $labels['ShowHeader'] = _t(__CLASS__ . '.SHOWHEADER', 'Show header'); + $labels['ShowDetails'] = _t(__CLASS__ . '.SHOWDETAILS', 'Show details'); + $labels['ShowSummary'] = _t(__CLASS__ . '.SHOWSUMMARY', 'Show summary'); + $labels['ShowContent'] = _t(__CLASS__ . '.SHOWCONTENT', 'Show content'); + $labels['ShowFooter'] = _t(__CLASS__ . '.SHOWFOOTER', 'Show footer'); + $labels['DateFormat'] = _t(__CLASS__ . '.DATEFORMAT', 'Date format'); + $labels['LinkTitles'] = _t(__CLASS__ . '.LINKTITLES', 'Link titles'); + $labels['LinkImages'] = _t(__CLASS__ . '.LINKIMAGES', 'Link images'); + $labels['ButtonLabel'] = _t(__CLASS__ . '.BUTTONLABEL', 'Button label'); + $labels['HeadingLevel'] = _t(__CLASS__ . '.HEADINGLEVEL', 'Heading level'); + $labels['ItemsPerPage'] = _t(__CLASS__ . '.ITEMSPERPAGE', 'Items per page'); + $labels['PaginateItems'] = _t(__CLASS__ . '.PAGINATEITEMS', 'Paginate items'); + $labels['ImageLinksTo'] = _t(__CLASS__ . '.IMAGELINKSTO', 'Image links to'); + $labels['ImageResize'] = _t(__CLASS__ . '.IMAGEDIMENSIONS', 'Image dimensions'); + $labels['ImageResizeMethod'] = _t(__CLASS__ . '.IMAGERESIZEMETHOD', 'Image resize method'); + $labels['ImageAlignment'] = _t(__CLASS__ . '.IMAGEALIGNMENT', 'Image alignment'); + $labels['TextAlignment'] = _t(__CLASS__ . '.TEXTALIGNMENT', 'Text alignment'); + $labels['TitleHidden'] = _t(__CLASS__ . '.HIDELISTTITLE', 'Hide list title'); + $labels['OverlayImages'] = _t(__CLASS__ . '.OVERLAYIMAGES', 'Overlay images'); + $labels['OverlayIcon'] = _t(__CLASS__ . '.OVERLAYICON', 'Overlay icon'); + $labels['OverlayIconColor'] = _t(__CLASS__ . '.OVERLAYICONCOLOR', 'Overlay icon color'); + $labels['NumberOfItems'] = _t(__CLASS__ . '.NUMBEROFITEMS', 'Number of items'); + $labels['ReverseItems'] = _t(__CLASS__ . '.REVERSEITEMS', 'Reverse items'); + $labels['ImageItems'] = _t(__CLASS__ . '.IMAGEITEMS', 'Show only items with images'); } /** @@ -220,8 +328,152 @@ public function updateFieldLabels(&$labels) */ public function onBeforeWrite() { - if (!$this->owner->ListPaginateItems) { - $this->owner->ListItemsPerPage = null; + if ($this->owner->isInDB()) { + + if ($request = Controller::curr()->getRequest()) { + + if ($config = $request->postVar(self::FIELD_WRAPPER)) { + $this->owner->setListConfig($config); + } + + } + + } + } + + /** + * Defines the value of the list config field. + * + * @param array $config + * + * @return $this + */ + public function setListConfig($config) + { + return $this->owner->setField('ListConfig', Convert::array2json($config)); + } + + /** + * Answers the value of the list config field. + * + * @return array|null + */ + public function getListConfig() + { + return ($config = $this->owner->getField('ListConfig')) ? Convert::json2array($config) : null; + } + + /** + * Answers true if the extended object has list config defined. + * + * @return boolean + */ + public function hasListConfig() + { + return is_array($this->owner->getListConfig()); + } + + /** + * Answers an array of list configuration with values inherited from parent objects. + * + * @return array + */ + public function getListConfigInherited() + { + // Obtain List Configuration: + + $config = $this->owner->hasListConfig() ? $this->owner->getListConfig() : []; + + // Obtain Inherited List Configuration: + + if ($this->owner->hasMethod('getParent')) { + + // Obtain Parent: + + $parent = $this->owner->getParent(); + + // Iterate While Parent Valid: + + while ($parent) { + + // Detect Extension and Configuration: + + if ($parent->hasExtension(self::class) && $parent->hasListConfig()) { + + // Iterate Parent Configuration: + + foreach ($parent->getListConfig() as $name => $value) { + + if (!$this->isEmptyValue($value)) { + + if (isset($config[$name]) && $this->isEmptyValue($config[$name])) { + $config[$name] = $value; + } + + } + + } + + } + + // Obtain Next Ancestor: + + $parent = $parent->hasMethod('getParent') ? $parent->getParent() : false; + + } + + } + + // Answer Config: + + return $config; + } + + /** + * Loads the list config into the given list of fields. + * + * @param FieldList $fields + * + * @return void + */ + public function loadListConfig(FieldList $fields) + { + // Obtain List Configuration: + + $config = $this->owner->getListConfig(); + + // Bail Early (if not array): + + if (!is_array($config)) { + return; + } + + // Iterate Configuration Values: + + foreach ($config as $name => $value) { + + if (is_array($value)) { + + // Handle Array Value: + + foreach ($value as $k => $v) { + + if ($field = $fields->dataFieldByName(self::FIELD_WRAPPER . "[$name][$k]")) { + $field->setValue($v); + } + + } + + } else { + + // Handle Regular Value: + + if ($field = $fields->dataFieldByName(self::FIELD_WRAPPER . "[$name]")) { + $field->setValue($value); + } + + } + } } @@ -256,42 +508,27 @@ public function getListComponent() $list = Injector::inst()->create($this->owner->getListComponentClass()); - // Define List Component: - - $list->setStyleIDFrom($this->owner); - - $this->setListField($list, 'Title', 'ListTitle'); - $this->setListField($list, 'HideTitle', 'ListTitleHidden'); + // Obtain List Configuration: - $this->setListField($list, 'HeadingLevel', 'ListHeadingLevel'); - $this->setListField($list, 'LinkTitles', 'ListLinkTitles'); + $config = $this->owner->getListConfigInherited(); - $this->setListField($list, 'PaginateItems', 'ListPaginateItems'); - $this->setListField($list, 'ItemsPerPage', 'ListItemsPerPage'); + // Configure List Component: - $this->setListField($list, 'ImageResizeWidth', 'ListImageResizeWidth'); - $this->setListField($list, 'ImageResizeHeight', 'ListImageResizeHeight'); - $this->setListField($list, 'ImageResizeMethod', 'ListImageResizeMethod'); - $this->setListField($list, 'ImageLinksTo', 'ListImageLinksTo'); - - $this->setListField($list, 'ButtonLabel', 'ListButtonLabel'); - $this->setListField($list, 'DateFormat', 'ListDateFormat'); - - // Define Text and Image Alignment: - - foreach (DBViewports::singleton()->getViewports() as $viewport) { - - $textField = "TextAlignment{$viewport}"; - $imageField = "ImageAlignment{$viewport}"; + if (is_array($config)) { - $this->setListField($list, $textField, "List{$textField}"); - $this->setListField($list, $imageField, "List{$imageField}"); + foreach ($config as $name => $value) { + + if (!$this->isEmptyValue($value)) { + $list->setField($name, $value); + } + + } } // Define List Parent: - $list->setParent($this->owner); + $list->setParentInstance($this->owner); // Define List Source: @@ -317,21 +554,29 @@ public function hasListComponent() } /** - * Defines the specified field for the given list component using a value from the hierarchy. + * Answers true if the given value is considered to be an 'empty' value. * - * @param BaseListComponent $list - * @param string $field - * @param string $value + * @param mixed $value * - * @return void + * @return boolean */ - protected function setListField(BaseListComponent $list, $field, $value) + protected function isEmptyValue($value) { - $value = $this->owner->getFieldFromHierarchy($value); - - if (!is_null($value)) { - $list->$field = $value; + if (is_array($value)) { + + foreach ($value as $item) { + + if ($this->isEmptyValue($item)) { + return true; + } + + } + + return false; + } + + return ($value === ''); } /** @@ -346,4 +591,54 @@ protected function getToggleOptions() 1 => _t(__CLASS__ . '.TOGGLEYES', 'Yes') ]; } + + /** + * Nests the names of the given fields within the list config wrapper. + * + * @param FieldList $fields + * + * @return FieldList + */ + protected function nest(FieldList $fields) + { + // Iterate Data Fields: + + foreach ($fields->dataFields() as $field) { + $field->setName($this->nestName($field->getName())); + } + + // Answer Fields: + + return $fields; + } + + /** + * Nests the given field name within the list config wrapper. + * + * @param string $name + * + * @return string + */ + protected function nestName($name) + { + // Obtain Bracket Position: + + $bpos = strpos($name, '['); + + // Has Bracket? + + if ($bpos !== false) { + + // Answer Bracketed Name: + + return sprintf('%s[%s]%s', self::FIELD_WRAPPER, substr($name, 0, $bpos), substr($name, $bpos)); + + } else { + + // Answer Regular Name: + + return sprintf('%s[%s]', self::FIELD_WRAPPER, $name); + + } + } } diff --git a/src/Extensions/Model/DetailFieldsExtension.php b/src/Extensions/Model/DetailFieldsExtension.php new file mode 100644 index 0000000..758880a --- /dev/null +++ b/src/Extensions/Model/DetailFieldsExtension.php @@ -0,0 +1,211 @@ +=5.6.0 + * + * For full copyright and license information, please view the + * LICENSE.md file that was distributed with this source code. + * + * @package SilverWare\Extensions\Model + * @author Colin Tucker + * @copyright 2017 Praxis Interactive + * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause + * @link https://github.com/praxisnetau/silverware + */ + +namespace SilverWare\Extensions\Model; + +use SilverStripe\Core\Extension; +use SilverStripe\ORM\ArrayList; +use SilverStripe\View\ArrayData; +use SilverWare\Tools\ViewTools; + +/** + * An extension class which adds detail fields functionality to the extended object. + * + * @package SilverWare\Extensions\Model + * @author Colin Tucker + * @copyright 2017 Praxis Interactive + * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause + * @link https://github.com/praxisnetau/silverware + */ +class DetailFieldsExtension extends Extension +{ + /** + * Defines the default detail fields heading level to use. + * + * @var string + * @config + */ + private static $detail_fields_heading_level_default = 'h3'; + + /** + * Answers an array list object containing the detail fields for the template. + * + * @return ArrayList + */ + public function getDetailFields() + { + // Create Details List: + + $details = ArrayList::create(); + + // Define Details List: + + foreach ($this->owner->getDetailFieldsConfig() as $name => $spec) { + + if ($spec) { + + foreach ($spec as $item => $value) { + + $args = []; + + if (is_array($value)) { + $args = $value; + $value = array_shift($args); + } + + $spec[$item] = $this->owner->processDetailFieldValue($value, $args); + + } + + $text = isset($spec['text']) ? $this->owner->processDetailFieldValue($spec['text']) : null; + + if ($text) { + + if (isset($spec['show']) && !$this->owner->{$spec['show']}) { + continue; + } + + $details->push( + ArrayData::create([ + 'Name' => isset($spec['name']) ? $spec['name'] : $name, + 'Icon' => isset($spec['icon']) ? $spec['icon'] : null, + 'Text' => $text + ]) + ); + + } + + } + + } + + // Answer Details List: + + return $details; + } + + /** + * Answers true if the extended object has detail fields available. + * + * @return boolean + */ + public function hasDetailFields() + { + return $this->owner->getDetailFields()->exists(); + } + + /** + * Answers the detail fields config for the extended object. + * + * @return array + */ + public function getDetailFieldsConfig() + { + $config = $this->owner->getDefaultDetailFieldsConfig(); + + if (is_array($this->owner->config()->detail_fields)) { + + foreach ($this->owner->config()->detail_fields as $name => $spec) { + + if (!$spec) { + unset($config[$name]); + } + + } + + $config = array_merge_recursive($config, $this->owner->config()->detail_fields); + + } + + return $config; + } + + /** + * Answers the default detail fields config for the extended object. + * + * @return array + */ + public function getDefaultDetailFieldsConfig() + { + if (is_array($this->owner->config()->default_detail_fields)) { + return $this->owner->config()->default_detail_fields; + } + + return []; + } + + /** + * Answers the heading tag for the detail fields section. + * + * @return string + */ + public function getDetailFieldsHeadingTag() + { + $tag = $this->owner->config()->detail_fields_heading_level; + + return $tag ? $tag : $this->owner->config()->detail_fields_heading_level_default; + } + + /** + * Answers the text for the detail fields heading. + * + * @return string + */ + public function getDetailFieldsHeadingText() + { + return _t(__CLASS__ . '.DEFAULTHEADING', 'Details'); + } + + /** + * Answers a string of class names for the detail fields wrapper. + * + * @return string + */ + public function getDetailFieldsClass() + { + return ViewTools::singleton()->array2att($this->owner->getDetailFieldsClassNames()); + } + + /** + * Answers an array of class names for the detail fields wrapper. + * + * @return array + */ + public function getDetailFieldsClassNames() + { + $classes = ['detail-fields']; + + $this->owner->extend('updateDetailFieldsClassNames', $classes); + + return $classes; + } + + /** + * Processes the given detail field value which references methods and/or fields of the extended object. + * + * @param string $value + * @param array $args + * + * @return string + */ + public function processDetailFieldValue($value, $args = []) + { + $parent = $this->owner->hasMethod('getParent') ? $this->owner->getParent() : null; + + return ViewTools::singleton()->processAttribute($value, $this->owner, $parent, $args); + } +} diff --git a/src/Extensions/Model/ImageDefaultsExtension.php b/src/Extensions/Model/ImageDefaultsExtension.php index 09a67c7..468b23a 100644 --- a/src/Extensions/Model/ImageDefaultsExtension.php +++ b/src/Extensions/Model/ImageDefaultsExtension.php @@ -101,7 +101,7 @@ public function updateCMSFields(FieldList $fields) public function updateFieldLabels(&$labels) { $labels['Style'] = _t(__CLASS__ . '.STYLE', 'Style'); - $labels['ImageDefaults'] = _t(__CLASS__ . '.IMAGEDEFAULTS', 'Image defaults'); + $labels['ImageDefaults'] = _t(__CLASS__ . '.IMAGEDEFAULTS', 'Image Defaults'); $labels['ImageDefaultResize'] = _t(__CLASS__ . '.DIMENSIONS', 'Dimensions'); $labels['ImageDefaultResizeMethod'] = _t(__CLASS__ . '.RESIZEMETHOD', 'Resize method'); $labels['ImageDefaultAlignment'] = _t(__CLASS__ . '.ALIGNMENT', 'Alignment'); diff --git a/src/Extensions/Model/ImageResizeExtension.php b/src/Extensions/Model/ImageResizeExtension.php index 5707f2a..2630699 100644 --- a/src/Extensions/Model/ImageResizeExtension.php +++ b/src/Extensions/Model/ImageResizeExtension.php @@ -88,7 +88,7 @@ public function updateCMSFields(FieldList $fields) public function updateFieldLabels(&$labels) { $labels['ImageResize'] = _t(__CLASS__ . '.DIMENSIONS', 'Dimensions'); - $labels['ImageResizeStyle'] = _t(__CLASS__ . '.IMAGERESIZE', 'Image resize'); + $labels['ImageResizeStyle'] = _t(__CLASS__ . '.IMAGERESIZE', 'Image Resize'); $labels['ImageResizeMethod'] = _t(__CLASS__ . '.RESIZEMETHOD', 'Resize method'); } diff --git a/src/Extensions/Model/LinkToExtension.php b/src/Extensions/Model/LinkToExtension.php index 00e425a..ba8c307 100644 --- a/src/Extensions/Model/LinkToExtension.php +++ b/src/Extensions/Model/LinkToExtension.php @@ -163,7 +163,7 @@ public function updateFieldLabels(&$labels) $labels['URL'] = _t(__CLASS__ . '.URL', 'URL'); $labels['None'] = _t(__CLASS__ . '.NONE', 'None'); $labels['Page'] = _t(__CLASS__ . '.PAGE', 'Page'); - $labels['LinkTo'] = _t(__CLASS__ . '.LINKTO', 'Link to'); + $labels['LinkTo'] = _t(__CLASS__ . '.LINKTO', 'Link To'); $labels['LinkURL'] = _t(__CLASS__ . '.LINKURL', 'Link URL'); $labels['LinkPageID'] = _t(__CLASS__ . '.LINKPAGE', 'Link page'); $labels['LinkOptions'] = _t(__CLASS__ . '.LINK', 'Link'); @@ -179,9 +179,13 @@ public function getLinkAttributes() { $attributes = [ 'href' => $this->owner->Link, - 'title' => $this->owner->Title + 'title' => $this->owner->LinkTitle ]; + if ($class = $this->owner->LinkClass) { + $attributes['class'] = $class; + } + if ($this->owner->OpenLinkInNewTab) { $attributes['target'] = '_blank'; } @@ -189,6 +193,36 @@ public function getLinkAttributes() return $attributes; } + /** + * Answers the title for the link. + * + * @return string + */ + public function getLinkTitle() + { + return $this->owner->Title; + } + + /** + * Answers a string of link class names for the template. + * + * @return string + */ + public function getLinkClass() + { + return ViewTools::singleton()->array2att($this->owner->getLinkClassNames()); + } + + /** + * Answers an array of link class names for the template. + * + * @return array + */ + public function getLinkClassNames() + { + return []; + } + /** * Answers a string of attributes for a link. * @@ -225,6 +259,16 @@ public function hasLink() return (boolean) $this->owner->getLink(); } + /** + * Answers true if the extended object has a link page. + * + * @return boolean + */ + public function hasLinkPage() + { + return $this->owner->LinkPage()->isInDB(); + } + /** * Answers true if the link is to a page. * diff --git a/src/Extensions/PageExtension.php b/src/Extensions/PageExtension.php index 19cb603..c133e7e 100644 --- a/src/Extensions/PageExtension.php +++ b/src/Extensions/PageExtension.php @@ -29,6 +29,8 @@ use SilverWare\Model\Layout; use SilverWare\Model\Link; use SilverWare\Model\Template; +use SilverWare\Tools\ViewTools; +use SilverWare\View\GridAware; use Page; /** @@ -42,6 +44,8 @@ */ class PageExtension extends DataExtension implements PermissionProvider { + use GridAware; + /** * Defines the has-one associations for the extended object. * @@ -313,6 +317,30 @@ public function getDefaultTemplate() return SiteConfig::current_site_config()->getTemplateForPage($this->owner); } + /** + * Answers a string of content class names for the HTML template. + * + * @return string + */ + public function getContentClass() + { + return ViewTools::singleton()->array2att($this->owner->getContentClassNames()); + } + + /** + * Answers an array of content class names for the HTML template. + * + * @return array + */ + public function getContentClassNames() + { + $classes = $this->styles('content', 'content.typography'); + + $this->owner->extend('updateContentClassNames', $classes); + + return $classes; + } + /** * Answers an array of custom CSS required for the template. * @@ -324,11 +352,9 @@ public function getCustomCSS() $css = []; - // Merge Template Custom CSS: + // Apply Extensions: - if ($template = $this->owner->getPageTemplate()) { - $css = array_merge($css, $template->getCustomCSS()); - } + $this->owner->extend('updateCustomCSS', $css); // Answer CSS Array: @@ -382,7 +408,7 @@ public function getBodyAttributesHTML() */ public function getFieldFromHierarchy($name) { - return ($value = $this->owner->$name) ? $value : $this->owner->getFieldFromParent($name); + return !is_null($this->owner->$name) ? $this->owner->$name : $this->owner->getFieldFromParent($name); } /** diff --git a/src/Extensions/Style/ButtonStyle.php b/src/Extensions/Style/ButtonStyle.php new file mode 100644 index 0000000..5df8c17 --- /dev/null +++ b/src/Extensions/Style/ButtonStyle.php @@ -0,0 +1,230 @@ +=5.6.0 + * + * For full copyright and license information, please view the + * LICENSE.md file that was distributed with this source code. + * + * @package SilverWare\Extensions\Style + * @author Colin Tucker + * @copyright 2017 Praxis Interactive + * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause + * @link https://github.com/praxisnetau/silverware + */ + +namespace SilverWare\Extensions\Style; + +use SilverStripe\Core\Config\Config; +use SilverStripe\Forms\DropdownField; +use SilverStripe\Forms\FieldList; +use SilverWare\Extensions\StyleExtension; +use SilverWare\Forms\FieldSection; + +/** + * A style extension which adds button styles to the extended object. + * + * @package SilverWare\Extensions\Style + * @author Colin Tucker + * @copyright 2017 Praxis Interactive + * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause + * @link https://github.com/praxisnetau/silverware + */ +class ButtonStyle extends StyleExtension +{ + /** + * Define size constants. + */ + const SIZE_SMALL = 'small'; + const SIZE_MEDIUM = 'medium'; + const SIZE_LARGE = 'large'; + + /** + * Define style constants. + */ + const STYLE_FILLED = 'filled'; + const STYLE_OUTLINE = 'outline'; + + /** + * Maps field names to field types for the extended object. + * + * @var array + * @config + */ + private static $db = [ + 'ButtonType' => 'Varchar(16)', + 'ButtonSize' => 'Varchar(16)', + 'ButtonStyle' => 'Varchar(16)' + ]; + + /** + * Defines the default values for the fields of this object. + * + * @var array + * @config + */ + private static $defaults = [ + 'ButtonType' => 'primary', + 'ButtonSize' => 'medium', + 'ButtonStyle' => 'filled' + ]; + + /** + * Updates the CMS fields of the extended object. + * + * @param FieldList $fields List of CMS fields from the extended object. + * + * @return void + */ + public function updateCMSFields(FieldList $fields) + { + // Update Field Objects (from parent): + + parent::updateCMSFields($fields); + + // Define Placeholder: + + $placeholder = _t(__CLASS__ . '.DROPDOWNDEFAULT', '(default)'); + + // Create Style Fields: + + $fields->addFieldsToTab( + 'Root.Style', + [ + FieldSection::create( + 'ButtonStyle', + $this->owner->fieldLabel('Button'), + [ + DropdownField::create( + 'ButtonType', + $this->owner->fieldLabel('ButtonType'), + $this->owner->getButtonTypeOptions() + ), + DropdownField::create( + 'ButtonSize', + $this->owner->fieldLabel('ButtonSize'), + $this->owner->getButtonSizeOptions() + ), + DropdownField::create( + 'ButtonStyle', + $this->owner->fieldLabel('ButtonStyle'), + $this->owner->getButtonStyleOptions() + ) + ] + ) + ] + ); + } + + /** + * Updates the field labels of the extended object. + * + * @param array $labels Array of field labels from the extended object. + * + * @return void + */ + public function updateFieldLabels(&$labels) + { + $labels['Button'] = _t(__CLASS__ . '.BUTTON', 'Button'); + $labels['ButtonType'] = _t(__CLASS__ . '.BUTTONTYPE', 'Button type'); + $labels['ButtonSize'] = _t(__CLASS__ . '.BUTTONSIZE', 'Button size'); + $labels['ButtonStyle'] = _t(__CLASS__ . '.BUTTONSTYLE', 'Button style'); + } + + /** + * Updates the given array of class names from the extended object. + * + * @param array $classes + * + * @return array + */ + public function updateClassNames(&$classes) + { + if (!$this->apply()) { + return; + } + + $classes[] = $this->style('button'); + + if ($class = $this->owner->ButtonTypeClass) { + $classes[] = $class; + } + + if ($class = $this->owner->ButtonSizeClass) { + $classes[] = $class; + } + } + + /** + * Answers the type class for the button. + * + * @return string + */ + public function getButtonTypeClass() + { + return $this->style('button', $this->owner->getButtonTypeStyle()); + } + + /** + * Answers the type style for the button. + * + * @return string + */ + public function getButtonTypeStyle() + { + if ($this->owner->ButtonStyle == self::STYLE_OUTLINE) { + return sprintf('outline-%s', $this->owner->ButtonType); + } + + return $this->owner->ButtonType; + } + + /** + * Answers the size class for the button. + * + * @return string + */ + public function getButtonSizeClass() + { + return $this->style('button', $this->owner->ButtonSize); + } + + /** + * Answers an array of options for the button type field. + * + * @return array + */ + public function getButtonTypeOptions() + { + return Config::inst()->get(static::class, 'button_types'); + } + + /** + * Answers an array of options for the button size field. + * + * @return array + */ + public function getButtonSizeOptions() + { + return [ + self::SIZE_SMALL => _t(__CLASS__ . '.SMALL', 'Small'), + self::SIZE_MEDIUM => _t(__CLASS__ . '.MEDIUM', 'Medium'), + self::SIZE_LARGE => _t(__CLASS__ . '.LARGE', 'Large') + ]; + } + + /** + * Answers an array of options for the button style field. + * + * @return array + */ + public function getButtonStyleOptions() + { + return [ + self::STYLE_FILLED => _t(__CLASS__ . '.FILLED', 'Filled'), + self::STYLE_OUTLINE => _t(__CLASS__ . '.OUTLINE', 'Outline') + ]; + } +} diff --git a/src/Extensions/StyleExtension.php b/src/Extensions/StyleExtension.php index 82810c8..498b932 100644 --- a/src/Extensions/StyleExtension.php +++ b/src/Extensions/StyleExtension.php @@ -20,6 +20,7 @@ use SilverStripe\Forms\FieldList; use SilverStripe\ORM\DataExtension; use SilverStripe\View\SSViewer; +use SilverWare\Tools\ViewTools; use SilverWare\View\GridAware; /** @@ -83,7 +84,7 @@ public function updateCustomCSS(&$css) $template = $this->owner->getStyleExtensionTemplate(static::class); if (SSViewer::hasTemplate($template)) { - $css[] = $this->owner->renderWith($template); + $css = ViewTools::singleton()->renderCSS($this->owner, $template, $css); } } diff --git a/src/Forms/ViewportsField.php b/src/Forms/ViewportsField.php index 2cbc43a..a5b699d 100644 --- a/src/Forms/ViewportsField.php +++ b/src/Forms/ViewportsField.php @@ -17,10 +17,10 @@ namespace SilverWare\Forms; +use SilverStripe\Forms\CompositeField; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\FieldGroup; use SilverStripe\Forms\FieldList; -use SilverStripe\Forms\FormField; use SilverStripe\Forms\TextField; use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\FieldType\DBField; @@ -30,7 +30,7 @@ use ArrayAccess; /** - * An extension of the form field class for a viewports field. + * An extension of the composite field class for a viewports field. * * @package SilverWare\Forms * @author Colin Tucker @@ -38,7 +38,7 @@ * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause * @link https://github.com/praxisnetau/silverware */ -class ViewportsField extends FormField +class ViewportsField extends CompositeField { /** * Source options for viewport fields. @@ -47,13 +47,6 @@ class ViewportsField extends FormField */ protected $source = []; - /** - * Array of viewport fields. - * - * @var array - */ - protected $fields = []; - /** * Labels for viewport fields. * @@ -106,10 +99,18 @@ class ViewportsField extends FormField */ public function __construct($name, $title = null, $source = [], $value = null) { + // Construct Parent: + + parent::__construct(); + // Define Name: $this->setName($name); + // Define Title: + + $this->setTitle($title); + // Define Source: $this->setSource($source); @@ -118,47 +119,21 @@ public function __construct($name, $title = null, $source = [], $value = null) $this->buildViewportFields(); - // Define Empty String: + // Define Value: - $this->setEmptyString(_t(__CLASS__ . '.DROPDOWNDEFAULT', '(default)')); + if ($value !== null) { + $this->setValue($value); + } - // Construct Parent: + // Define Empty String: - parent::__construct($name, $title, $value); + $this->setEmptyString(_t(__CLASS__ . '.DROPDOWNDEFAULT', '(default)')); // Update Viewport Fields: $this->updateViewportFields(); } - /** - * Called when the receiver is cloned (ensures associated objects are also cloned). - * - * @return void - */ - public function __clone() - { - foreach ($this->fields as $viewport => $field) { - $this->fields[$viewport] = clone $field; - } - } - - /** - * Defines the form for the receiver and viewport fields. - * - * @param Form $form - * - * @return $this - */ - public function setForm($form) - { - foreach ($this->fields as $field) { - $field->setForm($form); - } - - return parent::setForm($form); - } - /** * Defines the value of the receiver. * @@ -179,7 +154,9 @@ public function setValue($value, $data = null) // Define from Array: - foreach ($this->fields as $viewport => $field) { + foreach ($this->getViewports() as $viewport) { + + $field = $this->getViewportField($viewport); if (isset($value[$viewport])) { $field->setValue($value[$viewport]); @@ -191,8 +168,8 @@ public function setValue($value, $data = null) // Define from Field: - foreach ($this->fields as $viewport => $field) { - $field->setValue($value->{$viewport}); + foreach ($this->getViewports() as $viewport) { + $this->getViewportField($viewport)->setValue($value->{$viewport}); } } @@ -226,6 +203,16 @@ public function saveInto(DataObjectInterface $record) } } + /** + * Answers true to specify that data is contained within this field. + * + * @return boolean + */ + public function hasData() + { + return true; + } + /** * Defines the value of the source attribute. * @@ -310,24 +297,6 @@ public function getUseTextInput() return $this->useTextInput; } - /** - * Returns a read-only version of the receiver. - * - * @return ViewportsField - */ - public function performReadonlyTransformation() - { - $clone = clone $this; - - foreach ($clone->fields as $viewport => $field) { - $clone->fields[$viewport] = $field->performReadonlyTransformation(); - } - - $clone->setReadonly(true); - - return $clone; - } - /** * Defines the label for the specified viewport. * @@ -370,8 +339,8 @@ public function getViewportValues() { $values = []; - foreach ($this->fields as $viewport => $field) { - $values[$viewport] = $field->dataValue(); + foreach ($this->getViewports() as $viewport) { + $values[$viewport] = $this->getViewportField($viewport)->dataValue(); } return $values; @@ -394,7 +363,7 @@ public function setHiddenViewports() foreach ($viewports as $viewport) { - if (isset($this->fields[$viewport])) { + if ($this->getViewportField($viewport)) { $this->hidden[$viewport] = true; } @@ -450,7 +419,41 @@ public function getEmptyString() */ public function getFields() { - return FieldList::create($this->fields); + return $this->children; + } + + /** + * Answers the child field for the specified viewport. + * + * @param string $viewport + * + * @return FormField + */ + public function getViewportField($viewport) + { + return $this->children->dataFieldByName($this->getViewportName($viewport)); + } + + /** + * Answers the child field name for the specified viewport. + * + * @param string $viewport + * + * @return string + */ + public function getViewportName($viewport) + { + return sprintf('%s[%s]', $this->getName(), $viewport); + } + + /** + * Answers an array of the viewport field names. + * + * @return array + */ + public function getViewports() + { + return DBViewports::singleton()->getViewports(); } /** @@ -495,8 +498,8 @@ protected function getSourceAsArray($source) */ protected function buildViewportFields() { - foreach (DBViewports::singleton()->getViewports() as $viewport) { - $this->fields[$viewport] = $this->buildViewportField($viewport); + foreach ($this->getViewports() as $viewport) { + $this->push($this->buildViewportField($viewport)); } } @@ -512,14 +515,14 @@ protected function buildViewportField($viewport) if ($this->useTextInput) { $field = TextField::create( - sprintf('%s[%s]', $this->getName(), $viewport), + $this->getViewportName($viewport), $this->getViewportLabel($viewport) ); } else { $field = DropdownField::create( - sprintf('%s[%s]', $this->getName(), $viewport), + $this->getViewportName($viewport), $this->getViewportLabel($viewport), $this->getSource() ); @@ -536,7 +539,13 @@ protected function buildViewportField($viewport) */ protected function updateViewportFields() { - foreach ($this->fields as $viewport => $field) { + foreach ($this->getViewports() as $viewport) { + + // Obtain Field: + + if (!($field = $this->getViewportField($viewport))) { + continue; + } // Update Title: diff --git a/src/Model/Button.php b/src/Model/Button.php new file mode 100644 index 0000000..6349639 --- /dev/null +++ b/src/Model/Button.php @@ -0,0 +1,124 @@ +=5.6.0 + * + * For full copyright and license information, please view the + * LICENSE.md file that was distributed with this source code. + * + * @package SilverWare\Model + * @author Colin Tucker + * @copyright 2017 Praxis Interactive + * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause + * @link https://github.com/praxisnetau/silverware + */ + +namespace SilverWare\Model; + +use SilverWare\Extensions\Model\LinkToExtension; +use SilverWare\Extensions\Style\ButtonStyle; +use SilverWare\Extensions\Style\CornerStyle; +use SilverWare\FontIcons\Extensions\FontIconExtension; + +/** + * An extension of the component class for a button. + * + * @package SilverWare\Model + * @author Colin Tucker + * @copyright 2017 Praxis Interactive + * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause + * @link https://github.com/praxisnetau/silverware + */ +class Button extends Component +{ + /** + * Human-readable singular name. + * + * @var string + * @config + */ + private static $singular_name = 'Button'; + + /** + * Human-readable plural name. + * + * @var string + * @config + */ + private static $plural_name = 'Buttons'; + + /** + * Description of this object. + * + * @var string + * @config + */ + private static $description = 'A component which represents a button'; + + /** + * Icon file for this object. + * + * @var string + * @config + */ + private static $icon = 'silverware/admin/client/dist/images/icons/Button.png'; + + /** + * Defines an ancestor class to hide from the admin interface. + * + * @var string + * @config + */ + private static $hide_ancestor = Component::class; + + /** + * Defines the allowed children for this object. + * + * @var array|string + * @config + */ + private static $allowed_children = 'none'; + + /** + * Defines the extension classes to apply to this object. + * + * @var array + * @config + */ + private static $extensions = [ + ButtonStyle::class, + CornerStyle::class, + LinkToExtension::class, + FontIconExtension::class + ]; + + /** + * Answers an array of HTML tag attributes for the object. + * + * @return array + */ + public function getAttributes() + { + return array_merge( + parent::getAttributes(), + $this->getLinkAttributes(), + $this->getButtonAttributes() + ); + } + + /** + * Answers an array of attributes for the button. + * + * @return array + */ + public function getButtonAttributes() + { + $attributes = ['role' => 'button']; + + $this->extend('updateButtonAttributes', $attributes); + + return $attributes; + } +} diff --git a/src/Model/Component.php b/src/Model/Component.php index 76e443e..7321c52 100644 --- a/src/Model/Component.php +++ b/src/Model/Component.php @@ -34,7 +34,6 @@ use SilverWare\Tools\ViewTools; use SilverWare\View\GridAware; use SilverWare\View\Renderable; -use SilverWare\View\RequireFiles; use SilverWare\View\ViewClasses; use Page; @@ -52,7 +51,6 @@ class Component extends SiteTree implements Flushable, PermissionProvider use GridAware; use Renderable; use ViewClasses; - use RequireFiles; /** * Human-readable singular name. @@ -255,33 +253,45 @@ public function fieldLabels($includerelations = true) } /** - * Defines the parent of the receiver. + * Defines the parent instance of the receiver. * - * @param SiteTree|int $item + * @param SiteTree $parent * - * @return void + * @return $this */ - public function setParent($item) + public function setParentInstance(SiteTree $parent) { - // Record Parent Instance: - - if ($item instanceof SiteTree) { - $this->parentInstance = $item; - } + $this->parentInstance = $parent; - // Call Parent Method: + $this->setStyleIDFrom($parent); - return parent::setParent($item); + return $this; } /** - * Answers the parent instance of the receiver (if available). + * Answers the parent of the receiver. * * @return SiteTree */ - public function getParentInstance() + public function getParent() { - return $this->parentInstance; + if ($this->parentInstance) { + return $this->parentInstance; + } + + return parent::getParent(); + } + + /** + * Overrides the method inherited from DataObject in order to answer parent instances (if applicable). + * + * @param string $componentName + * + * @return DataObject + */ + public function getComponent($componentName) + { + return ($componentName == 'Parent') ? $this->getParent() : parent::getComponent($componentName); } /** @@ -344,10 +354,16 @@ public function canCreate($member = null, $context = []) } else { - // Disallow Page as Parent: + // Disallow Page as Parent (except if explicitly allowed): if ($context['Parent'] instanceof Page) { - return false; + + $allowed_children = $context['Parent']->allowedChildren(); + + if (in_array(SiteTree::class, $allowed_children) || !in_array(static::class, $allowed_children)) { + return false; + } + } } @@ -495,6 +511,18 @@ public function getAllChildren() return $this->cacheAllChildren; } + /** + * Answers a list of all children within the receiver of the given class. + * + * @param string $class + * + * @return DataList + */ + public function getAllChildrenByClass($class) + { + return $this->getAllChildren()->filter('ClassName', ClassInfo::subclassesFor($class)); + } + /** * Answers a list of all components within the receiver. * diff --git a/src/Model/Template.php b/src/Model/Template.php index 71a7530..c8fc61d 100644 --- a/src/Model/Template.php +++ b/src/Model/Template.php @@ -84,32 +84,6 @@ class Template extends SectionHolder TemplateFolder::class ]; - /** - * Answers an array of custom CSS required for the template. - * - * @return array - */ - public function getCustomCSS() - { - // Obtain CSS Array: - - $css = parent::getCustomCSS(); - - // Merge CSS from Enabled Components: - - foreach ($this->getEnabledComponents() as $component) { - $css = array_merge($css, $component->getCustomCSS()); - } - - // Filter CSS Array: - - $css = array_filter($css); - - // Answer CSS Array: - - return $css; - } - /** * Renders the component for the HTML template. * diff --git a/src/Tools/ViewTools.php b/src/Tools/ViewTools.php index e17a63b..fa53ada 100644 --- a/src/Tools/ViewTools.php +++ b/src/Tools/ViewTools.php @@ -21,6 +21,7 @@ use SilverStripe\Core\Convert; use SilverStripe\Core\Injector\Injectable; use SilverStripe\View\Requirements; +use SilverStripe\View\SSViewer; use SilverStripe\View\ViewableData; /** @@ -218,7 +219,7 @@ public function processAttribute($value, ViewableData $object, ViewableData $par return $object->$field; - } elseif ($parent->hasField($field)) { + } elseif (is_object($parent) && $parent->hasField($field)) { // Finally, answer a field value from the given parent object: @@ -264,4 +265,27 @@ public function removeEmptyLines($string) { return preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $string); } + + /** + * Renders the given object using the given CSS template. + * + * @param ViewableData $object + * @param string $template + * @param array $css + * + * @return array + */ + public function renderCSS(ViewableData $object, $template, $css = []) + { + if (SSViewer::hasTemplate($template)) { + + return array_merge( + $css, + preg_split('/\r\n|\n|\r/', (string) $object->renderWith($template)) + ); + + } + + return $css; + } } diff --git a/src/View/Renderable.php b/src/View/Renderable.php index 7d41c85..8b02347 100644 --- a/src/View/Renderable.php +++ b/src/View/Renderable.php @@ -18,11 +18,14 @@ namespace SilverWare\View; use Psr\SimpleCache\CacheInterface; +use SilverStripe\CMS\Controllers\ContentController; use SilverStripe\Control\Director; use SilverStripe\Core\Convert; use SilverStripe\Core\Injector\Injector; use SilverStripe\Dev\Debug; +use SilverStripe\ORM\ArrayLib; use SilverStripe\ORM\DataObject; +use SilverStripe\View\Requirements; use SilverStripe\View\SSViewer; use SilverWare\Extensions\ControllerExtension; use SilverWare\Tools\ClassTools; @@ -39,6 +42,46 @@ */ trait Renderable { + /** + * An array of required JavaScript files. + * + * @var array + * @config + */ + private static $required_js = []; + + /** + * An array of required CSS files. + * + * @var array + * @config + */ + private static $required_css = []; + + /** + * An array of required themed JavaScript files. + * + * @var array + * @config + */ + private static $required_themed_js = []; + + /** + * An array of required themed CSS files. + * + * @var array + * @config + */ + private static $required_themed_css = []; + + /** + * An array of required JavaScript template files. + * + * @var array + * @config + */ + private static $required_js_templates = []; + /** * Extra classes for the HTML tag. * @@ -73,6 +116,154 @@ public static function flushRenderCache() return self::getRenderCache()->clear(); } + /** + * Loads the requirements for the object. + * + * @return void + */ + public function loadRequirements() + { + // Load Required Themed JavaScript: + + if (ContentController::config()->load_themed_js) { + + foreach ($this->getRequiredThemedJS() as $js) { + Requirements::themedJavascript($js); + } + + } + + // Load Required Themed CSS: + + if (ContentController::config()->load_themed_css) { + + foreach ($this->getRequiredThemedCSS() as $css => $media) { + Requirements::themedCSS($css, $media); + } + + } + + // Load Required JavaScript: + + if (ContentController::config()->load_js) { + + foreach ($this->getRequiredJS() as $js) { + Requirements::javascript($js); + } + + } + + // Load Required CSS: + + if (ContentController::config()->load_css) { + + foreach ($this->getRequiredCSS() as $css => $media) { + Requirements::css($css, $media); + } + + } + + // Load Required Custom CSS: + + if (ContentController::config()->load_custom_css) { + + if ($css = $this->getCustomCSSAsString()) { + Requirements::customCSS($css, $this->HTMLID); + } + + } + + // Load Required JavaScript Templates: + + foreach ($this->getRequiredJSTemplates() as $file => $params) { + ViewTools::singleton()->loadJSTemplate($file, $params['vars'], $params['id']); + } + } + + /** + * Answers an array of JavaScript files required by the object. + * + * @return array + */ + public function getRequiredJS() + { + $js = $this->config()->required_js; + + $this->extend('updateRequiredJS', $js); + + return $js; + } + + /** + * Answers an array of CSS files required by the object. + * + * @return array + */ + public function getRequiredCSS() + { + $css = $this->config()->required_css; + + $this->extend('updateRequiredCSS', $css); + + return $this->processCSSConfig($css); + } + + /** + * Answers an array of themed JavaScript files required by the object. + * + * @return array + */ + public function getRequiredThemedJS() + { + $js = $this->config()->required_themed_js; + + $this->extend('updateRequiredThemedJS', $js); + + return $js; + } + + /** + * Answers an array of themed CSS files required by the object. + * + * @return array + */ + public function getRequiredThemedCSS() + { + $css = $this->config()->required_themed_css; + + $this->extend('updateRequiredThemedCSS', $css); + + return $this->processCSSConfig($css); + } + + /** + * Answers an array of JavaScript templates required by the object. + * + * @return array + */ + public function getRequiredJSTemplates() + { + $js = $this->config()->required_js_templates; + + $this->extend('updateRequiredJSTemplates', $js); + + return $this->processJSTemplateConfig($js); + } + + /** + * Answers an array of variables required by a JavaScript template. + * + * @return array + */ + public function getJSVars() + { + if (in_array(Renderable::class, class_uses(self::class))) { + return ['HTMLID' => $this->getHTMLID(), 'CSSID' => $this->getCSSID()]; + } + + return []; + } + /** * Defines the value of the extra classes attribute. * @@ -325,13 +516,14 @@ public function getDefaultStyleID() } /** - * Defines the style ID of the receiver from the given data object. + * Defines the style ID of the receiver from the given data object and optional suffix. * * @param DataObject $object + * @param string $suffix * * @return $this */ - public function setStyleIDFrom(DataObject $object) + public function setStyleIDFrom(DataObject $object, $suffix = null) { $this->StyleID = sprintf( '%s_%s', @@ -339,6 +531,16 @@ public function setStyleIDFrom(DataObject $object) ClassTools::singleton()->getClassWithoutNamespace(get_class($this)) ); + if (!is_null($suffix)) { + $this->StyleID = sprintf( + '%s_%s', + $this->StyleID, + $suffix + ); + } + + $this->StyleID = $this->cleanStyleID($this->StyleID); + return $this; } @@ -402,7 +604,7 @@ public function getCustomCSS() $template = $this->getCustomCSSTemplate($class); if (SSViewer::hasTemplate($template)) { - $css = array_merge($css, preg_split('/\r\n|\n|\r/', $this->renderWith($template))); + $css = ViewTools::singleton()->renderCSS($this, $template, $css); } } @@ -420,6 +622,30 @@ public function getCustomCSS() return $css; } + /** + * Answers the custom CSS required for the template as a string. + * + * @return string + */ + public function getCustomCSSAsString() + { + // Create CSS String: + + $css = implode("\n", $this->getCustomCSS()); + + // Remove Empty Lines: + + $css = ViewTools::singleton()->removeEmptyLines($css); + + // Trim CSS String: + + $css = trim($css); + + // Answer CSS String: + + return $css; + } + /** * Answers the name of a template used to render custom CSS for the receiver. * @@ -513,6 +739,8 @@ public function getRenderCacheLifetime() */ public function forTemplate() { + // Render Object: + return $this->render(); } @@ -530,6 +758,10 @@ public function render($layout = null, $title = null) $html = ''; + // Load Requirements: + + $this->loadRequirements(); + // Obtain Cached HTML (if enabled): if ($this->isCacheEnabled()) { @@ -570,4 +802,90 @@ public function render($layout = null, $title = null) return $html; } + + /** + * Processes the given CSS config and answers an array suitable for loading requirements. + * + * @param array $config + * + * @return array + */ + protected function processCSSConfig($config) + { + if (!ArrayLib::is_associative($config)) { + return array_fill_keys(array_values($config), null); + } + + return $config; + } + + /** + * Processes the given JavaScript template config and answers an array suitable for loading requirements. + * + * @param array $config + * + * @return array + */ + protected function processJSTemplateConfig($config) + { + $templates = []; + + foreach ($config as $key => $value) { + + if (is_integer($key)) { + + $templates[$value] = [ + 'vars' => $this->getJSVars(), + 'id' => $this->getHTMLID() + ]; + + } else { + + $templates[$key] = [ + 'vars' => $this->getJSTemplateVars(array_shift($value)), + 'id' => $this->getJSTemplateID(array_shift($value)) + ]; + + } + + } + + return $templates; + } + + /** + * Answers an array of variables for a required JavaScript template. + * + * @param string|array $vars Array of variables or method name. + * + * @return array + */ + protected function getJSTemplateVars($vars) + { + if (is_array($vars)) { + return $vars; + } + + if ($this->hasMethod($vars)) { + return $this->{$vars}(); + } + + return []; + } + + /** + * Answers the ID for a required JavaScript template. + * + * @param string $id ID or method name. + * + * @return string + */ + protected function getJSTemplateID($id) + { + if ($this->hasMethod($id)) { + return $this->{$id}(); + } + + return $id; + } } diff --git a/src/View/RequireFiles.php b/src/View/RequireFiles.php deleted file mode 100644 index 503fcce..0000000 --- a/src/View/RequireFiles.php +++ /dev/null @@ -1,283 +0,0 @@ -=5.6.0 - * - * For full copyright and license information, please view the - * LICENSE.md file that was distributed with this source code. - * - * @package SilverWare\View - * @author Colin Tucker - * @copyright 2017 Praxis Interactive - * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @link https://github.com/praxisnetau/silverware - */ - -namespace SilverWare\View; - -use SilverStripe\Core\Injector\Injector; -use SilverStripe\ORM\ArrayLib; -use SilverStripe\View\Requirements; -use SilverWare\Tools\ViewTools; - -/** - * Allows an object to use the configuration system to require files. - * - * @package SilverWare\View - * @author Colin Tucker - * @copyright 2017 Praxis Interactive - * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause - * @link https://github.com/praxisnetau/silverware - */ -trait RequireFiles -{ - /** - * An array of required JavaScript files. - * - * @var array - * @config - */ - private static $required_js = []; - - /** - * An array of required CSS files. - * - * @var array - * @config - */ - private static $required_css = []; - - /** - * An array of required themed JavaScript files. - * - * @var array - * @config - */ - private static $required_themed_js = []; - - /** - * An array of required themed CSS files. - * - * @var array - * @config - */ - private static $required_themed_css = []; - - /** - * An array of required JavaScript template files. - * - * @var array - * @config - */ - private static $required_js_templates = []; - - /** - * Loads the requirements for the object. - * - * @return void - */ - public function loadRequirements() - { - // Load Required CSS: - - foreach ($this->getRequiredCSS() as $css => $media) { - Requirements::css($css, $media); - } - - // Load Required Themed CSS: - - foreach ($this->getRequiredThemedCSS() as $css => $media) { - Requirements::themedCSS($css, $media); - } - - // Load Required JavaScript: - - foreach ($this->getRequiredJS() as $js) { - Requirements::javascript($js); - } - - // Load Required Themed JavaScript: - - foreach ($this->getRequiredThemedJS() as $js) { - Requirements::themedJavascript($js); - } - - // Load Required JavaScript Templates: - - foreach ($this->getRequiredJSTemplates() as $file => $params) { - ViewTools::singleton()->loadJSTemplate($file, $params['vars'], $params['id']); - } - } - - /** - * Answers an array of JavaScript files required by the object. - * - * @return array - */ - public function getRequiredJS() - { - $js = $this->config()->required_js; - - $this->extend('updateRequiredJS', $js); - - return $js; - } - - /** - * Answers an array of CSS files required by the object. - * - * @return array - */ - public function getRequiredCSS() - { - $css = $this->config()->required_css; - - $this->extend('updateRequiredCSS', $css); - - return $this->processCSSConfig($css); - } - - /** - * Answers an array of themed JavaScript files required by the object. - * - * @return array - */ - public function getRequiredThemedJS() - { - $js = $this->config()->required_themed_js; - - $this->extend('updateRequiredThemedJS', $js); - - return $js; - } - - /** - * Answers an array of themed CSS files required by the object. - * - * @return array - */ - public function getRequiredThemedCSS() - { - $css = $this->config()->required_themed_css; - - $this->extend('updateRequiredThemedCSS', $css); - - return $this->processCSSConfig($css); - } - - /** - * Answers an array of JavaScript templates required by the object. - * - * @return array - */ - public function getRequiredJSTemplates() - { - $js = $this->config()->required_js_templates; - - $this->extend('updateRequiredJSTemplates', $js); - - return $this->processJSTemplateConfig($js); - } - - /** - * Answers an array of variables required by a JavaScript template. - * - * @return array - */ - public function getJSVars() - { - if (in_array(Renderable::class, class_uses(self::class))) { - return ['HTMLID' => $this->getHTMLID(), 'CSSID' => $this->getCSSID()]; - } - - return []; - } - - /** - * Processes the given CSS config and answers an array suitable for loading requirements. - * - * @param array $config - * - * @return array - */ - protected function processCSSConfig($config) - { - if (!ArrayLib::is_associative($config)) { - return array_fill_keys(array_values($config), null); - } - - return $config; - } - - /** - * Processes the given JavaScript template config and answers an array suitable for loading requirements. - * - * @param array $config - * - * @return array - */ - protected function processJSTemplateConfig($config) - { - $templates = []; - - foreach ($config as $key => $value) { - - if (is_integer($key)) { - - $templates[$value] = [ - 'vars' => $this->getJSVars(), - 'id' => $this->getHTMLID() - ]; - - } else { - - $templates[$key] = [ - 'vars' => $this->getJSTemplateVars(array_shift($value)), - 'id' => $this->getJSTemplateID(array_shift($value)) - ]; - - } - - } - - return $templates; - } - - /** - * Answers an array of variables for a required JavaScript template. - * - * @param string|array $vars Array of variables or method name. - * - * @return array - */ - protected function getJSTemplateVars($vars) - { - if (is_array($vars)) { - return $vars; - } - - if ($this->hasMethod($vars)) { - return $this->{$vars}(); - } - - return []; - } - - /** - * Answers the ID for a required JavaScript template. - * - * @param string $id ID or method name. - * - * @return string - */ - protected function getJSTemplateID($id) - { - if ($this->hasMethod($id)) { - return $this->{$id}(); - } - - return $id; - } -} diff --git a/templates/SilverWare/Components/BaseListComponent/Includes/Alerts.ss b/templates/SilverWare/Components/BaseListComponent/Includes/Alerts.ss index 97341f1..9d82cda 100644 --- a/templates/SilverWare/Components/BaseListComponent/Includes/Alerts.ss +++ b/templates/SilverWare/Components/BaseListComponent/Includes/Alerts.ss @@ -1,6 +1,6 @@ -<% if $ParentInstance.HasListAlerts %> +<% if $Parent.HasListAlerts %>
- <% loop $ParentInstance.ListAlertsData %> + <% loop $Parent.ListAlertsData %> <% include Alert Type=$Type, Icon=$Icon, Text=$Text.RAW %> <% end_loop %>
diff --git a/templates/SilverWare/Components/FeatureComponent.ss b/templates/SilverWare/Components/FeatureComponent.ss index 617a64c..e6b1967 100644 --- a/templates/SilverWare/Components/FeatureComponent.ss +++ b/templates/SilverWare/Components/FeatureComponent.ss @@ -1,4 +1,5 @@ diff --git a/templates/SilverWare/Components/FeatureComponent/Includes/Footer.ss b/templates/SilverWare/Components/FeatureComponent/Includes/Footer.ss index 2d5912c..145db74 100644 --- a/templates/SilverWare/Components/FeatureComponent/Includes/Footer.ss +++ b/templates/SilverWare/Components/FeatureComponent/Includes/Footer.ss @@ -1,5 +1,5 @@ <% if $FooterShown %>
- <% include Button Tag='a', HREF=$FeaturedPage.MetaLink, Text=$ButtonLabel %> + <% include Button Tag='a', HREF=$FeatureLink, Text=$ButtonLabel %>
<% end_if %> diff --git a/templates/SilverWare/Components/FeatureComponent/Includes/Header.ss b/templates/SilverWare/Components/FeatureComponent/Includes/Header.ss index 07b0205..b7c9210 100644 --- a/templates/SilverWare/Components/FeatureComponent/Includes/Header.ss +++ b/templates/SilverWare/Components/FeatureComponent/Includes/Header.ss @@ -1,8 +1,8 @@ <% if $HeaderShown %>
<$HeadingTag class="$HeadingClass"> - <% if $LinkHeading %> - $HeadingText + <% if $HeadingLinked %> + $HeadingText <% else %> $HeadingText <% end_if %> diff --git a/templates/SilverWare/Extensions/Model/DetailFieldsExtension/Details.ss b/templates/SilverWare/Extensions/Model/DetailFieldsExtension/Details.ss new file mode 100644 index 0000000..d6de715 --- /dev/null +++ b/templates/SilverWare/Extensions/Model/DetailFieldsExtension/Details.ss @@ -0,0 +1,13 @@ +<% if $HasDetailFields %> +
+ <{$DetailFieldsHeadingTag}>{$DetailFieldsHeadingText} +
+ <% loop $DetailFields %> + <% if $Text %> +
<% include Icon Name=$Icon, FixedWidth=1 %>$Name
+
$Text.RAW
+ <% end_if %> + <% end_loop %> +
+
+<% end_if %> diff --git a/templates/SilverWare/Lists/ListItem.ss b/templates/SilverWare/Lists/ListItem.ss index 7c23bbb..83e3923 100644 --- a/templates/SilverWare/Lists/ListItem.ss +++ b/templates/SilverWare/Lists/ListItem.ss @@ -1,6 +1,6 @@
<% include SilverWare\Lists\ListItem\Image %> -
+
<% include SilverWare\Lists\ListItem\Header %> <% include SilverWare\Lists\ListItem\Details %> <% include SilverWare\Lists\ListItem\Summary %> diff --git a/templates/SilverWare/Model/Button.ss b/templates/SilverWare/Model/Button.ss new file mode 100644 index 0000000..496fc21 --- /dev/null +++ b/templates/SilverWare/Model/Button.ss @@ -0,0 +1 @@ +$FontIconTag$Title