From 69825da30ae238cbc61eb9d02f39ea1c3342c48e Mon Sep 17 00:00:00 2001 From: Loz Calver Date: Wed, 24 Jun 2020 17:23:10 +0100 Subject: [PATCH 1/4] FIX: Incorrect method call in savetreenode (fixes #2561) --- code/Controllers/CMSMain.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Controllers/CMSMain.php b/code/Controllers/CMSMain.php index c6e53309..27088263 100644 --- a/code/Controllers/CMSMain.php +++ b/code/Controllers/CMSMain.php @@ -818,7 +818,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr $virtualPages = VirtualPage::get()->filter("CopyContentFromID", $node->ID); foreach ($virtualPages as $virtualPage) { $statusUpdates['modified'][$virtualPage->ID] = array( - 'TreeTitle' => $virtualPage->TreeTitle() + 'TreeTitle' => $virtualPage->TreeTitle ); } From 4e10bcfc51f915f78a925f60adab1c175e93b2be Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Thu, 22 Oct 2020 21:21:15 +1300 Subject: [PATCH 2/4] ENH Disable option to create top-level pages based on permissions --- code/Controllers/CMSPageAddController.php | 10 ++++- tests/behat/features/create-a-page.feature | 25 ++++++++--- tests/behat/src/FixtureContext.php | 50 ++++++++++++++++++++++ 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/code/Controllers/CMSPageAddController.php b/code/Controllers/CMSPageAddController.php index a30397d1..a246826d 100644 --- a/code/Controllers/CMSPageAddController.php +++ b/code/Controllers/CMSPageAddController.php @@ -21,6 +21,7 @@ use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\ValidationResult; use SilverStripe\Security\Member; use SilverStripe\Security\Security; +use SilverStripe\SiteConfig\SiteConfig; class CMSPageAddController extends CMSPageEditController { @@ -79,7 +80,7 @@ class CMSPageAddController extends CMSPageEditController $parentModeField = new SelectionGroup( "ParentModeField", [ - new SelectionGroup_Item( + $topField = new SelectionGroup_Item( "top", null, $topTitle @@ -146,6 +147,13 @@ class CMSPageAddController extends CMSPageEditController $parentModeField->setValue('top'); } + // Check if the current user has enough permissions to create top level pages + // If not, then disable the option to do that + if (!SiteConfig::current_site_config()->canCreateTopLevel()) { + $topField->setDisabled(true); + $parentModeField->setValue('child'); + } + $actions = new FieldList( FormAction::create("doAdd", _t('SilverStripe\\CMS\\Controllers\\CMSMain.Create', "Create")) ->addExtraClass('btn-primary font-icon-plus-circled') diff --git a/tests/behat/features/create-a-page.feature b/tests/behat/features/create-a-page.feature index 29a1e101..eb776b26 100644 --- a/tests/behat/features/create-a-page.feature +++ b/tests/behat/features/create-a-page.feature @@ -5,14 +5,14 @@ Feature: Create a page Background: Given a "page" "MyPage" - Given I am logged in with "ADMIN" permissions - And I go to "/admin/pages" - Then I should see "MyPage" in the tree - And I should see a "Add new" button in CMS Content Toolbar + And a "group" "AUTHOR group" has permissions "Access to 'Pages' section" + And I am logged in with "ADMIN" permissions @javascript Scenario: I can create a page from the pages section - Given I go to "/admin/pages" + When I go to "/admin/pages" + Then I should see "MyPage" in the tree + And I should see a "Add new" button in CMS Content Toolbar When I press the "Add new" button And I select the "Page" radio button And I press the "Create" button @@ -20,10 +20,23 @@ Feature: Create a page @javascript Scenario: I can create a page under another page - Given I go to "/admin/pages" + When I go to "/admin/pages" + Then I should see "MyPage" in the tree + And I should see a "Add new" button in CMS Content Toolbar When I press the "Add new" button And I select the "Under another page" radio button And I select "MyPage" in the "#Form_AddForm_ParentID_Holder" tree dropdown And I select the "Page" radio button And I press the "Create" button Then I should see an edit page form + + Scenario: I cannot add root level pages without permission + When I go to "/admin/settings" + And I click the "Access" CMS tab + And I click the "#Form_EditForm_CanCreateTopLevelType_OnlyTheseUsers" element + And I press the "Save" button + And I click the ".cms-login-status__logout-link" element + When I am logged in with "AUTHOR" permissions + And I press the "Add new" button + Then I see the "Top level" radio button "disabled" attribute equals "1" + And I see the "Under another page" radio button "checked" attribute equals "1" diff --git a/tests/behat/src/FixtureContext.php b/tests/behat/src/FixtureContext.php index 0063372a..0d5931bb 100644 --- a/tests/behat/src/FixtureContext.php +++ b/tests/behat/src/FixtureContext.php @@ -2,6 +2,8 @@ namespace SilverStripe\CMS\Tests\Behaviour; +use Behat\Mink\Element\DocumentElement; +use Behat\Mink\Element\NodeElement; use SilverStripe\BehatExtension\Context\FixtureContext as BehatFixtureContext; use SilverStripe\CMS\Model\RedirectorPage; use SilverStripe\CMS\Model\SiteTree; @@ -63,4 +65,52 @@ class FixtureContext extends BehatFixtureContext $obj->write(); $obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE); } + + /** + * @When /^I click the "([^"]+)" element$/ + * @param $selector + */ + public function iClickTheElement($selector) + { + /** @var DocumentElement $page */ + $page = $this->getMainContext()->getSession()->getPage(); + $element = $page->find('css', $selector); + + assertNotNull($element, sprintf('Element %s not found', $selector)); + + $element->click(); + } + + /** + * @When /^I see the "([^"]+)" element$/ + * @param $selector + */ + public function iSeeTheElement($selector) + { + /** @var DocumentElement $page */ + $page = $this->getMainContext()->getSession()->getPage(); + $element = $page->find('css', $selector); + + assertNotNull($element, sprintf('Element %s not found', $selector)); + } + + /** + * Selects the specified radio button + * + * @Given /^I see the "([^"]*)" radio button "([^"]*)" attribute equals "([^"]*)"$/ + * @param string $radioLabel + * @param string $attribute + * @param string $value + */ + public function iSeeTheRadioButtonAttributeEquals($radioLabel, $attribute, $value) + { + /** @var NodeElement $radioButton */ + $session = $this->getMainContext()->getSession(); + $radioButton = $session->getPage()->find('named', [ + 'radio', + $this->getMainContext()->getXpathEscaper()->escapeLiteral($radioLabel) + ]); + assertNotNull($radioButton); + assertEquals($value, $radioButton->getAttribute($attribute)); + } } From 0ace2968b311d1c381828ae0553000a6340df2c3 Mon Sep 17 00:00:00 2001 From: bergice Date: Fri, 25 Sep 2020 18:28:30 +1200 Subject: [PATCH 3/4] Fixes pages not being scrolled to on treeview --- client/dist/js/bundle.js | 2 +- client/src/legacy/CMSMain.Tree.js | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 013c3e97..efa98b5d 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1 +1 @@ -!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,t),a.l=!0,a.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}var a=n("./client/src/boot/registerReducers.js"),i=r(a),o=n("./client/src/boot/registerComponents.js"),s=r(o);window.document.addEventListener("DOMContentLoaded",function(){(0,s.default)(),(0,i.default)()})},"./client/src/boot/registerComponents.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(3),i=r(a),o=n("./client/src/components/AnchorSelectorField/AnchorSelectorField.js"),s=r(o),l=n("./client/src/state/history/readOnePageQuery.js"),d=r(l),c=n("./client/src/state/history/rollbackPageMutation.js"),u=r(c);t.default=function(){i.default.component.register("AnchorSelectorField",s.default),i.default.transform("pages-history",function(e){e.component("HistoryViewer.pages-controller-cms-content",d.default,"PageHistoryViewer")}),i.default.transform("pages-history-revert",function(e){e.component("HistoryViewerToolbar.VersionedAdmin.HistoryViewer.SiteTree.HistoryViewerVersionDetail",u.default,"PageRevertMutation")})}},"./client/src/boot/registerReducers.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(3),i=r(a),o=n(12),s=n("./client/src/state/anchorSelector/AnchorSelectorReducer.js"),l=r(s);t.default=function(){i.default.reducer.register("cms",(0,o.combineReducers)({anchorSelector:l.default}))}},"./client/src/bundles/bundle.js":function(e,t,n){"use strict";n("./client/src/legacy/CMSMain.AddForm.js"),n("./client/src/legacy/CMSMain.EditForm.js"),n("./client/src/legacy/CMSMain.js"),n("./client/src/legacy/CMSMain.Tree.js"),n("./client/src/legacy/CMSPageHistoryController.js"),n("./client/src/legacy/RedirectorPage.js"),n("./client/src/legacy/SiteTreeURLSegmentField.js"),n("./client/src/boot/index.js")},"./client/src/components/AnchorSelectorField/AnchorSelectorField.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function s(e,t){var n=(0,_.formValueSelector)(t.formid,R.default),r=t&&t.data&&t.data.targetFieldName||"PageID",a=Number(n(e,r)||0),i=[],o=a?e.cms.anchorSelector.pages.find(function(e){return e.id===a}):null;!o||o.loadingState!==P.default.SUCCESS&&o.loadingState!==P.default.DIRTY||(i=o.anchors);var s=null;return s=o?o.loadingState:a?P.default.DIRTY:P.default.SUCCESS,{pageId:a,anchors:i,loadingState:s}}function l(e){return{actions:{anchorSelector:(0,v.bindActionCreators)(w,e)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.ConnectedAnchorSelectorField=t.Component=void 0;var d=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:this.props;if(t.loadingState===P.default.UPDATING||t.loadingState===P.default.SUCCESS||!t.pageId)return Promise.resolve();t.actions.anchorSelector.beginUpdating(t.pageId);var n=t.data.endpoint.replace(/:id/,t.pageId);return(0,m.default)(n,{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return t.actions.anchorSelector.updated(t.pageId,e),e}).catch(function(n){t.actions.anchorSelector.updateFailed(t.pageId),e.handleLoadingError(n,t)})}},{key:"getDropdownOptions",value:function(){var e=this,t=this.props.anchors.map(function(e){return{value:e}});return this.props.value&&!this.props.anchors.find(function(t){return t===e.props.value})&&t.unshift({value:this.props.value}),t}},{key:"handleChange",value:function(e){"function"==typeof this.props.onChange&&this.props.onChange(e?e.value:"")}},{key:"handleLoadingError",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.props;if(t.onLoadingError===O)throw e;return t.onLoadingError({errors:[{value:e.message,type:"error"}]})}},{key:"render",value:function(){var e={id:this.props.id},t=(0,x.default)("anchorselectorfield",this.props.extraClass),n=this.getDropdownOptions(),r=this.props.value||"",a=u.default._t("CMS.ANCHOR_SELECT_OR_TYPE","Select or enter anchor");return p.default.createElement(A.Creatable,{searchable:!0,options:n,className:t,name:this.props.name,inputProps:e,onChange:this.handleChange,onBlurResetsInput:!0,value:r,placeholder:a,labelKey:"value"})}}]),t}(C.default);L.propTypes={extraClass:M.default.string,id:M.default.string,name:M.default.string.isRequired,onChange:M.default.func,value:M.default.string,attributes:M.default.oneOfType([M.default.object,M.default.array]),pageId:M.default.number,anchors:M.default.array,loadingState:M.default.oneOf(Object.keys(P.default).map(function(e){return P.default[e]})),onLoadingError:M.default.func,data:M.default.shape({endpoint:M.default.string,targetFieldName:M.default.string})},L.defaultProps={value:"",extraClass:"",onLoadingError:O,attributes:{}};var I=(0,g.connect)(s,l)(L);t.Component=L,t.ConnectedAnchorSelectorField=I,t.default=(0,F.default)(I)},"./client/src/legacy/CMSMain.AddForm.js":function(e,t,n){"use strict";var r=n(0);(function(e){return e&&e.__esModule?e:{default:e}})(r).default.entwine("ss",function(e){e(".TreeDropdownField").entwine({OldValue:null}),e("#Form_AddForm_ParentID_Holder .treedropdownfield").entwine({onmatch:function(){this._super(),e(".cms-add-form").updateTypeList()}}),e(".cms-add-form .parent-mode :input").entwine({onclick:function(e){var t=this.closest("form").find("#Form_AddForm_ParentID_Holder .TreeDropdownField");"top"==this.val()?(t.setOldValue(t.getValue()),t.setValue(0)):(t.setValue(t.getOldValue()||0),t.setOldValue(null)),t.refresh(),t.trigger("change")}}),e(".cms-add-form").entwine({ParentCache:{},onadd:function(){var t=this;this.find("#Form_AddForm_ParentID_Holder .TreeDropdownField").bind("change",function(){t.updateTypeList()}),this.find(".SelectionGroup.parent-mode").bind("change",function(){t.updateTypeList()}),"top"==e(".cms-add-form .parent-mode :input").val()&&this.updateTypeList()},loadCachedChildren:function(e){var t=this.getParentCache();return void 0!==t[e]?t[e]:null},saveCachedChildren:function(e,t){var n=this.getParentCache();n[e]=t,this.setParentCache(n)},updateTypeList:function(){var t=this.data("hints"),n=this.find("#Form_AddForm_ParentID"),r=this.find("input[name=ParentModeField]:checked").val(),a=n.data("metadata"),i="child"===r?n.getValue():null,o=a?a.ClassName:null,s=o&&"child"===r&&i?o:"Root",l=void 0!==t[s]?t[s]:null,d=this,c=l&&void 0!==l.defaultChild?l.defaultChild:null,u=[];if(i){if(this.hasClass("loading"))return;return this.addClass("loading"),null!==(u=this.loadCachedChildren(i))?(this.updateSelectionFilter(u,c),void this.removeClass("loading")):(e.ajax({url:d.data("childfilter"),data:{ParentID:i},success:function(e){d.saveCachedChildren(i,e),d.updateSelectionFilter(e,c)},complete:function(){d.removeClass("loading")}}),!1)}u=l&&void 0!==l.disallowedChildren?l.disallowedChildren:[],this.updateSelectionFilter(u,c)},updateSelectionFilter:function(t,n){var r=this.find("#Form_AddForm_PageType div.radio.selected")[0],a=!1,i=null;if(this.find("#Form_AddForm_PageType div.radio").each(function(n,o){var s=e(this).find("input").val(),l=-1===e.inArray(s,t);o===r&&l&&(a=!0),e(this).setEnabled(l),l||e(this).setSelected(!1),i=null===i?l:i&&l}),a)var o=e(r).parents("li:first");else if(n)var o=this.find("#Form_AddForm_PageType div.radio input[value="+n+"]").parents("li:first");else var o=this.find("#Form_AddForm_PageType div.radio:not(.disabled):first");o.setSelected(!0),o.siblings().setSelected(!1),this.find("#Form_AddForm_PageType div.radio:not(.disabled)").length?this.find("button[name=action_doAdd]").removeAttr("disabled"):this.find("button[name=action_doAdd]").attr("disabled","disabled"),this.find(".message-restricted")[i?"hide":"show"]()}}),e(".cms-add-form #Form_AddForm_PageType div.radio").entwine({onclick:function(e){this.setSelected(!0)},setSelected:function(e){var t=this.find("input");e&&!t.is(":disabled")?(this.siblings().setSelected(!1),this.toggleClass("selected",!0),t.prop("checked",!0)):(this.toggleClass("selected",!1),t.prop("checked",!1))},setEnabled:function(t){e(this).toggleClass("disabled",!t),t?e(this).find("input").removeAttr("disabled"):e(this).find("input").attr("disabled","disabled").removeAttr("checked")}}),e(".cms-content-addpage-button").entwine({onclick:function(t){var n,r=e(".cms-tree"),a=e(".cms-list"),i=0;if(r.is(":visible")){var o=r.jstree("get_selected");i=o?e(o[0]).data("id"):null}else{var s=a.find('input[name="Page[GridState]"]').val();s&&(i=parseInt(JSON.parse(s).ParentID,10))}var l,d={selector:this.data("targetPanel"),pjax:this.data("pjax")};i?(n=this.data("extraParams")?this.data("extraParams"):"",l=e.path.addSearchParams(i18n.sprintf(this.data("urlAddpage"),i),n)):l=this.attr("href"),e(".cms-container").loadPanel(l,null,d),t.preventDefault(),this.blur()}})})},"./client/src/legacy/CMSMain.EditForm.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){return function(){var t=e.apply(this,arguments);return new Promise(function(e,n){function r(a,i){try{var o=t[a](i),s=o.value}catch(e){return void n(e)}if(!o.done)return Promise.resolve(s).then(function(e){r("next",e)},function(e){r("throw",e)});e(s)}return r("next")})}}var i=n(0),o=r(i),s=n(1),l=r(s),d=n("./node_modules/@silverstripe/reactstrap-confirm/dist/index.js"),c=r(d);o.default.entwine("ss",function(e){e(".cms-edit-form :input#Form_EditForm_ClassName").entwine({onchange:function(){alert(l.default._t("CMS.ALERTCLASSNAME"))}}),e(".cms-edit-form input[name=Title]").entwine({onmatch:function(){var t=this;t.data("OrigVal",t.val());var n=t.closest("form"),r=e("input:text[name=URLSegment]",n),a=e("input[name=LiveLink]",n);r.length>0&&(t._addActions(),this.bind("change",function(n){var i=t.data("OrigVal"),o=t.val();t.data("OrigVal",o),0===r.val().indexOf(r.data("defaultUrl"))&&""==a.val()?t.updateURLSegment(o):e(".update",t.parent()).show().parent(".form__field-holder").addClass("input-group"),t.updateRelatedFields(o,i),t.updateBreadcrumbLabel(o)})),this._super()},onunmatch:function(){this._super()},updateRelatedFields:function(t,n){this.parents("form").find("input[name=MetaTitle], input[name=MenuTitle]").each(function(){var r=e(this);r.val()==n&&(r.val(t),r.updatedRelatedFields&&r.updatedRelatedFields())})},updateURLSegment:function(t){var n=e("input:text[name=URLSegment]",this.closest("form")),r=n.closest(".field.urlsegment"),a=e(".update",this.parent());r.update(t),a.is(":visible")&&a.hide().parent(".form__field-holder").removeClass("input-group")},updateBreadcrumbLabel:function(t){var n=(e(".cms-edit-form input[name=ID]").val(),e("span.cms-panel-link.crumb"));t&&""!=t&&n.text(t)},_addActions:function(){var t,n=this;t=e("