From 6c8f5c4e2c63512481b5d36646cd8657d38ded9f Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Tue, 30 Jan 2018 19:40:14 +0100 Subject: [PATCH 01/18] fix(versioned-relations): add compatibility for 0.1.9 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 107ef7e..9f20014 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ }, "require": { "silverstripe/framework": "~3.6", - "flxlabs/silverstripe-versionedrelations": "^0.1.7" + "flxlabs/silverstripe-versionedrelations": "^0.1.9" }, "extra": { "installer-name": "pagesections" From 215b40c11fc002388a714191ca0a19b2354dc684 Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Tue, 30 Jan 2018 19:41:25 +0100 Subject: [PATCH 02/18] chore(version): Bump version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9f20014..b75003d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "flxlabs/silverstripe-pagesections", - "version": "0.1.2", + "version": "0.1.4", "description": "Adds configurable page sections and elements to your SilverStripe project.", "type": "silverstripe-module", "homepage": "http://github.com/flxlabs/silverstripe-pagesections", From 02b553a8141e39a29f95cda33b1554f3b705e5db Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Tue, 13 Feb 2018 12:45:24 +0100 Subject: [PATCH 03/18] fix(versioned-relations): Bump versioned relations dependency to fix OrderableRowsExtension issue --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b75003d..5fdc261 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ }, "require": { "silverstripe/framework": "~3.6", - "flxlabs/silverstripe-versionedrelations": "^0.1.9" + "flxlabs/silverstripe-versionedrelations": "^0.1.10" }, "extra": { "installer-name": "pagesections" From d60cf26b08f3fc608f8eff7746d781d0de11d817 Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Thu, 22 Feb 2018 13:06:31 +0100 Subject: [PATCH 04/18] feat(sort-order): Use a negative sort order ending by zero so that new elements are added to the bottom of the list --- code/PageElement.php | 3 ++- code/PageSectionsExtension.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/PageElement.php b/code/PageElement.php index c7510d6..4a93ac0 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -53,9 +53,10 @@ public function onBeforeWrite() { $list = $this->Children()->Sort("SortOrder")->toArray(); $count = count($list); + $min = -2 - ($count * 2); for ($i = 1; $i <= $count; $i++) { - $this->Children()->Add($list[$i - 1], array("SortOrder" => $i * 2)); + $this->Children()->Add($list[$i - 1], array("SortOrder" => $min + $i * 2)); } } diff --git a/code/PageSectionsExtension.php b/code/PageSectionsExtension.php index c0888c2..719d13d 100644 --- a/code/PageSectionsExtension.php +++ b/code/PageSectionsExtension.php @@ -43,9 +43,10 @@ public function onBeforeWrite() { $list = $this->owner->$name()->Sort("SortOrder")->toArray(); $count = count($list); + $min = -2 - ($count * 2); for ($i = 1; $i <= $count; $i++) { - $this->owner->$name()->Add($list[$i - 1], array("SortOrder" => $i * 2)); + $this->owner->$name()->Add($list[$i - 1], array("SortOrder" => $min + $i * 2)); } } } From e5a13f335668e225a0d27c1cded316b5c81ce4f2 Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Thu, 22 Feb 2018 13:19:07 +0100 Subject: [PATCH 05/18] feat(gridfield): Open all elements by default, unless they have static $default_is_open set to false --- code/GridFieldPageSectionsExtension.php | 13 +++++++++++++ code/PageElement.php | 2 ++ 2 files changed, 15 insertions(+) diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index c93c274..7dacf4e 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -80,6 +80,19 @@ public function augmentColumns($gridField, &$columns) { $state = $gridField->getState(); if (!isset($state->open)) { $state->open = array(); + + // Open all elements by default + $list = array(); + $newList = $gridField->getManipulatedList(); + while (count($list) < count($newList)) { + foreach ($newList as $item) { + if ($item->isOpenByDefault()) { + $this->openElement($state, $item); + } + } + $list = $newList; + $newList = $gridField->getManipulatedList(); + } } } diff --git a/code/PageElement.php b/code/PageElement.php index 4a93ac0..030d4d8 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -2,8 +2,10 @@ class PageElement extends DataObject { public static $singular_name = 'Element'; public static $plural_name = 'Elements'; + public static $default_is_open = true; public function getSingularName() { return static::$singular_name; } public function getPluralName() { return static::$plural_name; } + public function isOpenByDefault() { return static::$default_is_open; } function canView($member = null) { return true; } function canEdit($member = null) { return true; } From a94986e5df62b25b8fc8a9f67fe37b4e4a9db6f9 Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Thu, 22 Feb 2018 13:21:13 +0100 Subject: [PATCH 06/18] chore(version): Bump version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5fdc261..2692210 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "flxlabs/silverstripe-pagesections", - "version": "0.1.4", + "version": "0.1.5", "description": "Adds configurable page sections and elements to your SilverStripe project.", "type": "silverstripe-module", "homepage": "http://github.com/flxlabs/silverstripe-pagesections", From 1a98fca1cb6c1204e07e2aa244451e6980dd489d Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Wed, 28 Feb 2018 20:38:28 +0100 Subject: [PATCH 07/18] fix(ui): WIP enhance tree icons, enhance sorting visual feedback --- code/GridFieldPageSectionsExtension.php | 6 +- css/GridFieldPageSectionsExtension.css | 162 ++++++++++++------- javascript/GridFieldPageSectionsExtension.js | 34 ++-- templates/GridFieldPageElement.ss | 7 +- 4 files changed, 134 insertions(+), 75 deletions(-) diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index 7dacf4e..899d449 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -158,10 +158,10 @@ public function getColumnContent($gridField, $record, $columnName) { $field = null; if ($record->Children() && $record->Children()->Count() > 0) { - $icon = ($open === true ? '' - : ''); + $icon = ($open === true ? '' + : ''); } else { - $icon = ''; + $icon = ''; } $field = GridField_FormAction::create( diff --git a/css/GridFieldPageSectionsExtension.css b/css/GridFieldPageSectionsExtension.css index a031998..6e4334c 100644 --- a/css/GridFieldPageSectionsExtension.css +++ b/css/GridFieldPageSectionsExtension.css @@ -40,92 +40,103 @@ border-top: 1px solid #CCC; } - -.ss-gridfield-pagesections .col-treenav { +/* hierarchical gridfield */ +.cms table.ss-gridfield-table tr { height: 100%; - padding: 0 !important; } -.ss-gridfield-pagesections .col-treenav > button { - //width: 100% !important; - height: 100% !important; - font-weight: bold !important; - /*font-size: 150% !important;*/ - color: black !important; +.cms table.ss-gridfield-table tbody td.col-treenav { + padding: 0; + height: 100%; + vertical-align: middle; } -.ss-gridfield-pagesections .col-treenav > button, .ss-gridfield-pagesections .col-treenav > button > span { - padding: 0 !important; - margin: 0 !important; - text-align: left !important; +.cms table.ss-gridfield-table tbody td.col-treenav .col-treenav__inner { + display: flex; + height: 100%; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: center; } -.ss-gridfield-pagesections .col-treenav > button:focus { - background: none !important; - box-shadow: none!important; - border: none !important; - background-color: none !important; +.cms table.ss-gridfield-table tbody td.col-treenav button { + width: 2em; + height: 100%; + display: block; + float: left; + margin: 0 0.5em 0 0; + padding: 0; + border-radius: 0; + position: relative; } -.ss-gridfield-pagesections .col-treenav > button > span > span.is-closed{ - - font-size: 100%; +.cms table.ss-gridfield-table tbody td.col-treenav button svg { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); } -.ss-gridfield-pagesections .col-treenav > button > span > span.is-open{ - font-size: 96%; - margin-left: -0.1em; +.cms table.ss-gridfield-table tbody td.col-treenav button.level1 { + margin-left: 2em; } -.ss-gridfield-pagesections .col-treenav > button > span > span.is-end{ - font-size: 110%; - margin-left: -0.1em; +.cms table.ss-gridfield-table tbody td.col-treenav button.level2 { + margin-left: 4em; } -.ss-gridfield-pagesections .col-treenav > button > span > span { - margin-right: 1em; - text-transform: capitalize; - vertical-align: middle; +.cms table.ss-gridfield-table tbody td.col-treenav button.level3 { + margin-left: 6em; } -.ss-gridfield-pagesections .col-treenav > button > span { - margin-right: 30px !important; - text-overflow: ellipsis; - white-space: nowrap; - overflow-x: hidden; +.cms table.ss-gridfield-table tbody td.col-treenav button.level4 { + margin-left: 8em; } -.ss-gridfield-pagesections .col-treenav > button.level0 > span { - margin-left: 1em !important; -} -.ss-gridfield-pagesections .col-treenav > button.level1 > span { - margin-left: 2em !important; +.cms table.ss-gridfield-table tbody td.col-treenav button.level5 { + margin-left: 10em; } -.ss-gridfield-pagesections .col-treenav > button.level2 > span { - margin-left: 4em !important; -} -.ss-gridfield-pagesections .col-treenav > button.level3 > span { - margin-left: 6em !important; -} -.ss-gridfield-pagesections .col-treenav > button.level4 > span { - margin-left: 8em !important; +.cms table.ss-gridfield-table tbody td.col-treenav button.level6 { + margin-left: 12em; } - /** * Orderable rows */ +.col-treenav__draggable { + z-index: 300; + background: #f6f7f8; + padding: 2px 4px; + border-radius: 2px; + box-shadow: 0 0 1px #ccc; +} + +.col-treenav__draggable:before { + position: absolute; + content: ""; + left: -19px; + top: -19px; + width: 8px; + height: 8px; + border: 3px solid #4a4a4a; + border-radius: 50%; + box-sizing: border-box; +} + +.col-treenav__draggable.state-hover { + background: #A8CB7F; +} .ss-gridfield-pagesections thead tr th.col-Reorder span { padding: 0 !important; margin-left: 8px; } -.ss-gridfield-pagesections .col-reorder { +.cms .ss-gridfield-pagesections table.ss-gridfield-table tr td.col-reorder { position: relative; - padding: 0 !important; - width: 16px !important; + padding: 0; + width: 16px; } .ss-gridfield-pagesections .col-reorder .handle { @@ -150,26 +161,63 @@ .ss-gridfield-pagesections .col-reorder .ui-droppable { position: absolute; - left: 0; - width: 100%; + left: 100%; + width: 500%; z-index: 100; display: none; } +.ss-gridfield-pagesections .col-reorder .ui-droppable svg { + position: absolute; + left: 0; + top: 50%; + z-index: 100; + transform: translate(0, -50%); +} + +.ss-gridfield-pagesections .col-reorder .ui-droppable svg path { + fill: #A8CB7F; +} + .ss-gridfield-pagesections .col-reorder .ui-droppable.before { top: 0; height: 25%; background-color: rgba(0, 100, 0, 0.4); } +.ss-gridfield-pagesections .col-reorder .ui-droppable.before svg { + top: 0; +} + .ss-gridfield-pagesections .col-reorder .ui-droppable.middle { - top: 25%; - height: 50%; + left: auto; + right: 0; + top: 0; + height: 100%; background-color: rgba(0, 0, 100, 0.4); } +.ss-gridfield-pagesections .col-reorder .ui-droppable.middle svg { + left: auto; + right: 0; + transform: scale(-1) translate(0, 50%); +} + .ss-gridfield-pagesections .col-reorder .ui-droppable.after { bottom: 0; height: 25%; background-color: rgba(0, 100, 0, 0.4); } + +.ss-gridfield-pagesections .col-reorder .ui-droppable.after svg { + top: 100%; +} + +.ss-gridfield-pagesections .col-reorder .ui-droppable.state-active { + background: pink !important; + z-index: 200; +} + +.ss-gridfield-pagesections .col-reorder .ui-droppable.state-active svg path { + fill: #417505; +} diff --git a/javascript/GridFieldPageSectionsExtension.js b/javascript/GridFieldPageSectionsExtension.js index dbf35aa..8ff542c 100644 --- a/javascript/GridFieldPageSectionsExtension.js +++ b/javascript/GridFieldPageSectionsExtension.js @@ -50,7 +50,7 @@ var grid = this.getGridField(); var id = grid.data("id"); var rowId = $target.parents(".ss-gridfield-item").data("id"); - var $treeNav = $target.hasClass("col-treenav") ? $target : + var $treeNav = $target.hasClass("col-treenav") ? $target : $target.parents(".col-treenav").first(); // If we don't have a col-treenav the user clicked on another column @@ -63,7 +63,7 @@ if (level > 0) { // Go up through the rows and find the first row with lower level (=parent) $parent = $treeNav.parents(".ss-gridfield-item").prev(); - while ($parent.length > 0 && + while ($parent.length > 0 && $parent.find(".col-treenav").data("level") >= level) { $parent = $parent.prev(); } @@ -91,7 +91,7 @@ $menu.append("
  • " + value + "
  • "); }); $menu.append("
  • Options
  • "); - $menu.append("
  • Remove from " + + $menu.append("
  • Remove from " + (parentId ? parentName : "page") + "
  • "); $menu.append("
  • Delete
  • "); $menu.show(); @@ -133,11 +133,13 @@ $("tr.ss-gridfield-item").each(function() { var $this = $(this); + var icon = "" $col = $this.find(".col-reorder"); - $col.append("
    "); + $col.append("
    " + icon + "
    " + icon + "
    " + icon + "
    "); $col.find("div").each(function() { $(this).droppable({ + hoverClass: "state-active", tolerance: "pointer", drop: function(event, ui) { $drop = $(this); @@ -155,12 +157,12 @@ // If the current element is open, then dragging the other element to the // "after" slot means it becomes a child of this element, otherwise it // has to actually go after this element. - if ($treenav.find("button").hasClass("is-open")) { - type = "child"; - childOrder = -1000000; - } else { - type = "after"; - } + if ($treenav.find("button").hasClass("is-open")) { + type = "child"; + childOrder = -1000000; + } else { + type = "after"; + } } var id = ui.draggable.data("id"); @@ -195,13 +197,17 @@ $this.draggable({ revert: "invalid", + cursor: "crosshair", + cursorAt: { top: -15, left: -15 }, + activeClass: "state-hover", helper: function() { - var clone = $this.clone().css("z-index", 200).find(".ui-droppable").remove().end(); + var $helper = $("
    " + $this.find(".col-treenav__title").text() + "
    ") + //var clone = $this.clone().css("z-index", 200).find(".ui-droppable").remove().end(); // Timeout is needed otherwise the draggable position is messed up setTimeout(function() { hideRow($this); }, 1); - return clone; + return $helper; }, start: function() { var element = $this.data("class"); @@ -212,10 +218,10 @@ // Check if we're allowed to drop the element on the specified drop point. // Depending on where we drop it (before, middle or after) we have to either // check our allowed children, or the allowed children of our parent row. - if ($drop.hasClass("before") || + if ($drop.hasClass("before") || ($drop.hasClass("after") && !$treenav.find("button").hasClass("is-open"))) { - var $parent = $treenav.parent().siblings("[data-id=" + + var $parent = $treenav.parent().siblings("[data-id=" + $treenav.data("parent") + "]").first(); var allowed = $parent.find(".col-treenav").data("allowed-elements"); diff --git a/templates/GridFieldPageElement.ss b/templates/GridFieldPageElement.ss index 471c3b8..5035369 100644 --- a/templates/GridFieldPageElement.ss +++ b/templates/GridFieldPageElement.ss @@ -1 +1,6 @@ -$ButtonField $Title +
    + $ButtonField + + $Title + +
    From ed5fadcf3618e7afe45865755570db3c471aeeb0 Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Sun, 4 Mar 2018 14:24:59 +0100 Subject: [PATCH 08/18] fix(ui): enhance UI for sorting, add footer to gridfields --- code/GridFieldPageSectionsExtension.php | 29 +++++--- code/PageElement.php | 6 +- code/PageSectionsExtension.php | 10 +-- css/GridFieldPageSectionsExtension.css | 21 +++--- javascript/GridFieldPageSectionsExtension.js | 72 ++++++++++++++++---- 5 files changed, 97 insertions(+), 41 deletions(-) diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index 899d449..e149043 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -20,8 +20,9 @@ class GridFieldPageSectionsExtension implements ); - public function __construct($page, $sortField = "SortOrder") { + public function __construct($page, $allowedElementsOnPage = array(), $sortField = "SortOrder") { $this->page = $page; + $this->allowedElementsOnPage = $allowedElementsOnPage; $this->sortField = $sortField; } @@ -81,12 +82,13 @@ public function augmentColumns($gridField, &$columns) { if (!isset($state->open)) { $state->open = array(); - // Open all elements by default + // Open all elements by default if has children $list = array(); $newList = $gridField->getManipulatedList(); while (count($list) < count($newList)) { foreach ($newList as $item) { - if ($item->isOpenByDefault()) { + //var_dump($item->Children());die; + if ($item->isOpenByDefault() && $item->Children()->Count) { $this->openElement($state, $item); } } @@ -125,12 +127,23 @@ public function getColumnAttributes($gridField, $record, $columnName) { $elems[$class] = $class::$singular_name; } + // if element has no parent we need to + // know the allowed elements of the page + if (!$record->_Parent) { + $pageClasses = $this->allowedElementsOnPage; + $pageElems = array(); + foreach ($pageClasses as $class) { + $pageElems[$class] = $class::$singular_name; + } + } + return array( - "class" => "col-treenav", - "data-class" => $record->ClassName, - "data-level" => strval($record->_Level), - "data-parent" => $record->_Parent ? strval($record->_Parent->ID) : "", - "data-allowed-elements" => json_encode($elems, JSON_UNESCAPED_UNICODE), + "class" => "col-treenav", + "data-class" => $record->ClassName, + "data-level" => strval($record->_Level), + "data-parent" => $record->_Parent ? strval($record->_Parent->ID) : "", + "data-allowed-page-elements" => !$record->_Parent ? json_encode($pageElems, JSON_UNESCAPED_UNICODE) : "", + "data-allowed-elements" => json_encode($elems, JSON_UNESCAPED_UNICODE), ); } diff --git a/code/PageElement.php b/code/PageElement.php index 030d4d8..ec05aa5 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -63,8 +63,9 @@ public function onBeforeWrite() { } public function getChildrenGridField() { + $allowedElements = $this->getAllowedPageElements(); $addNewButton = new GridFieldAddNewMultiClass(); - $addNewButton->setClasses($this->getAllowedPageElements()); + $addNewButton->setClasses($allowedElements); $autoCompl = new GridFieldAddExistingAutocompleter('buttons-before-right'); $autoCompl->setResultsFormat('$Title ($ID)'); @@ -77,7 +78,8 @@ public function getChildrenGridField() { ->addComponent($autoCompl) ->addComponent($addNewButton) ->addComponent(new GridFieldPageSectionsExtension($this->owner)) - ->addComponent(new GridFieldDetailForm()); + ->addComponent(new GridFieldDetailForm()) + ->addComponent(new GridFieldFooter()); $dataColumns->setFieldCasting(array('GridFieldPreview' => 'HTMLText->RAW')); return new GridField("Children", "Children", $this->Children(), $config); diff --git a/code/PageSectionsExtension.php b/code/PageSectionsExtension.php index 719d13d..2ba02f4 100644 --- a/code/PageSectionsExtension.php +++ b/code/PageSectionsExtension.php @@ -28,7 +28,7 @@ public function extraStatics($class = null, $extensionClass = null) { return array(); } - public static function getAllowedPageElements() { + public static function getAllowedPageElements($sectionName = "Main") { $classes = array_values(ClassInfo::subclassesFor("PageElement")); $classes = array_diff($classes, ["PageElement"]); return $classes; @@ -61,8 +61,9 @@ public function updateCMSFields(FieldList $fields) { $fields->removeByName($name); if ($this->owner->ID) { + $allowedElements = $this->owner->getAllowedPageElements($section); $addNewButton = new GridFieldAddNewMultiClass(); - $addNewButton->setClasses($this->owner->getAllowedPageElements()); + $addNewButton->setClasses($allowedElements); $autoCompl = new GridFieldAddExistingAutocompleter('buttons-before-right'); $autoCompl->setResultsFormat('$Title ($ID)'); @@ -73,8 +74,9 @@ public function updateCMSFields(FieldList $fields) { ->addComponent($dataColumns = new GridFieldDataColumns()) ->addComponent($autoCompl) ->addComponent($addNewButton) - ->addComponent(new GridFieldPageSectionsExtension($this->owner)) - ->addComponent(new GridFieldDetailForm()); + ->addComponent(new GridFieldPageSectionsExtension($this->owner, $allowedElements)) + ->addComponent(new GridFieldDetailForm()) + ->addComponent(new GridFieldFooter()); $dataColumns = $config->getComponentByType('GridFieldDataColumns'); $dataColumns->setFieldCasting(array('GridFieldPreview' => 'HTMLText->RAW')); diff --git a/css/GridFieldPageSectionsExtension.css b/css/GridFieldPageSectionsExtension.css index 6e4334c..23cec77 100644 --- a/css/GridFieldPageSectionsExtension.css +++ b/css/GridFieldPageSectionsExtension.css @@ -112,6 +112,10 @@ box-shadow: 0 0 1px #ccc; } +.col-treenav__draggable.state-active { + border: 1px solid #417505; +} + .col-treenav__draggable:before { position: absolute; content: ""; @@ -124,10 +128,6 @@ box-sizing: border-box; } -.col-treenav__draggable.state-hover { - background: #A8CB7F; -} - .ss-gridfield-pagesections thead tr th.col-Reorder span { padding: 0 !important; margin-left: 8px; @@ -162,7 +162,7 @@ .ss-gridfield-pagesections .col-reorder .ui-droppable { position: absolute; left: 100%; - width: 500%; + width: 100vw; z-index: 100; display: none; } @@ -181,8 +181,7 @@ .ss-gridfield-pagesections .col-reorder .ui-droppable.before { top: 0; - height: 25%; - background-color: rgba(0, 100, 0, 0.4); + height: 50%; } .ss-gridfield-pagesections .col-reorder .ui-droppable.before svg { @@ -194,7 +193,6 @@ right: 0; top: 0; height: 100%; - background-color: rgba(0, 0, 100, 0.4); } .ss-gridfield-pagesections .col-reorder .ui-droppable.middle svg { @@ -204,17 +202,16 @@ } .ss-gridfield-pagesections .col-reorder .ui-droppable.after { - bottom: 0; - height: 25%; - background-color: rgba(0, 100, 0, 0.4); + bottom: -1px; + height: 50%; } .ss-gridfield-pagesections .col-reorder .ui-droppable.after svg { top: 100%; + margin-top: -1px; } .ss-gridfield-pagesections .col-reorder .ui-droppable.state-active { - background: pink !important; z-index: 200; } diff --git a/javascript/GridFieldPageSectionsExtension.js b/javascript/GridFieldPageSectionsExtension.js index 8ff542c..a01ca2e 100644 --- a/javascript/GridFieldPageSectionsExtension.js +++ b/javascript/GridFieldPageSectionsExtension.js @@ -170,6 +170,11 @@ var newParent = type === "child" ? $this.data("id") : $treenav.data("parent"); var sort = type === "child" ? childOrder : $reorder.data("sort"); + // we alter the state of the published / saved buttons + $('.cms-edit-form .Actions #Form_EditForm_action_publish').button({ + showingAlternate: true + }); + grid.reload({ url: grid.data("url-reorder"), data: [{ @@ -193,20 +198,23 @@ }); }); - - $this.draggable({ revert: "invalid", cursor: "crosshair", cursorAt: { top: -15, left: -15 }, - activeClass: "state-hover", + activeClass: "state-active", + hoverClass: "state-active", + tolerance: "pointer", + greedy: true, helper: function() { - var $helper = $("
    " + $this.find(".col-treenav__title").text() + "
    ") - //var clone = $this.clone().css("z-index", 200).find(".ui-droppable").remove().end(); - // Timeout is needed otherwise the draggable position is messed up - setTimeout(function() { - hideRow($this); - }, 1); + var $tr = $this.parents("tr.ss-gridfield-item"); + var $helper = $( + "
    " + + $this.find(".col-treenav__title").text() + + "
    " + ) + $this.css("opacity", 0.6) + return $helper; }, start: function() { @@ -214,15 +222,49 @@ $(".ui-droppable").each(function() { var $drop = $(this); var $treenav = $drop.parent().siblings(".col-treenav"); + var isOpen = $treenav.find("button").hasClass("is-open"); + var $tr = $drop.parents("tr.ss-gridfield-item"); // Check if we're allowed to drop the element on the specified drop point. + // dont enable dropping on itself + if ($tr.data("id") == $this.data("id")) return + + // dont enable dropping on .before of itself + if ($drop.hasClass("before") && $tr.prev().data("id") == $this.data("id")) return // Depending on where we drop it (before, middle or after) we have to either + // don't show middle if open + if ( + $drop.hasClass("middle") && + isOpen + ) { + return; + } + // let's handle level 0 if not open + else if ( + $treenav.data("level") == 0 && + ( + $drop.hasClass("before") || + ( + $drop.hasClass("after") && + !isOpen + ) + ) + ) { + var allowed = $treenav.data("allowed-page-elements"); + console.log(allowed, element, $treenav) + if (!allowed[element]) return; + } // check our allowed children, or the allowed children of our parent row. - if ($drop.hasClass("before") || - ($drop.hasClass("after") && !$treenav.find("button").hasClass("is-open"))) { - - var $parent = $treenav.parent().siblings("[data-id=" + - $treenav.data("parent") + "]").first(); + else if ( + $drop.hasClass("before") || + ( + $drop.hasClass("after") && + !isOpen + ) + ) { + var $parent = $treenav.parent().siblings( + "[data-id=" + $treenav.data("parent") + "]" + ).first(); var allowed = $parent.find(".col-treenav").data("allowed-elements"); if (allowed && !allowed[element]) return; @@ -239,7 +281,7 @@ // Show the previous elements. If the user made an invalid movement then // we want this to show anyways. If he did something valid the grid will // refresh so we don't care if it's visible behind the loading icon. - $("tr.ss-gridfield-item").show(); + $("tr.ss-gridfield-item").css("opacity", "") }, }); }); From 61ba4d8cc647aa8fc913b5ae18ff8b593ae40636 Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Sun, 4 Mar 2018 18:12:06 +0100 Subject: [PATCH 09/18] fix(gridfield): set toggle button to disabled if no childre --- code/GridFieldPageSectionsExtension.php | 8 ++++++-- css/GridFieldPageSectionsExtension.css | 14 ++++++++++++++ javascript/GridFieldPageSectionsExtension.js | 4 ++-- javascript/lang/de.js | 9 +++++++++ javascript/lang/en.js | 9 +++++++++ 5 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 javascript/lang/de.js create mode 100644 javascript/lang/en.js diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index e149043..c4d0649 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -51,6 +51,7 @@ public function getHTMLFragments($field) { $moduleDir = self::getModuleDir(); Requirements::css($moduleDir . "/css/GridFieldPageSectionsExtension.css"); Requirements::javascript($moduleDir . "/javascript/GridFieldPageSectionsExtension.js"); + Requirements::add_i18n_javascript($moduleDir . '/javascript/lang', false, true); $id = rand(1000000, 9999999); $field->addExtraClass("ss-gridfield-pagesections"); @@ -87,8 +88,7 @@ public function augmentColumns($gridField, &$columns) { $newList = $gridField->getManipulatedList(); while (count($list) < count($newList)) { foreach ($newList as $item) { - //var_dump($item->Children());die; - if ($item->isOpenByDefault() && $item->Children()->Count) { + if ($item->isOpenByDefault() && $item->Children()->Count()) { $this->openElement($state, $item); } } @@ -185,6 +185,10 @@ public function getColumnContent($gridField, $record, $columnName) { array("element" => $record) ); $field->addExtraClass("level".$level . ($open ? " is-open" : " is-closed")); + if (!$record->Children()->Count()) { + $field->addExtraClass(" is-end"); + $field->setDisabled(true); + } $field->setButtonContent($icon); $field->setForm($gridField->getForm()); diff --git a/css/GridFieldPageSectionsExtension.css b/css/GridFieldPageSectionsExtension.css index 23cec77..6bfb42f 100644 --- a/css/GridFieldPageSectionsExtension.css +++ b/css/GridFieldPageSectionsExtension.css @@ -70,6 +70,20 @@ position: relative; } +.cms table.ss-gridfield-table tbody td.col-treenav button.ui-state-disabled { + opacity: 1; + filter: Alpha(Opacity=100); + background-image: none; + background: none; + box-shadow: none; +} + +.cms table.ss-gridfield-table tbody td.col-treenav button.ui-state-disabled:active { + border: none; + background: none; + box-shadow: none; +} + .cms table.ss-gridfield-table tbody td.col-treenav button svg { position: absolute; left: 50%; diff --git a/javascript/GridFieldPageSectionsExtension.js b/javascript/GridFieldPageSectionsExtension.js index a01ca2e..d1b2a61 100644 --- a/javascript/GridFieldPageSectionsExtension.js +++ b/javascript/GridFieldPageSectionsExtension.js @@ -69,7 +69,7 @@ } if ($parent != null) { parentId = $parent.data("id"); - parentName = $parent.find(".col-treenav > span").html(); + parentName = $parent.find(".col-treenav .col-treenav__title").html(); } } @@ -86,7 +86,7 @@ rowId: rowId, parentId: parentId, }); - $menu.append("
  • Add a child
  • "); + $menu.append("
  • " + ss.i18n._t('PageSections.GridField.AddAChild','Add a child') + "
  • "); $.each(elems, function(key, value) { $menu.append("
  • " + value + "
  • "); }); diff --git a/javascript/lang/de.js b/javascript/lang/de.js new file mode 100644 index 0000000..e4908cf --- /dev/null +++ b/javascript/lang/de.js @@ -0,0 +1,9 @@ +if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') { + if (typeof(console) !== 'undefined') { // eslint-disable-line no-console + console.error('Class ss.i18n not defined'); // eslint-disable-line no-console + } +} else { + ss.i18n.addDictionary('en', { + "GridField.AddAChild": "Unterelement hinzufügen", +}); +} diff --git a/javascript/lang/en.js b/javascript/lang/en.js new file mode 100644 index 0000000..fa6f20c --- /dev/null +++ b/javascript/lang/en.js @@ -0,0 +1,9 @@ +if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') { + if (typeof(console) !== 'undefined') { // eslint-disable-line no-console + console.error('Class ss.i18n not defined'); // eslint-disable-line no-console + } +} else { + ss.i18n.addDictionary('en', { + "PageSections.GridField.AddAChild": "Add a child", +}); +} From 7d100cb612f9ebe958daa0389400e73af3182094 Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Sun, 4 Mar 2018 18:47:01 +0100 Subject: [PATCH 10/18] fix(sortable): fix sorting of elements in a grid from pageElement instead of a Page --- code/GridFieldPageSectionsExtension.php | 24 ++++++++++---------- code/PageElement.php | 2 +- javascript/GridFieldPageSectionsExtension.js | 14 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index c4d0649..922cdb8 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -20,9 +20,9 @@ class GridFieldPageSectionsExtension implements ); - public function __construct($page, $allowedElementsOnPage = array(), $sortField = "SortOrder") { + public function __construct($page, $allowedParentElements = array(), $sortField = "SortOrder") { $this->page = $page; - $this->allowedElementsOnPage = $allowedElementsOnPage; + $this->allowedParentElements = $allowedParentElements; $this->sortField = $sortField; } @@ -130,20 +130,20 @@ public function getColumnAttributes($gridField, $record, $columnName) { // if element has no parent we need to // know the allowed elements of the page if (!$record->_Parent) { - $pageClasses = $this->allowedElementsOnPage; - $pageElems = array(); - foreach ($pageClasses as $class) { - $pageElems[$class] = $class::$singular_name; + $parentClasses = $this->allowedParentElements; + $parentElems = array(); + foreach ($parentClasses as $class) { + $parentElems[$class] = $class::$singular_name; } } return array( - "class" => "col-treenav", - "data-class" => $record->ClassName, - "data-level" => strval($record->_Level), - "data-parent" => $record->_Parent ? strval($record->_Parent->ID) : "", - "data-allowed-page-elements" => !$record->_Parent ? json_encode($pageElems, JSON_UNESCAPED_UNICODE) : "", - "data-allowed-elements" => json_encode($elems, JSON_UNESCAPED_UNICODE), + "class" => "col-treenav", + "data-class" => $record->ClassName, + "data-level" => strval($record->_Level), + "data-parent" => $record->_Parent ? strval($record->_Parent->ID) : "", + "data-allowed-parent-elements" => !$record->_Parent ? json_encode($parentElems, JSON_UNESCAPED_UNICODE) : "", + "data-allowed-elements" => json_encode($elems, JSON_UNESCAPED_UNICODE), ); } diff --git a/code/PageElement.php b/code/PageElement.php index ec05aa5..09d7a24 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -77,7 +77,7 @@ public function getChildrenGridField() { ->addComponent($dataColumns = new GridFieldDataColumns()) ->addComponent($autoCompl) ->addComponent($addNewButton) - ->addComponent(new GridFieldPageSectionsExtension($this->owner)) + ->addComponent(new GridFieldPageSectionsExtension($this->owner, $this->owner->getAllowedPageElements())) ->addComponent(new GridFieldDetailForm()) ->addComponent(new GridFieldFooter()); $dataColumns->setFieldCasting(array('GridFieldPreview' => 'HTMLText->RAW')); diff --git a/javascript/GridFieldPageSectionsExtension.js b/javascript/GridFieldPageSectionsExtension.js index d1b2a61..1b19ec6 100644 --- a/javascript/GridFieldPageSectionsExtension.js +++ b/javascript/GridFieldPageSectionsExtension.js @@ -170,11 +170,6 @@ var newParent = type === "child" ? $this.data("id") : $treenav.data("parent"); var sort = type === "child" ? childOrder : $reorder.data("sort"); - // we alter the state of the published / saved buttons - $('.cms-edit-form .Actions #Form_EditForm_action_publish').button({ - showingAlternate: true - }); - grid.reload({ url: grid.data("url-reorder"), data: [{ @@ -194,6 +189,12 @@ value: sort, }], }); + // we alter the state of the published / saved buttons + $('.cms-edit-form .Actions #Form_EditForm_action_publish').button({ + showingAlternate: true + }); + $('.cms-preview').entwine('.ss.preview').changeState('StageLink'); + }, }); }); @@ -250,8 +251,7 @@ ) ) ) { - var allowed = $treenav.data("allowed-page-elements"); - console.log(allowed, element, $treenav) + var allowed = $treenav.data("allowed-parent-elements"); if (!allowed[element]) return; } // check our allowed children, or the allowed children of our parent row. From e7dca833e9d60033544ad4ff3b7ae8fcc2fe50a5 Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Sun, 4 Mar 2018 18:52:31 +0100 Subject: [PATCH 11/18] fix(gridfield): remove unused action --- code/GridFieldPageSectionsExtension.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index 922cdb8..b461aae 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -16,7 +16,6 @@ class GridFieldPageSectionsExtension implements "handleRemove", "handleDelete", "handleReorder", - "handleMoveToPage" ); @@ -39,7 +38,6 @@ public function getURLHandlers($grid) { "POST remove" => "handleRemove", "POST delete" => "handleDelete", "POST reorder" => "handleReorder", - "POST movetopage" => "handleMoveToPage" ); } @@ -70,12 +68,12 @@ public function augmentColumns($gridField, &$columns) { array_splice($columns, 0, 0, "Reorder"); } - if (!in_array("TreeNav", $columns)) { - array_splice($columns, 1, 0, "TreeNav"); + if (!in_array("Actions", $columns)) { + array_splice($columns, 1, 0, "Actions"); } - if (!in_array("Actions", $columns)) { - array_push($columns, "Actions"); + if (!in_array("TreeNav", $columns)) { + array_splice($columns, 2, 0, "TreeNav"); } // Insert grid state initial data @@ -101,8 +99,8 @@ public function augmentColumns($gridField, &$columns) { public function getColumnsHandled($gridField) { return array( "Reorder", - "TreeNav", "Actions", + "TreeNav", ); } From b3209ad23ecbb9f79c6131c172292dd3b08c7fea Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Mon, 5 Mar 2018 14:51:36 +0100 Subject: [PATCH 12/18] fix(gridfield ui): display id and times used, replace context menu with buttons, fix reorder issues --- code/GridFieldPageSectionsExtension.php | 73 +++++++-- code/PageElement.php | 2 +- code/PageSectionsExtension.php | 2 +- css/GridFieldPageSectionsExtension.css | 36 +++++ javascript/GridFieldPageSectionsExtension.js | 145 +++++++++--------- javascript/lang/de.js | 15 +- javascript/lang/en.js | 7 +- templates/GridFieldPageElement.ss | 11 +- .../GridFieldPageSectionsActionColumn.ss | 5 + 9 files changed, 198 insertions(+), 98 deletions(-) create mode 100644 templates/GridFieldPageSectionsActionColumn.ss diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index b461aae..90603fc 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -19,9 +19,8 @@ class GridFieldPageSectionsExtension implements ); - public function __construct($page, $allowedParentElements = array(), $sortField = "SortOrder") { + public function __construct($page, $sortField = "SortOrder") { $this->page = $page; - $this->allowedParentElements = $allowedParentElements; $this->sortField = $sortField; } @@ -68,12 +67,12 @@ public function augmentColumns($gridField, &$columns) { array_splice($columns, 0, 0, "Reorder"); } - if (!in_array("Actions", $columns)) { - array_splice($columns, 1, 0, "Actions"); + if (!in_array("TreeNav", $columns)) { + array_splice($columns, 1, 0, "TreeNav"); } - if (!in_array("TreeNav", $columns)) { - array_splice($columns, 2, 0, "TreeNav"); + if (!in_array("Actions", $columns)) { + array_splice($columns, 2, 0, "Actions"); } // Insert grid state initial data @@ -128,7 +127,7 @@ public function getColumnAttributes($gridField, $record, $columnName) { // if element has no parent we need to // know the allowed elements of the page if (!$record->_Parent) { - $parentClasses = $this->allowedParentElements; + $parentClasses = $this->page->getAllowedPageElements(); $parentElems = array(); foreach ($parentClasses as $class) { $parentElems[$class] = $class::$singular_name; @@ -192,7 +191,10 @@ public function getColumnContent($gridField, $record, $columnName) { return ViewableData::create()->customise(array( "ButtonField" => $field, - "Title" => $record->i18n_singular_name(), + "ID" => $record->ID, + "UsedCount" => $record->Parents()->Count() + $record->Pages()->Count(), + "ClassName" => $record->i18n_singular_name(), + "Title" => $record->Title, ))->renderWith("GridFieldPageElement"); } @@ -206,7 +208,60 @@ public function getColumnContent($gridField, $record, $columnName) { ); } $link = Controller::join_links($gridField->link(), $link); - return "Edit"; + $data = new ArrayData(array( + 'Link' => $link + )); + $editButton = $data->renderWith('GridFieldEditButton'); + + $classes = $record->getAllowedPageElements(); + $elems = array(); + foreach ($classes as $class) { + $elems[$class] = $class::$singular_name; + } + $addButton = GridField_FormAction::create( + $gridField, + "AddAction".$record->ID, + null, + null, + null + ); + $addButton->setAttribute("data-allowed-elements", json_encode($elems, JSON_UNESCAPED_UNICODE)); + $addButton->addExtraClass("col-actions__button add-button"); + if (!count($elems)) { + $addButton->setDisabled(true); + } + $addButton->setButtonContent(' + + + + + '); + + $deleteButton = GridField_FormAction::create( + $gridField, + "DeleteAction".$record->ID, + null, + null, + null + ); + $deleteButton->setAttribute("data-used-count", $record->Parents()->Count() + $record->Pages()->Count()); + $deleteButton->addExtraClass("col-actions__button delete-button"); + + $deleteButton->setButtonContent(' + + + + + '); + + return ViewableData::create()->customise(array( + "EditButton" => $editButton, + "AddButton" => $addButton, + "DeleteButton" => $deleteButton, + "ParentID" => $record->_Parent ? $record->_Parent->ID : $this->page->ID, + ))->renderWith("GridFieldPageSectionsActionColumn"); + + return $ret; } } diff --git a/code/PageElement.php b/code/PageElement.php index 09d7a24..ec05aa5 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -77,7 +77,7 @@ public function getChildrenGridField() { ->addComponent($dataColumns = new GridFieldDataColumns()) ->addComponent($autoCompl) ->addComponent($addNewButton) - ->addComponent(new GridFieldPageSectionsExtension($this->owner, $this->owner->getAllowedPageElements())) + ->addComponent(new GridFieldPageSectionsExtension($this->owner)) ->addComponent(new GridFieldDetailForm()) ->addComponent(new GridFieldFooter()); $dataColumns->setFieldCasting(array('GridFieldPreview' => 'HTMLText->RAW')); diff --git a/code/PageSectionsExtension.php b/code/PageSectionsExtension.php index 2ba02f4..e8d3f36 100644 --- a/code/PageSectionsExtension.php +++ b/code/PageSectionsExtension.php @@ -74,7 +74,7 @@ public function updateCMSFields(FieldList $fields) { ->addComponent($dataColumns = new GridFieldDataColumns()) ->addComponent($autoCompl) ->addComponent($addNewButton) - ->addComponent(new GridFieldPageSectionsExtension($this->owner, $allowedElements)) + ->addComponent(new GridFieldPageSectionsExtension($this->owner)) ->addComponent(new GridFieldDetailForm()) ->addComponent(new GridFieldFooter()); $dataColumns = $config->getComponentByType('GridFieldDataColumns'); diff --git a/css/GridFieldPageSectionsExtension.css b/css/GridFieldPageSectionsExtension.css index 6bfb42f..dc2c040 100644 --- a/css/GridFieldPageSectionsExtension.css +++ b/css/GridFieldPageSectionsExtension.css @@ -59,6 +59,18 @@ align-items: center; } +.cms table.ss-gridfield-table tbody td.col-treenav .col-treenav__text { + padding: 8px 8px 8px 0; +} + +.cms table.ss-gridfield-table tbody td.col-treenav .col-treenav__classname { + font-size: 87%; +} + +.cms table.ss-gridfield-table tbody td.col-treenav .col-treenav__title { + font-weight: bold; +} + .cms table.ss-gridfield-table tbody td.col-treenav button { width: 2em; height: 100%; @@ -115,6 +127,30 @@ margin-left: 12em; } +/** + * action col + */ +.cms .ss-gridfield-pagesections table.ss-gridfield-table tr td.col-actions { + width: 68px; +} + +.cms .ss-gridfield-pagesections table.ss-gridfield-table tbody td.col-actions a.view-link, .cms .ss-gridfield-pagesections table.ss-gridfield-table tbody td.col-actions a.edit-link { + margin-top: -5px; +} + +.cms .ss-gridfield-pagesections table.ss-gridfield-table tbody td button.col-actions__button { + margin: 2px; +} + +.cms .ss-gridfield-pagesections table.ss-gridfield-table tbody td button.col-actions__button .ui-button-text { + line-height: 0; + padding: 0; +} + +.cms .ss-gridfield-pagesections table.ss-gridfield-table tbody td button.col-actions__button .col-actions__button__icon svg { + display: block; +} + /** * Orderable rows */ diff --git a/javascript/GridFieldPageSectionsExtension.js b/javascript/GridFieldPageSectionsExtension.js index 1b19ec6..250e27e 100644 --- a/javascript/GridFieldPageSectionsExtension.js +++ b/javascript/GridFieldPageSectionsExtension.js @@ -18,84 +18,8 @@ } }); - // Context menu click - $(document).on("click", ".treenav-menu li", function(event) { - var $this = $(this); - var $menu = $this.parents(".treenav-menu"); - var $gridfield = $(".ss-gridfield-pagesections[data-id='" + $menu.data("grid-id") + "']").find("tbody"); - var newType = $this.data("type"); - - // If we don't have a type then the user clicked a header or some random thing - if (!newType) return; - - if (newType === "__REMOVE__") { - $gridfield.removeElement($menu.data("row-id"), $menu.data("parent-id")); - } else if (newType === "__DELETE__") { - if (!confirm("Are you sure you want to remove this element? All children will be orphans!")) - return; - - $gridfield.deleteElement($menu.data("row-id")); - } else { - $gridfield.addElement($menu.data("row-id"), newType); - } - - $this.parents(".treenav-menu").hide(); - }); - // Show context menu $(".ss-gridfield-pagesections tbody").entwine({ - oncontextmenu: function(event) { - $target = $(event.target); - - var grid = this.getGridField(); - var id = grid.data("id"); - var rowId = $target.parents(".ss-gridfield-item").data("id"); - var $treeNav = $target.hasClass("col-treenav") ? $target : - $target.parents(".col-treenav").first(); - - // If we don't have a col-treenav the user clicked on another column - if ($treeNav.length <= 0) return; - event.preventDefault(); - - var parentId = null; - var parentName = null; - var level = $treeNav.data("level"); - if (level > 0) { - // Go up through the rows and find the first row with lower level (=parent) - $parent = $treeNav.parents(".ss-gridfield-item").prev(); - while ($parent.length > 0 && - $parent.find(".col-treenav").data("level") >= level) { - $parent = $parent.prev(); - } - if ($parent != null) { - parentId = $parent.data("id"); - parentName = $parent.find(".col-treenav .col-treenav__title").html(); - } - } - - var elems = $treeNav.data("allowed-elements"); - $menu = $("
      "); - $menu.css({ - top: event.pageY + "px", - left: event.pageX + "px" - }); - $(document.body).append($menu); - - $menu.data({ - gridId: id, - rowId: rowId, - parentId: parentId, - }); - $menu.append("
    • " + ss.i18n._t('PageSections.GridField.AddAChild','Add a child') + "
    • "); - $.each(elems, function(key, value) { - $menu.append("
    • " + value + "
    • "); - }); - $menu.append("
    • Options
    • "); - $menu.append("
    • Remove from " + - (parentId ? parentName : "page") + "
    • "); - $menu.append("
    • Delete
    • "); - $menu.show(); - }, addElement: function(id, elemType) { var grid = this.getGridField(); @@ -130,9 +54,78 @@ }, onadd: function() { var grid = this.getGridField(); + var thisGrid = this; $("tr.ss-gridfield-item").each(function() { var $this = $(this); + // actions + $this.find(".col-actions .add-button").click(function(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + $target = $(event.target); + var elems = $target.data("allowed-elements"); + + var id = grid.data("id"); + var rowId = $target.parents(".ss-gridfield-item").data("id"); + + + var $menu = $("
        "); + $menu.css({ + top: event.pageY + "px", + left: event.pageX + "px" + }); + $(document.body).append($menu); + + $menu.append("
      • " + ss.i18n._t('PageSections.GridField.AddAChild','Add a child') + "
      • "); + $.each(elems, function(key, value) { + var $li = $("
      • " + value + "
      • ") + $li.click(function() { + thisGrid.addElement(rowId, key); + $menu.remove(); + }) + $menu.append($li); + }); + $menu.show(); + }); + + $this.find(".col-actions .delete-button").click(function(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + + $target = $(event.target); + + var id = grid.data("id"); + var rowId = $target.parents(".ss-gridfield-item").data("id"); + var parentId = $target.data("parent-id"); + + var $menu = $("
          "); + $menu.css({ + top: event.pageY + "px", + left: event.pageX + "px" + }); + $(document.body).append($menu); + + $menu.append("
        • " + ss.i18n._t('PageSections.GridField.Delete','Delete') + "
        • "); + + var $li = $("
        • " + ss.i18n._t('PageSections.GridField.RemoveAChild','Remove') + "
        • ") + $li.click(function() { + thisGrid.removeElement(rowId, $menu.data("parent-id")); + $menu.remove(); + }) + $menu.append($li); + if ($target.data("used-count") < 2) { + var $li = $("
        • " + ss.i18n._t('PageSections.GridField.DeleteAChild','Finally delete') + "
        • ") + $li.click(function() { + thisGrid.deleteElement(rowId, $menu.data("parent-id")); + $menu.remove(); + }) + $menu.append($li); + } + + $menu.show(); + }); + + // reorder var icon = "" $col = $this.find(".col-reorder"); diff --git a/javascript/lang/de.js b/javascript/lang/de.js index e4908cf..89b4d9a 100644 --- a/javascript/lang/de.js +++ b/javascript/lang/de.js @@ -1,9 +1,12 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') { - if (typeof(console) !== 'undefined') { // eslint-disable-line no-console - console.error('Class ss.i18n not defined'); // eslint-disable-line no-console - } + if (typeof(console) !== 'undefined') { // eslint-disable-line no-console + console.error('Class ss.i18n not defined'); // eslint-disable-line no-console + } } else { - ss.i18n.addDictionary('en', { - "GridField.AddAChild": "Unterelement hinzufügen", -}); + ss.i18n.addDictionary('en', { + "PageSections.GridField.AddAChild": "Unterelement hinzufügen", + "PageSections.GridField.Delete": "Löschen", + "PageSections.GridField.DeleteAChild": "Endgültig löschen", + "PageSections.GridField.RemoveAChild": "Entfernen", + }); } diff --git a/javascript/lang/en.js b/javascript/lang/en.js index fa6f20c..615f3ee 100644 --- a/javascript/lang/en.js +++ b/javascript/lang/en.js @@ -4,6 +4,9 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') { } } else { ss.i18n.addDictionary('en', { - "PageSections.GridField.AddAChild": "Add a child", -}); + "PageSections.GridField.AddAChild": "Add a child", + "PageSections.GridField.Delete": "Delete", + "PageSections.GridField.DeleteAChild": "Finally delete", + "PageSections.GridField.RemoveAChild": "Remove", + }); } diff --git a/templates/GridFieldPageElement.ss b/templates/GridFieldPageElement.ss index 5035369..1b87b4c 100644 --- a/templates/GridFieldPageElement.ss +++ b/templates/GridFieldPageElement.ss @@ -1,6 +1,11 @@
          $ButtonField - - $Title - +
          +
          + $ClassName (ID: {$ID}, {$UsedCount}x) +
          +
          + $Title +
          +
          diff --git a/templates/GridFieldPageSectionsActionColumn.ss b/templates/GridFieldPageSectionsActionColumn.ss new file mode 100644 index 0000000..75edf74 --- /dev/null +++ b/templates/GridFieldPageSectionsActionColumn.ss @@ -0,0 +1,5 @@ +
          + $AddButton + $DeleteButton + $EditButton +
          From 55c344f273ae7d1036e632dd1a276633cc4266eb Mon Sep 17 00:00:00 2001 From: Felix Eggmann Date: Mon, 5 Mar 2018 15:02:13 +0100 Subject: [PATCH 13/18] chore(version): bump version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2692210..52767b5 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "flxlabs/silverstripe-pagesections", - "version": "0.1.5", + "version": "0.1.6", "description": "Adds configurable page sections and elements to your SilverStripe project.", "type": "silverstripe-module", "homepage": "http://github.com/flxlabs/silverstripe-pagesections", From 0793c1d273c019fd9b065a0adbe85ebbea931218 Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Tue, 26 Jun 2018 17:52:43 +0200 Subject: [PATCH 14/18] feat(page-element): Add tab with pages where element is used --- code/PageElement.php | 9 +++++++ code/PageSectionsExtension.php | 4 +++ composer.json | 45 +++++++++++++++++----------------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/code/PageElement.php b/code/PageElement.php index ec05aa5..4d12587 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -1,4 +1,5 @@ addFieldToTab('Root.PageSections', $this->getChildrenGridField()); } + $fields->addFieldsToTab("Root.Pages", ReadonlyField::create("Version", "Version", $this->Version)); + $config = GridFieldConfig_Base::create() + ->removeComponentsByType(GridFieldDataColumns::class) + ->addComponent($dataColumns = new GridFieldDataColumns()); + $dataColumns->setDisplayFields(["Title" => "Title", "getIsPublished" => "Is Published"]); + $gridField = GridField::create("Pages", "Pages", $this->Pages(), $config); + $fields->addFieldToTab("Root.Pages", $gridField); + return $fields; } diff --git a/code/PageSectionsExtension.php b/code/PageSectionsExtension.php index e8d3f36..76acb7d 100644 --- a/code/PageSectionsExtension.php +++ b/code/PageSectionsExtension.php @@ -93,4 +93,8 @@ public function PageSection($name = "Main") { array("Elements" => $elements, "ParentList" => strval($this->owner->ID)) ); } + + public function getIsPublished() { + return DBField::create_field("HTMLText", $this->owner->latestPublished() ? "Published" : "Draft"); + } } diff --git a/composer.json b/composer.json index 52767b5..d5b20ca 100644 --- a/composer.json +++ b/composer.json @@ -1,25 +1,24 @@ { - "name": "flxlabs/silverstripe-pagesections", - "version": "0.1.6", - "description": "Adds configurable page sections and elements to your SilverStripe project.", - "type": "silverstripe-module", - "homepage": "http://github.com/flxlabs/silverstripe-pagesections", - "keywords": ["silverstripe", "sections", "elements", "page sections", "page elements"], - "license": "MIT", - "authors": [ - { - "name": "Marco Crespi", - "email": "mrc@flxlabs.com" - } - ], - "support": { - "issues": "http://github.com/flxlabs/silverstripe-pagesections/issues" - }, - "require": { - "silverstripe/framework": "~3.6", - "flxlabs/silverstripe-versionedrelations": "^0.1.10" - }, - "extra": { - "installer-name": "pagesections" - } + "name": "flxlabs/silverstripe-pagesections", + "version": "0.1.6", + "description": "Adds configurable page sections and elements to your SilverStripe project.", + "type": "silverstripe-module", + "homepage": "http://github.com/flxlabs/silverstripe-pagesections", + "keywords": ["silverstripe", "sections", "elements", "page sections", "page elements"], + "license": "MIT", + "authors": [{ + "name": "Marco Crespi", + "email": "mrc@flxlabs.com" + }], + "support": { + "issues": "http://github.com/flxlabs/silverstripe-pagesections/issues" + }, + "require": { + "silverstripe/framework": "~3.7", + "flxlabs/silverstripe-versionedrelations": "^0.1.14", + "symbiote/silverstripe-gridfieldextensions": "^2.0.2" + }, + "extra": { + "installer-name": "pagesections" + } } From 38c082e10cb114f0feb06adb1590adf3071d8dad Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Thu, 28 Jun 2018 12:49:35 +0200 Subject: [PATCH 15/18] feat(page-element): Add "publish on all pages" button Add tab to show which pages an element is on. Fixed "useCount" of elements --- code/GridFieldPageSectionsExtension.php | 7 ++- code/PageElement.php | 83 ++++++++++++++++++++++--- code/PageSectionsExtension.php | 9 ++- composer.json | 3 +- templates/PageElement.ss | 2 +- 5 files changed, 91 insertions(+), 13 deletions(-) diff --git a/code/GridFieldPageSectionsExtension.php b/code/GridFieldPageSectionsExtension.php index 90603fc..d9f426e 100644 --- a/code/GridFieldPageSectionsExtension.php +++ b/code/GridFieldPageSectionsExtension.php @@ -192,7 +192,7 @@ public function getColumnContent($gridField, $record, $columnName) { return ViewableData::create()->customise(array( "ButtonField" => $field, "ID" => $record->ID, - "UsedCount" => $record->Parents()->Count() + $record->Pages()->Count(), + "UsedCount" => $record->Parents()->Count() + $record->getAllPages()->Count(), "ClassName" => $record->i18n_singular_name(), "Title" => $record->Title, ))->renderWith("GridFieldPageElement"); @@ -244,7 +244,10 @@ public function getColumnContent($gridField, $record, $columnName) { null, null ); - $deleteButton->setAttribute("data-used-count", $record->Parents()->Count() + $record->Pages()->Count()); + $deleteButton->setAttribute( + "data-used-count", + $record->Parents()->Count() + $record->getAllPages()->Count() + ); $deleteButton->addExtraClass("col-actions__button delete-button"); $deleteButton->setButtonContent(' diff --git a/code/PageElement.php b/code/PageElement.php index 4d12587..3b6de8e 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -25,7 +25,6 @@ function canCreate($member = null) { return true; } private static $versioned_belongs_many_many = array( 'Parents' => 'PageElement', - 'Pages' => 'Page', ); private static $many_many_extraFields = array( @@ -44,6 +43,10 @@ function canCreate($member = null) { return true; } 'ID' ); + private static $better_buttons_actions = array ( + 'publishOnAllPages', + ); + public static function getAllowedPageElements() { $classes = array_values(ClassInfo::subclassesFor('PageElement')); // remove @@ -90,24 +93,81 @@ public function getGridFieldPreview() { return $this->Title; } + // Get all the versioned_belongs_many_many that might have been added by + // additional page sections from various pages. (Also contains "Parent" relation) + public function getVersionedBelongsManyMany() { + return Config::inst()->get($this->getClassName(), "versioned_belongs_many_many"); + } + + // Gets all the pages that this page element is on, plus + // adds an __PageSectionName attribute to the page object so we + // know which section this element is in. + public function getAllPages() { + $pages = ArrayList::create(); + foreach ($this->getVersionedBelongsManyMany() as $name => $relation) { + // Skip any relations that probably aren't from page sections + $splits = explode("_", $name); + if (count($splits) < 2 || mb_substr($splits[1], 0, 11) !== "PageSection") { + continue; + } + + // Add all pages (and the page section that this element is in) + foreach ($this->$name() as $page) { + $stage = Versioned::current_stage(); + Versioned::reading_stage(Versioned::get_live_stage()); + + $oldPage = DataObject::get_by_id($page->ClassName, $page->ID); + + $page->__PageSectionName = mb_substr($splits[1], 11); + $page->__PageElementVersion = $page->$splits[1]()->filter("ID", $this->ID)->First()->Version; + $page->__PageElementPublishedVersion = $oldPage->$splits[1]()->filter("ID", $this->ID)->First()->Version; + $pages->add($page); + + Versioned::reading_stage($stage); + } + } + return $pages; + } + public function getCMSFields() { $fields = parent::getCMSFields(); - $fields->removeByName('Pages'); $fields->removeByName('Parents'); $fields->removeByName("Children"); if ($this->ID && count(static::getAllowedPageElements())) { - $fields->addFieldToTab('Root.PageSections', $this->getChildrenGridField()); + $fields->addFieldToTab('Root.Children', $this->getChildrenGridField()); + } + + // Add our newest version as a readonly field + $fields->addFieldsToTab( + "Root.Main", + ReadonlyField::create("Version", "Version", $this->Version), + "Title" + ); + + // Create an array of all the pages this element is on + $pages = $this->getAllPages(); + + // Remove default fields + foreach ($this->getVersionedBelongsManyMany() as $name => $rel) { + $fields->removeByName($name); } - $fields->addFieldsToTab("Root.Pages", ReadonlyField::create("Version", "Version", $this->Version)); $config = GridFieldConfig_Base::create() ->removeComponentsByType(GridFieldDataColumns::class) ->addComponent($dataColumns = new GridFieldDataColumns()); - $dataColumns->setDisplayFields(["Title" => "Title", "getIsPublished" => "Is Published"]); - $gridField = GridField::create("Pages", "Pages", $this->Pages(), $config); + $dataColumns->setDisplayFields([ + "ID" => "ID", + "ClassName" => "Type", + "Title" => "Title", + "__PageSectionName" => "PageSection", + "__PageElementVersion" => "Element version", + "__PageElementPublishedVersion" => "Published element version", + "getPublishState" => "Page state", + ]); + $gridField = GridField::create("Pages", "Pages", $pages, $config); $fields->addFieldToTab("Root.Pages", $gridField); - + return $fields; } @@ -152,7 +212,16 @@ public function getBetterButtonsActions() { $fieldList = FieldList::create(array( BetterButton_SaveAndClose::create(), BetterButton_Save::create(), + BetterButtonCustomAction::create('publishOnAllPages', 'Publish on all pages') + ->setRedirectType(BetterButtonCustomAction::REFRESH) )); return $fieldList; } + + public function publishOnAllPages() { + foreach ($this->getAllPages() as $page) { + $page->publish(Versioned::current_stage(), Versioned::get_live_stage()); + } + return 'Published on all pages'; + } } diff --git a/code/PageSectionsExtension.php b/code/PageSectionsExtension.php index 76acb7d..36607db 100644 --- a/code/PageSectionsExtension.php +++ b/code/PageSectionsExtension.php @@ -2,7 +2,7 @@ class PageSectionsExtension extends DataExtension { // Generate the needed relations on the class - public function extraStatics($class = null, $extensionClass = null) { + public static function get_extra_config($class = null, $extensionClass = null, $args) { $versioned_many_many = array(); $many_many_extraFields = array(); @@ -20,6 +20,11 @@ public function extraStatics($class = null, $extensionClass = null) { $name = "PageSection".$section; $versioned_many_many[$name] = "PageElement"; $many_many_extraFields[$name] = array("SortOrder" => "Int"); + + // Add the inverse relation to the PageElement class + Config::inst()->update(PageElement::class, "versioned_belongs_many_many", array( + $class . "_" . $name => $class . "." . $name + )); } // Create the relations for our sections @@ -94,7 +99,7 @@ public function PageSection($name = "Main") { ); } - public function getIsPublished() { + public function getPublishState() { return DBField::create_field("HTMLText", $this->owner->latestPublished() ? "Published" : "Draft"); } } diff --git a/composer.json b/composer.json index d5b20ca..75ee6f1 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "require": { "silverstripe/framework": "~3.7", "flxlabs/silverstripe-versionedrelations": "^0.1.14", - "symbiote/silverstripe-gridfieldextensions": "^2.0.2" + "symbiote/silverstripe-gridfieldextensions": "^2.0.2", + "unclecheese/betterbuttons": "^1.3.14" }, "extra": { "installer-name": "pagesections" diff --git a/templates/PageElement.ss b/templates/PageElement.ss index 04c2f2c..d7a3c0d 100644 --- a/templates/PageElement.ss +++ b/templates/PageElement.ss @@ -1,5 +1,5 @@
          -

          $Title

          +

          $Title (v$Version)

          $Layout
          $RenderChildren($ParentList) From c29722b43bca6feeb0b15dfa7be415611ff6fbec Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Thu, 28 Jun 2018 14:06:41 +0200 Subject: [PATCH 16/18] feat(page-element): Add edit button for pages --- code/PageElement.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/PageElement.php b/code/PageElement.php index 3b6de8e..3abf948 100644 --- a/code/PageElement.php +++ b/code/PageElement.php @@ -155,7 +155,9 @@ public function getCMSFields() { $config = GridFieldConfig_Base::create() ->removeComponentsByType(GridFieldDataColumns::class) - ->addComponent($dataColumns = new GridFieldDataColumns()); + ->addComponent($dataColumns = new GridFieldDataColumns()) + ->addComponent(new GridFieldDetailForm()) + ->addComponent(new GridFieldEditButton()); $dataColumns->setDisplayFields([ "ID" => "ID", "ClassName" => "Type", From 24dbc0a02d44aa65a2572ab9fdd25bb7940baed9 Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Thu, 28 Jun 2018 14:09:07 +0200 Subject: [PATCH 17/18] chore(version): Bump version number --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 75ee6f1..d6258ef 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "flxlabs/silverstripe-pagesections", - "version": "0.1.6", + "version": "0.2.0", "description": "Adds configurable page sections and elements to your SilverStripe project.", "type": "silverstripe-module", "homepage": "http://github.com/flxlabs/silverstripe-pagesections", From 97068d11ced5499dcf2e89de17013f95a5620f1c Mon Sep 17 00:00:00 2001 From: Marco Crespi Date: Thu, 28 Jun 2018 14:09:24 +0200 Subject: [PATCH 18/18] chore(license): Update license year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 947ddef..11b7684 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2017, FLX Labs +Copyright (c) 2017 - 2018, FLX Labs All rights reserved. Redistribution and use in source and binary forms, with or without