From d3e515f9c1b33d591bc4dcb3547a2564e40b85b9 Mon Sep 17 00:00:00 2001 From: Colin Tucker Date: Mon, 8 May 2017 10:15:04 +1000 Subject: [PATCH] Numerous fixes and improvements --- _config/controller.yml | 2 + _config/model.yml | 16 +- _config/styles.yml | 5 + admin/client/dist/styles/bundle.css | 2 +- .../src/styles/forms/ViewportsField.scss | 9 +- client/dist/styles/bundle.css | 2 +- .../src/styles/components/ListComponent.scss | 2 + composer.json | 2 +- package.json | 2 +- src/Components/BaseListComponent.php | 157 +++++++++ src/Components/ListComponent.php | 72 +---- src/Dev/FixtureBlueprint.php | 97 ++++-- src/Extensions/ConfigExtension.php | 20 +- src/Extensions/ControllerExtension.php | 69 +++- src/Extensions/Lists/ListSourceExtension.php | 61 +++- src/Extensions/Model/MetaDataExtension.php | 300 +++++++++++++----- src/Extensions/PageExtension.php | 22 ++ src/Forms/ViewportsField.php | 117 +++++-- src/Grid/Framework.php | 34 ++ src/Lists/ListItem.php | 69 +--- src/Model/Component.php | 43 +++ src/Model/Template.php | 26 ++ src/ORM/FieldType/DBViewports.php | 13 + src/Tools/ViewTools.php | 85 +++++ .../Lists/ListItem/Includes/Footer.ss | 2 +- yarn.lock | 65 ++-- 26 files changed, 978 insertions(+), 316 deletions(-) create mode 100644 src/Components/BaseListComponent.php diff --git a/_config/controller.yml b/_config/controller.yml index 468dfcc..1ac19c5 100644 --- a/_config/controller.yml +++ b/_config/controller.yml @@ -32,6 +32,7 @@ SilverStripe\CMS\Controllers\ContentController: load_css: true load_themed_js: true load_themed_css: false + load_custom_css: true load_component_requirements: true disable_cache: true @@ -48,5 +49,6 @@ SilverStripe\CMS\Controllers\ContentController: load_css: false load_themed_js: true load_themed_css: true + load_custom_css: true load_component_requirements: false disable_cache: false diff --git a/_config/model.yml b/_config/model.yml index a88d391..804f801 100644 --- a/_config/model.yml +++ b/_config/model.yml @@ -25,11 +25,19 @@ SilverWare\Dev\FixtureFactory: SilverWare\Dev\FixtureBlueprint: default_parents: - SilverWare\Model\Template: `\SilverWare\Folders\TemplateFolder::find()->ID` - SilverWare\Model\Layout: `\SilverWare\Folders\LayoutFolder::find()->ID` - SilverWare\Model\Panel: `\SilverWare\Folders\PanelFolder::find()->ID` + SilverWare\Model\Template: + class: SilverWare\Folders\TemplateFolder + method: find + SilverWare\Model\Layout: + class: SilverWare\Folders\LayoutFolder + method: find + SilverWare\Model\Panel: + class: SilverWare\Folders\PanelFolder + method: find default_relations: - SilverStripe\SiteConfig\SiteConfig: `\SilverStripe\SiteConfig\SiteConfig::current_site_config()->ID` + SilverStripe\SiteConfig\SiteConfig: + class: SilverStripe\SiteConfig\SiteConfig + method: current_site_config default_identifiers: SilverWare\Model\PageType: PageClass verbose: false diff --git a/_config/styles.yml b/_config/styles.yml index b58eaf0..626442f 100644 --- a/_config/styles.yml +++ b/_config/styles.yml @@ -5,6 +5,11 @@ Name: silverware-styles # Bootstrap Styles: SilverWare\Grid\Frameworks\Bootstrap\Framework: + breakpoints: + small: 576px + medium: 768px + large: 992px + huge: 1200px style_mappings: align: left: left diff --git a/admin/client/dist/styles/bundle.css b/admin/client/dist/styles/bundle.css index cfa4006..7c2c576 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}.field .chosen-container-single .chosen-default{color:#999}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.autocomplete .value-wrapper{margin-top:.5em}.field.autocomplete .value-wrapper .clear{display:none}.field.autocomplete .value-wrapper.has-value>.clear{display:inline}.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{min-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.form-group.hidden{display:none}.field .chosen-container-single .chosen-default{color:#999}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.autocomplete .value-wrapper{margin-top:.5em}.field.autocomplete .value-wrapper .clear{display:none}.field.autocomplete .value-wrapper.has-value>.clear{display:inline}.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/styles/forms/ViewportsField.scss b/admin/client/src/styles/forms/ViewportsField.scss index 40a835e..60b02a9 100644 --- a/admin/client/src/styles/forms/ViewportsField.scss +++ b/admin/client/src/styles/forms/ViewportsField.scss @@ -3,8 +3,13 @@ .field { - &.viewports .dropdown { - min-width: 140px; + &.viewports { + + .text, .dropdown { + min-width: 140px; + max-width: 140px; + } + } } diff --git a/client/dist/styles/bundle.css b/client/dist/styles/bundle.css index 61d3fc5..2e05cee 100644 --- a/client/dist/styles/bundle.css +++ b/client/dist/styles/bundle.css @@ -1 +1 @@ -.listcomponent .items>article.item{margin-bottom:2rem}.listcomponent .items>article.item>div.image{margin-bottom:1rem}.listcomponent .items>article.item>div.image a{display:block}.listcomponent .items>article.item>section.content header a{color:inherit}.listcomponent .items>article.item>section.content div.details{color:#636c72}.listcomponent .items>article.item>section.content div.details a{color:inherit}.listcomponent .items>article.item>section.content div.details>span{margin-right:.5rem}.listcomponent .items>article.item>section.content footer{margin-top:1rem}.listcomponent .items>article.item>section.content>div{margin-bottom:.5rem}.listcomponent .items>article.item:last-child{margin-bottom:0}@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{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{order:1;margin-right:1rem}}.pagecomponent.page-title-hidden .content-container>article>header{display:none}.scrolltotopbutton{color:#fff;background-color:#139fda;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:focus{color:#fff}.scrolltotopbutton:hover{color:#fff;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}} \ No newline at end of file +.listcomponent .items>article.item{margin-bottom:2rem}.listcomponent .items>article.item>div.image{margin-bottom:1rem}.listcomponent .items>article.item>div.image a{display:block}.listcomponent .items>article.item>section.content header a{color:inherit}.listcomponent .items>article.item>section.content div.details{color:#636c72}.listcomponent .items>article.item>section.content div.details a{color:inherit}.listcomponent .items>article.item>section.content div.details>span{margin-right:.5rem}.listcomponent .items>article.item>section.content footer{margin-top:1rem}.listcomponent .items>article.item>section.content>div{margin-bottom:.5rem}.listcomponent .items>article.item:last-child{margin-bottom:0}@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}}.pagecomponent.page-title-hidden .content-container>article>header{display:none}.scrolltotopbutton{color:#fff;background-color:#139fda;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:focus{color:#fff}.scrolltotopbutton:hover{color:#fff;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}} \ No newline at end of file diff --git a/client/src/styles/components/ListComponent.scss b/client/src/styles/components/ListComponent.scss index c6076f7..7ebd1c5 100644 --- a/client/src/styles/components/ListComponent.scss +++ b/client/src/styles/components/ListComponent.scss @@ -91,6 +91,7 @@ } > section.content { + flex: 1; order: 2; margin-left: $spacer; } @@ -105,6 +106,7 @@ } > section.content { + flex: 1; order: 1; margin-right: $spacer; } diff --git a/composer.json b/composer.json index 5198d25..7ca9374 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.1.x-dev" + "dev-master": "0.2.x-dev" }, "installer-name": "silverware" }, diff --git a/package.json b/package.json index b545e0b..1546378 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "silverware", - "version": "0.1.0", + "version": "0.2.0", "description": "SilverWare Component Framework.", "homepage": "https://github.com/praxisnetau/silverware", "keywords": [ diff --git a/src/Components/BaseListComponent.php b/src/Components/BaseListComponent.php new file mode 100644 index 0000000..d44ebb6 --- /dev/null +++ b/src/Components/BaseListComponent.php @@ -0,0 +1,157 @@ +=5.6.0 + * + * For full copyright and license information, please view the + * LICENSE.md file that was distributed with this source code. + * + * @package SilverWare\Components + * @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\Components; + +use SilverStripe\Forms\CheckboxField; +use SilverStripe\Forms\CompositeField; +use SilverStripe\Forms\DropdownField; +use SilverWare\Extensions\Lists\ListSourceExtension; +use SilverWare\Extensions\Model\ImageResizeExtension; + +/** + * An extension of the base component class for a base list component. + * + * @package SilverWare\Components + * @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 BaseListComponent extends BaseComponent +{ + /** + * Define image link constants. + */ + const IMAGE_LINK_ITEM = 'item'; + const IMAGE_LINK_FILE = 'file'; + + /** + * Maps field names to field types for this object. + * + * @var array + * @config + */ + private static $db = [ + 'ImageLinksTo' => 'Varchar(8)', + 'LinkImages' => 'Boolean' + ]; + + /** + * Defines the default values for the fields of this object. + * + * @var array + * @config + */ + private static $defaults = [ + 'ImageLinksTo' => 'item', + 'LinkImages' => 1 + ]; + + /** + * Defines the extension classes to apply to this object. + * + * @var array + * @config + */ + private static $extensions = [ + ListSourceExtension::class, + ImageResizeExtension::class + ]; + + /** + * Answers a list of field objects for the CMS interface. + * + * @return FieldList + */ + public function getCMSFields() + { + // Obtain Field Objects (from parent): + + $fields = parent::getCMSFields(); + + // Create Options Fields: + + $fields->addFieldsToTab( + 'Root.Options', + [ + CompositeField::create([ + DropdownField::create( + 'ImageLinksTo', + $this->fieldLabel('ImageLinksTo'), + $this->getImageLinksToOptions() + ), + CheckboxField::create( + 'LinkImages', + $this->fieldLabel('LinkImages') + ) + ])->setName('ListImageOptions')->setTitle($this->fieldLabel('ListImageOptions')) + ] + ); + + // Answer Field Objects: + + return $fields; + } + + /** + * Answers the labels for the fields of the receiver. + * + * @param boolean $includerelations Include labels for relations. + * + * @return array + */ + public function fieldLabels($includerelations = true) + { + // Obtain Field Labels (from parent): + + $labels = parent::fieldLabels($includerelations); + + // Define Field Labels: + + $labels['LinkImages'] = _t(__CLASS__ . '.LINKIMAGES', 'Link images'); + $labels['ImageLinksTo'] = _t(__CLASS__ . '.IMAGELINKSTO', 'Image links to'); + $labels['ListImageOptions'] = _t(__CLASS__ . '.LISTIMAGES', 'List images'); + + // Answer Field Labels: + + return $labels; + } + + /** + * Answers a message string to be shown when no data is available. + * + * @return string + */ + public function getNoDataMessage() + { + return _t(__CLASS__ . '.NODATAAVAILABLE', 'No data available.'); + } + + /** + * Answers an array of options for the image links to field. + * + * @return array + */ + public function getImageLinksToOptions() + { + return [ + self::IMAGE_LINK_ITEM => _t(__CLASS__ . '.ITEM', 'Item'), + self::IMAGE_LINK_FILE => _t(__CLASS__ . '.FILE', 'File') + ]; + } +} diff --git a/src/Components/ListComponent.php b/src/Components/ListComponent.php index 589e103..4c49fac 100644 --- a/src/Components/ListComponent.php +++ b/src/Components/ListComponent.php @@ -21,7 +21,6 @@ use SilverStripe\Forms\CompositeField; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\TextField; -use SilverWare\Extensions\Lists\ListSourceExtension; /** * An extension of the base component class for a list component. @@ -32,7 +31,7 @@ * @license https://opensource.org/licenses/BSD-3-Clause BSD-3-Clause * @link https://github.com/praxisnetau/silverware */ -class ListComponent extends BaseComponent +class ListComponent extends BaseListComponent { /** * Define show constants. @@ -48,12 +47,6 @@ class ListComponent extends BaseComponent const ALIGN_RIGHT = 'right'; const ALIGN_STAGGER = 'stagger'; - /** - * Define image link constants. - */ - const IMAGE_LINK_ITEM = 'item'; - const IMAGE_LINK_FILE = 'file'; - /** * Human-readable singular name. * @@ -92,7 +85,7 @@ class ListComponent extends BaseComponent * @var string * @config */ - private static $hide_ancestor = BaseComponent::class; + private static $hide_ancestor = BaseListComponent::class; /** * Maps field names to field types for this object. @@ -110,10 +103,8 @@ class ListComponent extends BaseComponent 'DateFormat' => 'Varchar(32)', 'HeadingLevel' => 'Varchar(2)', 'ImageAlignment' => 'Varchar(16)', - 'ImageLinksTo' => 'Varchar(8)', 'ButtonLabel' => 'Varchar(128)', - 'LinkTitles' => 'Boolean', - 'LinkImages' => 'Boolean' + 'LinkTitles' => 'Boolean' ]; /** @@ -128,20 +119,8 @@ class ListComponent extends BaseComponent 'ShowDetails' => 'all', 'ShowSummary' => 'all', 'ShowFooter' => 'all', - 'ImageLinksTo' => 'item', 'DateFormat' => 'd MMMM Y', - 'LinkTitles' => 1, - 'LinkImages' => 1 - ]; - - /** - * Defines the extension classes to apply to this object. - * - * @var array - * @config - */ - private static $extensions = [ - ListSourceExtension::class + 'LinkTitles' => 1 ]; /** @@ -230,11 +209,6 @@ public function getCMSFields() $this->fieldLabel('ImageAlignment'), $this->getImageAlignmentOptions() )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholderNone), - DropdownField::create( - 'ImageLinksTo', - $this->fieldLabel('ImageLinksTo'), - $this->getImageLinksToOptions() - ), TextField::create( 'DateFormat', $this->fieldLabel('DateFormat') @@ -280,7 +254,6 @@ public function fieldLabels($includerelations = true) $labels['ShowFooter'] = _t(__CLASS__ . '.SHOWFOOTER', 'Show footer'); $labels['HeadingLevel'] = _t(__CLASS__ . '.HEADINGLEVEL', 'Heading level'); $labels['LinkTitles'] = _t(__CLASS__ . '.LINKTITLES', 'Link titles'); - $labels['LinkImages'] = _t(__CLASS__ . '.LINKIMAGES', 'Link images'); $labels['ButtonLabel'] = _t(__CLASS__ . '.BUTTONLABEL', 'Button label'); $labels['ImageAlignment'] = _t(__CLASS__ . '.IMAGEALIGNMENT', 'Image alignment'); @@ -306,19 +279,13 @@ public function populateDefaults() } /** - * Answers a list of items. + * Answers true if the receiver can paginate. * - * @return ArrayList + * @return boolean */ - public function getListItems() + public function canPaginate() { - $items = parent::getListItems(); - - foreach ($items as $item) { - $item->setListComponent($this); - } - - return $items; + return true; } /** @@ -437,16 +404,6 @@ public function isFooterShown($isFirst = false, $isMiddle = false, $isLast = fal return $this->isShown('Footer', $isFirst, $isMiddle, $isLast); } - /** - * Answers a message string to be shown when no data is available. - * - * @return string - */ - public function getNoDataMessage() - { - return _t(__CLASS__ . '.NODATAAVAILABLE', 'No data available.'); - } - /** * Answers an array of options for the show fields. * @@ -475,19 +432,6 @@ public function getImageAlignmentOptions() ]; } - /** - * Answers an array of options for the image links to field. - * - * @return array - */ - public function getImageLinksToOptions() - { - return [ - self::IMAGE_LINK_ITEM => _t(__CLASS__ . '.ITEM', 'Item'), - self::IMAGE_LINK_FILE => _t(__CLASS__ . '.FILE', 'File') - ]; - } - /** * Answers true if the part with the given name is shown. * diff --git a/src/Dev/FixtureBlueprint.php b/src/Dev/FixtureBlueprint.php index 9e03a16..e22cb26 100644 --- a/src/Dev/FixtureBlueprint.php +++ b/src/Dev/FixtureBlueprint.php @@ -818,15 +818,15 @@ public function isReference($value) } /** - * Answers true if the given value is a code string. + * Answers true if the given value is a callback array. * - * @param string $value + * @param array $value * * @return boolean */ - public function isCode($value) + public function isCallbackArray($value) { - return !is_array($value) ? preg_match('/^`(.)*`$/', $value) : false; + return (is_array($value) && isset($value['class']) && isset($value['method'])); } /** @@ -977,7 +977,15 @@ public function getDefaultParent() if ($defaultParents = $this->config()->default_parents) { if (isset($defaultParents[$this->getClass()])) { - return $this->processValue($defaultParents[$this->getClass()]); + + $result = $this->processValue($defaultParents[$this->getClass()]); + + if ($result instanceof DataObject) { + return $result->ID; + } + + return $result; + } } @@ -995,7 +1003,15 @@ public function getDefaultRelation($class) if ($defaultRelations = $this->config()->default_relations) { if (isset($defaultRelations[$class])) { - return $this->processValue($defaultRelations[$class]); + + $result = $this->processValue($defaultRelations[$class]); + + if ($result instanceof DataObject) { + return $result->ID; + } + + return $result; + } } @@ -1168,41 +1184,80 @@ protected function onAfterCreate(DataObject $object, $identifier, &$data, &$fixt /** * Processes the given value and answers the resulting data. * - * @param string $value + * @param string|array $value * * @return mixed */ protected function processValue($value) { - $value = trim($value); - - if ($this->isCode($value)) { - return $this->processCode($value); - } elseif ($this->isReference($value)) { - return $this->processReference($value); + if (is_array($value)) { + + // Handle Array Value: + + $value = $this->processArray($value); + + } else { + + // Handle String Value: + + $value = trim($value); + + if ($this->isReference($value)) { + return $this->processReference($value); + } + } + // Answer Value: + return $value; } /** - * Processes the given code string and answers the evaluated result. + * Processes the given array value and answers the resulting data. * - * @param string $value e.g. `SiteConfig::current_site_config()->ID` + * @param array $value * * @return mixed */ - protected function processCode($value) + protected function processArray($value) { - if ($this->isCode($value)) { + if (is_array($value)) { + + if ($this->isCallbackArray($value)) { + return $this->processCallbackArray($value); + } - // Process Code String: + return implode(', ', $value); - $code = trim($value, '`'); + } + + return $value; + } + + /** + * Processes the given callback array value and answers the resulting data. + * + * @param array $value + * + * @return mixed + */ + public function processCallbackArray($value) + { + if ($this->isCallbackArray($value)) { - // Answer Evaluated Result: + $class = $value['class']; + $method = $value['method']; - return eval("return $code;"); + if ($result = call_user_func("{$class}::{$method}")) { + + if (isset($value['property'])) { + return $result->{$value['property']}; + } + + return $result; + + } } } diff --git a/src/Extensions/ConfigExtension.php b/src/Extensions/ConfigExtension.php index bef49a6..c6e0bcd 100644 --- a/src/Extensions/ConfigExtension.php +++ b/src/Extensions/ConfigExtension.php @@ -111,6 +111,16 @@ public function getBodyAttributesHTML() return ViewTools::singleton()->getAttributesHTML($this->owner->getAllBodyAttributes()); } + /** + * Answers the current site config object. + * + * @return SiteConfig + */ + public function getSiteConfig() + { + return SiteConfig::current_site_config(); + } + /** * Answers the asset folder used by the receiver. * @@ -124,14 +134,4 @@ protected function getAssetFolder() return $folders[static::class]; } } - - /** - * Answers the current site config object. - * - * @return SiteConfig - */ - public function getSiteConfig() - { - return SiteConfig::current_site_config(); - } } diff --git a/src/Extensions/ControllerExtension.php b/src/Extensions/ControllerExtension.php index ade2c95..47a89c8 100644 --- a/src/Extensions/ControllerExtension.php +++ b/src/Extensions/ControllerExtension.php @@ -25,6 +25,7 @@ use SilverStripe\View\Requirements; use SilverWare\Grid\Grid; use SilverWare\Tools\ClassTools; +use SilverWare\Tools\ViewTools; use PageController; /** @@ -263,13 +264,27 @@ protected function initRequirements() } - // Component Requirements Enabled? + // Load Page Controller Requirements: - if ($this->owner->config()->load_component_requirements) { + if ($this->owner instanceof PageController) { - // Load Component Requirements: + // Custom CSS Enabled? - if ($this->owner instanceof PageController) { + if ($this->owner->config()->load_custom_css) { + + // Load Custom CSS: + + if ($css = $this->getCustomCSSAsString()) { + $this->loadCustomCSS($css); + } + + } + + // Component Requirements Enabled? + + if ($this->owner->config()->load_component_requirements) { + + // Load Component Requirements: foreach ($this->owner->getEnabledComponents() as $component) { $component->loadRequirements(); @@ -351,6 +366,52 @@ protected function loadCSS($name) Requirements::css($name); } + /** + * Loads the given custom CSS string. + * + * @param string $css Custom CSS. + * + * @return void + */ + protected function loadCustomCSS($css) + { + Requirements::customCSS($css); + } + + /** + * Answers the custom CSS required for the template as a string. + * + * @return string + */ + public function getCustomCSSAsString() + { + // Create CSS Array: + + $css = []; + + // Merge Custom CSS from Page Controller: + + if ($this->owner instanceof PageController) { + $css = array_merge($css, $this->owner->getCustomCSS()); + } + + // Create CSS String: + + $css = implode("\n", $css); + + // Remove Empty Lines: + + $css = ViewTools::singleton()->removeEmptyLines($css); + + // Trim CSS String: + + $css = trim($css); + + // Answer CSS String: + + return $css; + } + /** * Loads the themed JavaScript with the given name. * diff --git a/src/Extensions/Lists/ListSourceExtension.php b/src/Extensions/Lists/ListSourceExtension.php index 33d4d83..5aa2d3b 100644 --- a/src/Extensions/Lists/ListSourceExtension.php +++ b/src/Extensions/Lists/ListSourceExtension.php @@ -59,7 +59,8 @@ class ListSourceExtension extends DataExtension 'ImageResize' => 'Dimensions', 'ImageResizeMethod' => 'Varchar(32)', 'PaginateItems' => 'Boolean', - 'ReverseItems' => 'Boolean' + 'ReverseItems' => 'Boolean', + 'ImageItems' => 'Boolean' ]; /** @@ -81,7 +82,8 @@ class ListSourceExtension extends DataExtension private static $defaults = [ 'ItemsPerPage' => 10, 'PaginateItems' => 0, - 'ReverseItems' => 0 + 'ReverseItems' => 0, + 'ImageItems' => 0 ]; /** @@ -112,13 +114,29 @@ public function updateCMSFields(FieldList $fields) // Create Options Fields: - $fields->addFieldsToTab( + $fields->addFieldToTab( 'Root.Options', CompositeField::create([ TextField::create( 'NumberOfItems', $this->owner->fieldLabel('NumberOfItems') ), + CheckboxField::create( + 'ReverseItems', + $this->owner->fieldLabel('ReverseItems') + ), + CheckboxField::create( + 'ImageItems', + $this->owner->fieldLabel('ImageItems') + ) + ])->setName('ListSourceOptions')->setTitle($this->owner->fieldLabel('ListSourceOptions')) + ); + + // Create Pagination Options (if permitted): + + if ($this->owner->canPaginate()) { + + $fields->insertAfter( SelectionGroup::create( 'PaginateItems', [ @@ -137,12 +155,10 @@ public function updateCMSFields(FieldList $fields) ) ] )->setTitle($this->owner->fieldLabel('PaginateItems')), - CheckboxField::create( - 'ReverseItems', - $this->owner->fieldLabel('ReverseItems') - ) - ])->setName('ListSourceOptions')->setTitle($this->owner->fieldLabel('ListSourceOptions')) - ); + 'NumberOfItems' + ); + + } } /** @@ -157,6 +173,7 @@ public function updateFieldLabels(&$labels) $labels['Enabled'] = _t(__CLASS__ . '.ENABLED', 'Enabled'); $labels['Disabled'] = _t(__CLASS__ . '.DISABLED', 'Disabled'); $labels['ListSource'] = $labels['ListSourceID'] = _t(__CLASS__ . '.LISTSOURCE', 'List source'); + $labels['ImageItems'] = _t(__CLASS__ . '.IMAGEITEMS', 'Show only items with images'); $labels['ReverseItems'] = _t(__CLASS__ . '.REVERSEITEMS', 'Reverse items'); $labels['ItemsPerPage'] = _t(__CLASS__ . '.ITEMSPERPAGE', 'Items per page'); $labels['PaginateItems'] = _t(__CLASS__ . '.PAGINATEITEMS', 'Paginate items'); @@ -164,6 +181,16 @@ public function updateFieldLabels(&$labels) $labels['ListSourceOptions'] = _t(__CLASS__ . '.LISTSOURCE', 'List source'); } + /** + * Answers true if the extended object can paginate. + * + * @return boolean + */ + public function canPaginate() + { + return false; + } + /** * Answers a list of items. * @@ -195,6 +222,16 @@ public function getListItems() } + // Remove Items without Images (if applicable): + + if ($this->owner->ImageItems) { + + $items = $items->filterByCallback(function($item) { + return $item->hasMetaImage(); + }); + + } + // Reverse Items (if applicable): if ($this->owner->ReverseItems) { @@ -219,6 +256,12 @@ public function getListItems() } + // Associate Items with List Component: + + foreach ($items as $item) { + $item->setListComponent($this->owner); + } + // Answer List: return $items; diff --git a/src/Extensions/Model/MetaDataExtension.php b/src/Extensions/Model/MetaDataExtension.php index c93938e..d5199d7 100644 --- a/src/Extensions/Model/MetaDataExtension.php +++ b/src/Extensions/Model/MetaDataExtension.php @@ -19,6 +19,7 @@ use SilverStripe\AssetAdmin\Forms\UploadField; use SilverStripe\Assets\Image; +use SilverStripe\Core\Config\Config; use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\CompositeField; use SilverStripe\Forms\DropdownField; @@ -27,7 +28,7 @@ use SilverStripe\Forms\Tab; use SilverStripe\ORM\DataExtension; use SilverStripe\ORM\FieldType\DBField; -use SilverWare\Components\ListComponent; +use SilverWare\Components\BaseListComponent; use SilverWare\Forms\DimensionsField; use SilverWare\Tools\ImageTools; use SilverWare\Tools\ViewTools; @@ -117,6 +118,25 @@ class MetaDataExtension extends DataExtension ] ]; + /** + * Defines the default meta field configuration for the extended object. + * + * @var array + * @config + */ + private static $default_meta_fields = [ + 'Image' => 'Root.Meta', + 'Summary' => 'Root.Meta' + ]; + + /** + * Defines the default asset folder for uploaded images. + * + * @var string + * @config + */ + private static $default_image_folder = 'Images'; + /** * Updates the CMS fields of the extended object. * @@ -136,53 +156,120 @@ public function updateCMSFields(FieldList $fields) 'Main' ); + // Iterate Meta Field Configuration: + + foreach ($this->owner->getMetaFieldConfig() as $name => $spec) { + + if ($spec) { + + // Initialise: + + $before = null; + $method = "getMeta{$name}Fields"; + + // Determine Specification Type: + + if (is_array($spec)) { + $tab = isset($spec['tab']) ? $spec['tab'] : 'Root.Meta'; + $method = isset($spec['method']) ? $spec['method'] : "getMeta{$name}Fields"; + $before = isset($spec['before']) ? $spec['before'] : null; + } else { + $tab = $spec; + } + + // Add Fields to Specified Tab: + + if ($this->owner->hasMethod($method)) { + $fields->addFieldsToTab($tab, $this->owner->$method(), $before); + } + + } + + } + + // Remove Meta Tab (if empty): + + if (!$fields->fieldByName('Root.Meta')->getChildren()->exists()) { + $fields->removeFieldFromTab('Root', 'Meta'); + } + } + + /** + * Answers the meta field configuration from the extended object. + * + * @return array + */ + public function getMetaFieldConfig() + { + if (is_array($this->owner->config()->meta_fields)) { + return $this->owner->config()->meta_fields; + } + + return Config::inst()->get(self::class, 'default_meta_fields'); + } + + /** + * Answers a list of fields for the meta summary. + * + * @return FieldList + */ + public function getMetaSummaryFields() + { + return FieldList::create([ + HTMLEditorField::create( + 'SummaryMeta', + $this->owner->fieldLabel('SummaryMeta') + )->setRows(10) + ]); + } + + /** + * Answers a list of fields for the meta image. + * + * @return FieldList + */ + public function getMetaImageFields() + { // Define Placeholder: $placeholder = _t(__CLASS__ . '.DROPDOWNDEFAULT', '(default)'); - // Create Meta Fields: + // Answer Field Objects: - $fields->addFieldsToTab( - 'Root.Meta', - [ + return FieldList::create([ + CompositeField::create([ + UploadField::create( + 'ImageMeta', + $this->owner->fieldLabel('ImageMeta') + )->setAllowedFileCategories('image')->setFolderName($this->owner->getMetaImageFolder()), HTMLEditorField::create( - 'SummaryMeta', - $this->owner->fieldLabel('SummaryMeta') + 'ImageMetaCaption', + $this->owner->fieldLabel('ImageMetaCaption') )->setRows(10), - CompositeField::create([ - UploadField::create( - 'ImageMeta', - $this->owner->fieldLabel('ImageMeta') - )->setAllowedFileCategories('image'), //->setFolderName($this->getAssetFolder()), - HTMLEditorField::create( - 'ImageMetaCaption', - $this->owner->fieldLabel('ImageMetaCaption') - )->setRows(10), - DropdownField::create( - 'ImageMetaAlignment', - $this->owner->fieldLabel('ImageMetaAlignment'), - ImageTools::singleton()->getAlignmentOptions() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), - DimensionsField::create( - 'ImageMetaResize', - $this->owner->fieldLabel('ImageMetaResize') - ), - DropdownField::create( - 'ImageMetaResizeMethod', - $this->owner->fieldLabel('ImageMetaResizeMethod'), - ImageTools::singleton()->getResizeMethods() - )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), - CheckboxField::create( - 'ImageMetaHidden', - $this->owner->fieldLabel('ImageMetaHidden') - ), - CheckboxField::create( - 'ImageMetaCaptionHidden', - $this->owner->fieldLabel('ImageMetaCaptionHidden') - ) - ])->setName('ImageMetaComposite')->setTitle($this->owner->fieldLabel('ImageMetaComposite')) - ] - ); + DropdownField::create( + 'ImageMetaAlignment', + $this->owner->fieldLabel('ImageMetaAlignment'), + ImageTools::singleton()->getAlignmentOptions() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + DimensionsField::create( + 'ImageMetaResize', + $this->owner->fieldLabel('ImageMetaResize') + ), + DropdownField::create( + 'ImageMetaResizeMethod', + $this->owner->fieldLabel('ImageMetaResizeMethod'), + ImageTools::singleton()->getResizeMethods() + )->setEmptyString(' ')->setAttribute('data-placeholder', $placeholder), + CheckboxField::create( + 'ImageMetaHidden', + $this->owner->fieldLabel('ImageMetaHidden') + ), + CheckboxField::create( + 'ImageMetaCaptionHidden', + $this->owner->fieldLabel('ImageMetaCaptionHidden') + ) + ])->setName('ImageMetaComposite')->setTitle($this->owner->fieldLabel('ImageMetaComposite')) + ]); } /** @@ -227,7 +314,7 @@ public function getMetaLink() */ public function hasMetaLink() { - return (boolean) $this->getMetaLink(); + return (boolean) $this->owner->getMetaLink(); } /** @@ -261,7 +348,7 @@ public function getMetaTitle() */ public function getMetaDate() { - return $this->getMetaCreated(); + return $this->owner->getMetaCreated(); } /** @@ -273,7 +360,7 @@ public function getMetaDate() */ public function getMetaDateFormatted($format) { - return $this->getMetaDate()->Format($format); + return $this->owner->getMetaDate()->Format($format); } /** @@ -321,7 +408,7 @@ public function getMetaSummary() return $this->owner->dbObject('Summary'); } - if ($content = $this->getMetaContent()) { + if ($content = $this->owner->getMetaContent()) { return DBField::create_field('HTMLFragment', sprintf('

%s

', $content->Summary())); } } @@ -333,7 +420,7 @@ public function getMetaSummary() */ public function hasMetaSummary() { - if ($summary = $this->getMetaSummary()) { + if ($summary = $this->owner->getMetaSummary()) { return (boolean) $summary->RAW(); } @@ -349,8 +436,8 @@ public function hasMetaSummary() */ public function getMetaSummaryLimited($maxSentences = 2) { - if ($this->hasMetaSummary()) { - return trim(preg_replace('/\s+/', ' ', $this->getMetaSummary()->LimitSentences($maxSentences))); + if ($this->owner->hasMetaSummary()) { + return trim(preg_replace('/\s+/', ' ', $this->owner->getMetaSummary()->LimitSentences($maxSentences))); } } @@ -373,7 +460,7 @@ public function getMetaContent() */ public function hasMetaContent() { - if ($content = $this->getMetaContent()) { + if ($content = $this->owner->getMetaContent()) { return (boolean) $content->RAW(); } @@ -388,7 +475,7 @@ public function hasMetaContent() public function getMetaClassNames() { return [ - ($this->hasMetaImage() ? 'has-image' : 'no-image') + ($this->owner->hasMetaImage() ? 'has-image' : 'no-image') ]; } @@ -404,6 +491,16 @@ public function getMetaImage() } } + /** + * Answers the name of the asset folder used for uploading images. + * + * @return string + */ + public function getMetaImageFolder() + { + return Config::inst()->get(self::class, 'default_image_folder'); + } + /** * Answers true if the extended object has an image. * @@ -411,7 +508,7 @@ public function getMetaImage() */ public function hasMetaImage() { - if ($image = $this->getMetaImage()) { + if ($image = $this->owner->getMetaImage()) { return $image->exists(); } @@ -425,13 +522,13 @@ public function hasMetaImage() */ public function getMetaImageLink() { - if ($this->hasMetaImage()) { + if ($this->owner->hasMetaImage()) { - if ($list = $this->getListComponent()) { + if ($list = $this->owner->getListComponent()) { switch ($list->ImageLinksTo) { - case ListComponent::IMAGE_LINK_ITEM: + case BaseListComponent::IMAGE_LINK_ITEM: return $this->owner->getMetaLink(); } @@ -450,10 +547,29 @@ public function getMetaImageLink() */ public function getMetaImageLinkAttributes() { - return [ + $attributes = [ 'href' => $this->owner->MetaImageLink, + 'class' => $this->owner->MetaImageLinkClass, 'title' => $this->owner->MetaTitle ]; + + if ($extra = Config::inst()->get(self::class, 'image_link_attributes')) { + + foreach ($extra as $name => $value) { + + // Process Attribute Value: + + $attributes[$name] = ViewTools::singleton()->processAttribute( + $value, + $this->owner, + $this->getListComponent() + ); + + } + + } + + return $attributes; } /** @@ -463,13 +579,25 @@ public function getMetaImageLinkAttributes() */ public function getMetaImageLinkAttributesHTML() { - return ViewTools::singleton()->getAttributesHTML($this->getMetaImageLinkAttributes()); + return ViewTools::singleton()->getAttributesHTML($this->owner->getMetaImageLinkAttributes()); + } + + /** + * Answers the meta image group for the extended object. + * + * @return string + */ + public function getMetaImageGroup() + { + if ($list = $this->owner->getListComponent()) { + return $list->getHTMLID(); + } } /** * Answers the meta image caption for the extended object. * - * @return Image + * @return string */ public function getMetaImageCaption() { @@ -493,7 +621,7 @@ public function hasMetaImageCaption() */ public function getMetaImageShown() { - return ($this->hasMetaImage() && !$this->owner->ImageMetaHidden); + return ($this->owner->hasMetaImage() && !$this->owner->ImageMetaHidden); } /** @@ -503,7 +631,7 @@ public function getMetaImageShown() */ public function getMetaImageCaptionShown() { - return ($this->hasMetaImageCaption() && !$this->owner->ImageMetaCaptionHidden); + return ($this->owner->hasMetaImageCaption() && !$this->owner->ImageMetaCaptionHidden); } /** @@ -517,13 +645,13 @@ public function getMetaImageCaptionShown() */ public function getMetaImageResized($width = null, $height = null, $method = null) { - if ($this->hasMetaImage()) { + if ($this->owner->hasMetaImage()) { return ImageTools::singleton()->resize( - $this->getMetaImage(), - $this->getMetaImageResizeWidth($width), - $this->getMetaImageResizeHeight($height), - $this->getMetaImageResizeMethod($method) + $this->owner->getMetaImage(), + $this->owner->getMetaImageResizeWidth($width), + $this->owner->getMetaImageResizeHeight($height), + $this->owner->getMetaImageResizeMethod($method) ); } @@ -616,7 +744,7 @@ public function getMetaImageAlignment($alignment = null) */ public function getMetaImageClass() { - return ViewTools::singleton()->array2att($this->getMetaImageClassNames()); + return ViewTools::singleton()->array2att($this->owner->getMetaImageClassNames()); } /** @@ -628,13 +756,43 @@ public function getMetaImageClassNames() { $classes = ['image']; - if ($alignment = $this->getMetaImageAlignment()) { + if ($alignment = $this->owner->getMetaImageAlignment()) { $classes[] = $alignment; } return $classes; } + /** + * Answers a string of meta image link class names for the template. + * + * @return string + */ + public function getMetaImageLinkClass() + { + return ViewTools::singleton()->array2att($this->owner->getMetaImageLinkClassNames()); + } + + /** + * Answers an array of meta image link class names for the template. + * + * @return array + */ + public function getMetaImageLinkClassNames() + { + $classes = ['image-link']; + + if ($list = $this->owner->getListComponent()) { + + if ($to = strtolower($list->ImageLinksTo)) { + $classes[] = sprintf('to-%s', $to); + } + + } + + return $classes; + } + /** * Answers a string of meta image caption class names for the template. * @@ -642,7 +800,7 @@ public function getMetaImageClassNames() */ public function getMetaImageCaptionClass() { - return ViewTools::singleton()->array2att($this->getMetaImageCaptionClassNames()); + return ViewTools::singleton()->array2att($this->owner->getMetaImageCaptionClassNames()); } /** @@ -654,7 +812,7 @@ public function getMetaImageCaptionClassNames() { $classes = ['caption']; - if ($alignment = $this->getMetaImageAlignment()) { + if ($alignment = $this->owner->getMetaImageAlignment()) { $classes[] = $alignment; } @@ -668,7 +826,7 @@ public function getMetaImageCaptionClassNames() */ public function getMetaImageWrapperClass() { - return ViewTools::singleton()->array2att($this->getMetaImageWrapperClassNames()); + return ViewTools::singleton()->array2att($this->owner->getMetaImageWrapperClassNames()); } /** @@ -680,7 +838,7 @@ public function getMetaImageWrapperClassNames() { $classes = [$this->owner->MetaImageCaptionShown ? 'captionImage' : 'image']; - if ($alignment = $this->getMetaImageAlignment()) { + if ($alignment = $this->owner->getMetaImageAlignment()) { $classes[] = $alignment; } @@ -690,7 +848,7 @@ public function getMetaImageWrapperClassNames() /** * Answers a list component associated with the extended object (if it exists). * - * @return ListComponent + * @return BaseListComponent */ protected function getListComponent() { diff --git a/src/Extensions/PageExtension.php b/src/Extensions/PageExtension.php index 42ccf3c..e3985be 100644 --- a/src/Extensions/PageExtension.php +++ b/src/Extensions/PageExtension.php @@ -308,6 +308,28 @@ public function getDefaultTemplate() return SiteConfig::current_site_config()->getTemplateForPage($this->owner); } + /** + * Answers an array of custom CSS required for the template. + * + * @return array + */ + public function getCustomCSS() + { + // Create CSS Array: + + $css = []; + + // Merge Template Custom CSS: + + if ($template = $this->owner->getPageTemplate()) { + $css = array_merge($css, $template->getCustomCSS()); + } + + // Answer CSS Array: + + return $css; + } + /** * Answers a list of the enabled components within the extended object. * diff --git a/src/Forms/ViewportsField.php b/src/Forms/ViewportsField.php index cbc39bb..2cbc43a 100644 --- a/src/Forms/ViewportsField.php +++ b/src/Forms/ViewportsField.php @@ -21,6 +21,7 @@ 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; use SilverStripe\ORM\Map; @@ -74,6 +75,13 @@ class ViewportsField extends FormField */ protected $emptyString; + /** + * If true, text fields are used instead of dropdown fields. + * + * @var boolean + */ + protected $useTextInput = false; + /** * Defines the schema data type. * @@ -108,9 +116,7 @@ public function __construct($name, $title = null, $source = [], $value = null) // Build Viewport Fields: - foreach (DBViewports::singleton()->getViewports() as $viewport) { - $this->fields[$viewport] = $this->buildViewportField($viewport); - } + $this->buildViewportFields(); // Define Empty String: @@ -278,6 +284,32 @@ public function setReadonly($readonly) return $self; } + /** + * Defines the value of the useTextInput attribute. + * + * @param boolean $useTextInput + * + * @return $this + */ + public function setUseTextInput($useTextInput) + { + $this->useTextInput = (boolean) $useTextInput; + + $this->buildViewportFields(); + + return $this; + } + + /** + * Answers the value of the useTextInput attribute. + * + * @return boolean + */ + public function getUseTextInput() + { + return $this->useTextInput; + } + /** * Returns a read-only version of the receiver. * @@ -436,24 +468,6 @@ public function FieldHolder($properties = []) )->addExtraClass('viewports')->FieldHolder($properties); } - /** - * Builds and answers a form field for the specified viewport. - * - * @param string $viewport - * - * @return FormField - */ - protected function buildViewportField($viewport) - { - $field = DropdownField::create( - sprintf('%s[%s]', $this->getName(), $viewport), - $this->getViewportLabel($viewport), - $this->getSource() - ); - - return $field; - } - /** * Converts the given source parameter to an array. * @@ -474,6 +488,47 @@ protected function getSourceAsArray($source) return $source; } + /** + * Builds the viewport fields for the receiver. + * + * @return void + */ + protected function buildViewportFields() + { + foreach (DBViewports::singleton()->getViewports() as $viewport) { + $this->fields[$viewport] = $this->buildViewportField($viewport); + } + } + + /** + * Builds and answers a form field for the specified viewport. + * + * @param string $viewport + * + * @return FormField + */ + protected function buildViewportField($viewport) + { + if ($this->useTextInput) { + + $field = TextField::create( + sprintf('%s[%s]', $this->getName(), $viewport), + $this->getViewportLabel($viewport) + ); + + } else { + + $field = DropdownField::create( + sprintf('%s[%s]', $this->getName(), $viewport), + $this->getViewportLabel($viewport), + $this->getSource() + ); + + } + + return $field; + } + /** * Updates the viewport fields after a state change. * @@ -495,16 +550,22 @@ protected function updateViewportFields() $field->removeExtraClass('hidden'); } - // Update Empty Strings: + // Update Dropdown Fields: - if ($field->hasMethod('setEmptyString')) { - $field->setEmptyString(' ')->setAttribute('data-placeholder', $this->emptyString); + if ($field instanceof DropdownField) { + + // Update Empty Strings: + + if ($field->hasMethod('setEmptyString')) { + $field->setEmptyString(' ')->setAttribute('data-placeholder', $this->emptyString); + } + + // Update Source: + + $field->setSource($this->getSource()); + } - // Update Source: - - $field->setSource($this->getSource()); - // Update Disabled Flags: $field->setDisabled($this->disabled); diff --git a/src/Grid/Framework.php b/src/Grid/Framework.php index ac52ff8..8ebf54b 100644 --- a/src/Grid/Framework.php +++ b/src/Grid/Framework.php @@ -39,6 +39,14 @@ abstract class Framework extends ViewableData */ private static $columns = 12; + /** + * Maps viewport sizes to breakpoints for this framework. + * + * @var array + * @config + */ + private static $breakpoints = []; + /** * Maps style names to the appropriate style classes provided by this framework. * @@ -115,6 +123,32 @@ public function getStyles($names = []) return array_filter($styles); } + /** + * Answers the breakpoint for the specified viewport. + * + * @param string $viewport + * + * @return string + */ + public function getBreakpoint($viewport) + { + $viewport = strtolower($viewport); + + if (($breakpoints = $this->getBreakpoints()) && isset($breakpoints[$viewport])) { + return $breakpoints[$viewport]; + } + } + + /** + * Answers an array of breakpoints defined for the framework. + * + * @return array + */ + public function getBreakpoints() + { + return $this->config()->breakpoints; + } + /** * Answers an array of style mappings defined for the framework. * diff --git a/src/Lists/ListItem.php b/src/Lists/ListItem.php index 56747c6..5e71529 100644 --- a/src/Lists/ListItem.php +++ b/src/Lists/ListItem.php @@ -20,7 +20,8 @@ use SilverStripe\ORM\ArrayList; use SilverStripe\View\ArrayData; use SilverStripe\View\SSViewer; -use SilverWare\Components\ListComponent; +use SilverWare\Components\BaseListComponent; +use SilverWare\Tools\ViewTools; use SilverWare\View\ViewClasses; /** @@ -37,20 +38,20 @@ trait ListItem use ViewClasses; /** - * The list component instance responsible for rendering the list item. + * The component instance responsible for rendering the list item. * - * @var ListComponent + * @var BaseListComponent */ protected $listComponent; /** * Defines the value of the listComponent attribute. * - * @param ListComponent $listComponent + * @param BaseListComponent $listComponent * * @return $this */ - public function setListComponent(ListComponent $listComponent) + public function setListComponent(BaseListComponent $listComponent) { $this->listComponent = $listComponent; @@ -60,7 +61,7 @@ public function setListComponent(ListComponent $listComponent) /** * Answers the value of the listComponent attribute. * - * @return ListComponent + * @return BaseListComponent */ public function getListComponent() { @@ -153,7 +154,7 @@ public function getListItemDetails() } /** - * Processes the given string of text which references methods / fields of the receiver and list component. + * Processes the given string of text which references methods / fields of the receiver and List Component. * * @param string $text * @param array $args @@ -162,58 +163,6 @@ public function getListItemDetails() */ public function processListItemText($text, $args = []) { - // Does the text refer to a field or method? - - if (strpos($text, '$') === 0) { - - // Obtain Field Name: - - $field = ltrim($text, '$'); - - // Obtain Field Value: - - if ($this->hasMethod("get{$field}")) { - - // First, answer the result of a method call on the receiver: - - $text = call_user_func_array([$this, "get{$field}"], $this->processListItemArgs($args)); - - } elseif ($this->hasField($field)) { - - // Next, answer a field value from the receiver: - - return $this->$field; - - } elseif ($this->getListComponent()->hasField($field)) { - - // Finally, answer a field value from the associated List Component: - - return $this->getListComponent()->$field; - - } - - } - - // Answer Text Value: - - return $text; - } - - /** - * Processes the given array of arguments for a list item detail. - * - * @param string|array $stringOrArray - * - * @return array - */ - public function processListItemArgs($stringOrArray) - { - $args = (array) $stringOrArray; - - foreach ($args as $key => $arg) { - $args[$key] = $this->processListItemText($arg); - } - - return $args; + return ViewTools::singleton()->processAttribute($text, $this, $this->getListComponent(), $args); } } diff --git a/src/Model/Component.php b/src/Model/Component.php index f3a1bca..14f494b 100644 --- a/src/Model/Component.php +++ b/src/Model/Component.php @@ -27,6 +27,7 @@ use SilverStripe\Security\Permission; use SilverStripe\Security\PermissionProvider; use SilverStripe\SiteConfig\SiteConfig; +use SilverStripe\View\SSViewer; use SilverWare\Admin\PageIconFix; use SilverWare\Extensions\RenderableExtension; use SilverWare\Tools\ClassTools; @@ -708,6 +709,48 @@ public function tag($content = null) return $this->getOpeningTag() . $content . $this->getClosingTag(); } + /** + * Answers an array of custom CSS required for the template. + * + * @return array + */ + public function getCustomCSS() + { + // Create CSS Array: + + $css = []; + + // Merge Custom CSS from Template: + + $template = $this->getCustomCSSTemplate(); + + if (SSViewer::hasTemplate($template)) { + $css = array_merge($css, preg_split('/\r\n|\n|\r/', $this->renderWith($template))); + } + + // Update CSS via Extensions: + + $this->extend('updateCustomCSS', $css); + + // Filter CSS Array: + + $css = array_filter($css); + + // Answer CSS Array: + + return $css; + } + + /** + * Answers the name of a template used to render custom CSS for the receiver. + * + * @return string + */ + public function getCustomCSSTemplate() + { + return sprintf('%s\CustomCSS', $this->class); + } + /** * Renders the component for the HTML template. * diff --git a/src/Model/Template.php b/src/Model/Template.php index c8fc61d..71a7530 100644 --- a/src/Model/Template.php +++ b/src/Model/Template.php @@ -84,6 +84,32 @@ 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/ORM/FieldType/DBViewports.php b/src/ORM/FieldType/DBViewports.php index 7b8810f..9d25f8e 100644 --- a/src/ORM/FieldType/DBViewports.php +++ b/src/ORM/FieldType/DBViewports.php @@ -20,6 +20,7 @@ use SilverStripe\ORM\ArrayLib; use SilverStripe\ORM\FieldType\DBComposite; use SilverWare\Forms\ViewportsField; +use SilverWare\Grid\Grid; /** * A composite database field used to store different values for particular device viewports. @@ -98,6 +99,18 @@ public function allEqualTo($value) return true; } + /** + * Answers the breakpoint for the specified viewport. + * + * @param string $viewport + * + * @return string + */ + public function getBreakpoint($viewport) + { + return Grid::framework()->getBreakpoint($viewport); + } + /** * Answers an array of the viewport field names. * diff --git a/src/Tools/ViewTools.php b/src/Tools/ViewTools.php index 32570bc..12fd29c 100644 --- a/src/Tools/ViewTools.php +++ b/src/Tools/ViewTools.php @@ -21,6 +21,7 @@ use SilverStripe\Core\Convert; use SilverStripe\Core\Object; use SilverStripe\View\Requirements; +use SilverStripe\View\ViewableData; /** * A singleton providing utility functions for use with views. @@ -143,4 +144,88 @@ public function getAttributesHTML($attributes) return implode(' ', $markup); } + + /** + * Processes the given attribute value which references methods / fields of the given objects. + * + * @param string $value + * @param ViewableData $object + * @param ViewableData $parent + * @param array $args + * + * @return string + */ + public function processAttribute($value, ViewableData $object, ViewableData $parent = null, $args = []) + { + // Does the value refer to a field or method? + + if (strpos($value, '$') === 0) { + + // Obtain Field Name: + + $field = ltrim($value, '$'); + + // Obtain Field Value: + + if ($object->hasMethod("get{$field}")) { + + // First, answer the result of a method call on the receiver: + + return call_user_func_array( + [$object, "get{$field}"], + $this->processAttributeArgs($args, $object, $parent) + ); + + } elseif ($object->hasField($field)) { + + // Next, answer a field value from the given object: + + return $object->$field; + + } elseif ($parent->hasField($field)) { + + // Finally, answer a field value from the given parent object: + + return $parent->$field; + + } + + } + + // Answer Value: + + return $value; + } + + /** + * Processes the given array of arguments for an attribute. + * + * @param string|array $stringOrArray + * @param ViewableData $object + * @param ViewableData $parent + * + * @return array + */ + public function processAttributeArgs($stringOrArray, ViewableData $object, ViewableData $parent = null) + { + $args = (array) $stringOrArray; + + foreach ($args as $key => $arg) { + $args[$key] = $this->processAttribute($arg, $object, $parent); + } + + return $args; + } + + /** + * Removes empty lines from the given string. + * + * @param string $string + * + * @return string + */ + public function removeEmptyLines($string) + { + return preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $string); + } } diff --git a/templates/SilverWare/Lists/ListItem/Includes/Footer.ss b/templates/SilverWare/Lists/ListItem/Includes/Footer.ss index 21e5707..329812a 100644 --- a/templates/SilverWare/Lists/ListItem/Includes/Footer.ss +++ b/templates/SilverWare/Lists/ListItem/Includes/Footer.ss @@ -4,4 +4,4 @@ <% include Button Tag='a', HREF=$MetaLink, Text=$ListComponent.ButtonLabel %> <% end_if %> -<% end_if %> \ No newline at end of file +<% end_if %> diff --git a/yarn.lock b/yarn.lock index 3d89343..c3c20b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -613,7 +613,7 @@ boom@2.x.x: dependencies: hoek "2.x.x" -brace-expansion@^1.0.0: +brace-expansion@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" dependencies: @@ -743,8 +743,8 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000664" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000664.tgz#e16316e5fdabb9c7209b2bf0744ffc8a14201f22" + version "1.0.30000665" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000665.tgz#e84f4277935f295f546f8533cb0b410a8415b972" caseless@~0.12.0: version "0.12.0" @@ -1155,8 +1155,8 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" electron-to-chromium@^1.2.7: - version "1.3.8" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.8.tgz#b2c8a2c79bb89fbbfd3724d9555e15095b5f5fb6" + version "1.3.9" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.9.tgz#db1cba2a26aebcca2f7f5b8b034554468609157d" elliptic@^6.0.0: version "6.4.0" @@ -1203,10 +1203,6 @@ esprima@^2.6.0: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" -esprima@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -1367,7 +1363,7 @@ function-bind@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" -gauge@~2.7.1: +gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" dependencies: @@ -1742,14 +1738,7 @@ js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" -js-yaml@^3.4.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766" - dependencies: - argparse "^1.0.7" - esprima "^3.1.1" - -js-yaml@~3.7.0: +js-yaml@^3.4.3, js-yaml@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" dependencies: @@ -2014,10 +2003,10 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" "minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: - brace-expansion "^1.0.0" + brace-expansion "^1.1.7" minimist@0.0.8: version "0.0.8" @@ -2055,8 +2044,8 @@ node-dir@^0.1.10: minimatch "^3.0.2" node-gyp@^3.3.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.0.tgz#7474f63a3a0501161dda0b6341f022f14c423fa6" + version "3.6.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.1.tgz#19561067ff185464aded478212681f47fd578cbc" dependencies: fstream "^1.0.0" glob "^7.0.3" @@ -2179,12 +2168,12 @@ normalize-url@^1.4.0: sort-keys "^1.0.0" "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + version "4.1.0" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" - gauge "~2.7.1" + gauge "~2.7.3" set-blocking "~2.0.0" num2fraction@^1.2.2: @@ -2850,15 +2839,15 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@~2.5.1: - version "2.5.4" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: glob "^7.0.5" -rimraf@^2.5.1, rimraf@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" +rimraf@~2.5.1: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: glob "^7.0.5" @@ -2956,8 +2945,8 @@ source-list-map@^0.1.7, source-list-map@~0.1.7: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" source-list-map@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.1.tgz#1a33ac210ca144d1e561f906ebccab5669ff4cb4" + version "1.1.2" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" source-map-support@^0.4.2: version "0.4.15" @@ -3175,8 +3164,8 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" uglify-js@^2.8.5: - version "2.8.22" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.22.tgz#d54934778a8da14903fa29a326fb24c0ab51a1a0" + version "2.8.23" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.23.tgz#8230dd9783371232d62a7821e2cf9a817270a8a0" dependencies: source-map "~0.5.1" yargs "~3.10.0" @@ -3279,8 +3268,8 @@ webpack-sources@^0.2.3: source-map "~0.5.3" webpack@^2.2.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.4.1.tgz#15a91dbe34966d8a4b99c7d656efd92a2e5a6f6a" + version "2.5.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.5.1.tgz#61742f0cf8af555b87460a9cd8bba2f1e3ee2fce" dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0"