diff --git a/_config/adminpanels.yml b/_config/adminpanels.yml index 0c13ba40bf..3704d1016d 100644 --- a/_config/adminpanels.yml +++ b/_config/adminpanels.yml @@ -2,4 +2,4 @@ Name: cmsdefaultadmin --- SilverStripe\Admin\AdminRootController: - default_panel: SilverStripe\CMS\Controllers\CMSPagesController + default_panel: SilverStripe\CMS\Controllers\CMSMain diff --git a/_config/cache.yml b/_config/cache.yml index 65c7b08084..a6abd6ff10 100644 --- a/_config/cache.yml +++ b/_config/cache.yml @@ -4,15 +4,15 @@ After: - '#corecache' --- SilverStripe\Core\Injector\Injector: - Psr\SimpleCache\CacheInterface.CMSMain_SiteTreeHints: + Psr\SimpleCache\CacheInterface.CMSMain_TreeHints: factory: SilverStripe\Core\Cache\CacheFactory constructor: - namespace: "CMSMain_SiteTreeHints" + namespace: "CMSMain_TreeHints" Psr\SimpleCache\CacheInterface.SiteTree_CreatableChildren: factory: SilverStripe\Core\Cache\CacheFactory constructor: namespace: "SiteTree_CreatableChildren" - Psr\SimpleCache\CacheInterface.SiteTree_PageIcons: + Psr\SimpleCache\CacheInterface.CMS_RecordIcons: factory: SilverStripe\Core\Cache\CacheFactory constructor: - namespace: "SiteTree_PageIcons" \ No newline at end of file + namespace: "CMS_RecordIcons" diff --git a/_config/config.yml b/_config/config.yml index a179cf9e0e..ef35968709 100644 --- a/_config/config.yml +++ b/_config/config.yml @@ -3,7 +3,7 @@ Name: cmsextensions --- SilverStripe\Admin\LeftAndMain: extensions: - - SilverStripe\CMS\Controllers\LeftAndMainPageIconsExtension + - SilverStripe\CMS\Controllers\LeftAndMainRecordIconsExtension - SilverStripe\CMS\Controllers\LeftAndMainBatchActionsExtension --- Name: cmsmodals diff --git a/_config/permissions.yml b/_config/permissions.yml index e1401cf66c..00bb904817 100644 --- a/_config/permissions.yml +++ b/_config/permissions.yml @@ -17,4 +17,3 @@ SilverStripe\Core\Injector\Injector: Services: - '%$SilverStripe\Security\PermissionChecker.sitetree' - '%$SilverStripe\CMS\Controllers\CMSMain' - - '%$SilverStripe\CMS\Model\SiteTree' diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 45acbd6d8a..1c7c028a0c 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1 +1 @@ -!function(){"use strict";var e={38:function(e,t,n){var a=i(n(420)),o=i(n(121));function i(e){return e&&e.__esModule?e:{default:e}}window.document.addEventListener("DOMContentLoaded",(()=>{(0,o.default)(),(0,a.default)()}))},121:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=i(n(207)),o=i(n(269));function i(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{a.default.component.register("AnchorSelectorField",o.default)}},420:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=r(n(207)),o=n(367),i=r(n(796));function r(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{a.default.reducer.register("cms",(0,o.combineReducers)({anchorSelector:i.default}))}},269:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ConnectedAnchorSelectorField=t.Component=void 0;var a=C(n(815)),o=C(n(594)),i=C(n(950)),r=n(40),s=n(367),l=n(381),d=C(n(898)),u=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_(t);if(n&&n.has(e))return n.get(e);var a={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&{}.hasOwnProperty.call(e,i)){var r=o?Object.getOwnPropertyDescriptor(e,i):null;r&&(r.get||r.set)?Object.defineProperty(a,i,r):a[i]=e[i]}return a.default=e,n&&n.set(e,a),a}(n(979)),c=C(n(996)),f=C(n(623)),h=C(n(315)),p=C(n(657)),m=C(n(432)),g=C(n(304)),v=C(n(935));function _(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(_=function(e){return e?n:t})(e)}function C(e){return e&&e.__esModule?e:{default:e}}const b=()=>null;class w extends d.default{constructor(e){super(e),this.handleChange=this.handleChange.bind(this),this.handleLoadingError=this.handleLoadingError.bind(this)}componentDidMount(){this.ensurePagesLoaded()}componentDidUpdate(e){this.props.pageId!==e.pageId&&this.ensurePagesLoaded()}ensurePagesLoaded(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.props;if(e.loadingState===c.default.UPDATING||e.loadingState===c.default.SUCCESS||!e.pageId)return Promise.resolve();let t=[];e.loadingState===c.default.FIELD_ONLY&&(t=this.props.anchors),e.actions.anchorSelector.beginUpdating(e.pageId);const n=e.data.endpoint.replace(/:id/,e.pageId);return(0,i.default)(n,{credentials:"same-origin"}).then((e=>e.json())).then((n=>{const a=[...new Set([...n,...t])];return e.actions.anchorSelector.updated(e.pageId,a),a})).catch((t=>{e.actions.anchorSelector.updateFailed(e.pageId),this.handleLoadingError(t,e)}))}getDropdownOptions(){const e=this.props.anchors.map((e=>({value:e})));return this.props.value&&!this.props.anchors.find((e=>e===this.props.value))&&e.unshift({value:this.props.value}),e}handleChange(e){"function"==typeof this.props.onChange&&this.props.onChange(e?e.value:"")}handleLoadingError(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.props;if(t.onLoadingError===b)throw e;return t.onLoadingError({errors:[{value:e.message,type:"error"}]})}render(){const{extraClass:e,CreatableSelectComponent:t}=this.props,n=(0,g.default)("anchorselectorfield",e),i=this.getDropdownOptions(),r=this.props.value||"",s=a.default._t("CMS.ANCHOR_SELECT_OR_TYPE","Select or enter anchor");return o.default.createElement(p.default,null,o.default.createElement(t,{isSearchable:!0,isClearable:!0,options:i,className:n,name:this.props.name,onChange:this.handleChange,value:{value:r},noOptionsMessage:()=>a.default._t("CMS.ANCHOR_NO_OPTIONS","No options"),placeholder:s,getOptionLabel:e=>{let{value:t}=e;return t},classNamePrefix:"anchorselectorfield"}))}}t.Component=w,w.propTypes={extraClass:v.default.string,id:v.default.string,name:v.default.string.isRequired,onChange:v.default.func,value:v.default.string,attributes:v.default.oneOfType([v.default.object,v.default.array]),pageId:v.default.number,anchors:v.default.array,loadingState:v.default.oneOf(Object.keys(c.default).map((e=>c.default[e]))),onLoadingError:v.default.func,data:v.default.shape({endpoint:v.default.string,targetFieldName:v.default.string})},w.defaultProps={value:"",extraClass:"",onLoadingError:b,attributes:{},CreatableSelectComponent:h.default};const S=t.ConnectedAnchorSelectorField=(0,r.connect)((function(e,t){const n=(0,l.formValueSelector)(t.formid,m.default),a=t&&t.data&&t.data.targetFieldName||"PageID",o=Number(n(e,a)||0);let i=[];const r=o?e.cms.anchorSelector.pages.find((e=>e.id===o)):null;!r||r.loadingState!==c.default.SUCCESS&&r.loadingState!==c.default.DIRTY&&r.loadingState!==c.default.FIELD_ONLY||(i=r.anchors);let s=null;return s=r?r.loadingState:o?c.default.DIRTY:c.default.SUCCESS,{pageId:o,anchors:i,loadingState:s}}),(function(e){return{actions:{anchorSelector:(0,s.bindActionCreators)(u,e)}}}))(w);t.default=(0,f.default)(S)},586:function(e,t,n){var a;((a=n(669))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e(".TreeDropdownField").entwine({OldValue:null}),e("#Form_AddForm_ParentID_Holder .treedropdownfield").entwine({onmatch(){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").on("change",(function(){t.updateTypeList()})),this.find(".SelectionGroup.parent-mode").on("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"),a=this.find("input[name=ParentModeField]:checked").val(),o=n.data("metadata"),i="child"===a?n.getValue():null,r=o?o.ClassName:null,s=r&&"child"===a&&i?r:"Root",l=void 0!==t[s]?t[s]:null,d=this,u=l&&void 0!==l.defaultChild?l.defaultChild:null,c=[];if(i){if(this.hasClass("loading"))return;return this.addClass("loading"),null!==(c=this.loadCachedChildren(i))?(this.updateSelectionFilter(c,u),void this.removeClass("loading")):(e.ajax({url:d.data("childfilter"),data:{ParentID:i},success:function(e){d.saveCachedChildren(i,e),d.updateSelectionFilter(e,u)},complete:function(){d.removeClass("loading")}}),!1)}c=l&&void 0!==l.disallowedChildren?l.disallowedChildren:[],this.updateSelectionFilter(c,u)},updateSelectionFilter:function(t,n){var a=this.find("#Form_AddForm_PageType div.radio.selected")[0],o=!1,i=null;if(this.find("#Form_AddForm_PageType div.radio").each((function(n,r){var s=e(this).find("input").val(),l=-1===e.inArray(s,t);r===a&&l&&(o=!0),e(this).setEnabled(l),l||e(this).setSelected(!1),i=(null===i||i)&&l})),o)var r=e(a).parents("li:first");else if(n)r=this.find("#Form_AddForm_PageType div.radio input[value="+n+"]").parents("li:first");else r=this.find("#Form_AddForm_PageType div.radio:not(.disabled):first");r.setSelected(!0),r.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,a=e(".cms-tree"),o=e(".cms-list"),i=0;if(a.is(":visible")){var r=a.jstree("get_selected");i=r?e(r[0]).data("id"):null}else{var s=o.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()}})}))},677:function(e,t,n){var a=r(n(669)),o=r(n(815)),i=r(n(216));function r(e){return e&&e.__esModule?e:{default:e}}a.default.entwine("ss",(function(e){e(".cms-edit-form :input#Form_EditForm_ClassName").entwine({onchange:function(){alert(o.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"),a=e("input:text[name=URLSegment]",n),o=e("input[name=LiveLink]",n);a.length>0&&(t._addActions(),this.on("change",(function(n){var i=t.data("OrigVal"),r=t.val();t.data("OrigVal",r),0===a.val().indexOf(a.data("defaultUrl"))&&""==o.val()?t.updateURLSegment(r):e(".update",t.parent()).show().parent(".form__field-holder").addClass("input-group"),t.updateRelatedFields(r,i),t.updateBreadcrumbLabel(r)}))),this._super()},onunmatch:function(){this._super()},updateRelatedFields:function(t,n){this.parents("form").find("input[name=MetaTitle], input[name=MenuTitle]").each((function(){var a=e(this);a.val()==n&&(a.val(t),a.updatedRelatedFields&&a.updatedRelatedFields())}))},updateURLSegment:function(t){var n=e("input:text[name=URLSegment]",this.closest("form")).closest(".field.urlsegment"),a=e(".update",this.parent());n.update(t),a.is(":visible")&&a.hide().parent(".form__field-holder").removeClass("input-group")},updateBreadcrumbLabel:function(t){e(".cms-edit-form input[name=ID]").val();var n=e("span.cms-panel-link.crumb");t&&""!=t&&n.text(t)},_addActions:function(){var t,n=this;(t=e(" + <% end_if %> diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_LeftPanel.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_LeftPanel.ss new file mode 100644 index 0000000000..12e1feba9b --- /dev/null +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_LeftPanel.ss @@ -0,0 +1,24 @@ +
+
+ <% if $TreeIsFiltered %> + <% include SilverStripe\\Admin\\BackLink_Button Backlink=$BreadcrumbsBacklink %> + <% end_if %> + <% if $CurrentRecord %> + <%-- Explicit breadcrumb item for this menu section --%> +
+ +
+ <% else %> + <%-- Full breadcrumbs (useful for tree view which isn't available when viewing an edit form) --%> + <% include SilverStripe\\Admin\\CMSBreadcrumbs %> + <% end_if %> + <% include SilverStripe\\CMS\\Controllers\\CMSMain_Filter %> +
+
+ +
+
+
+
+ $RecordList +
diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ListView.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ListView.ss index aed42dfefd..e9257f3584 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ListView.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ListView.ss @@ -1,6 +1,6 @@ -<% include SilverStripe\\CMS\\Controllers\\CMSPagesController_ContentToolActions %> +<% include SilverStripe\\CMS\\Controllers\\CMSMain_ContentToolActions %> -
+
$AddForm
diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_PageList_Sidebar.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_PageList_Sidebar.ss deleted file mode 100644 index 296f8a6e05..0000000000 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_PageList_Sidebar.ss +++ /dev/null @@ -1 +0,0 @@ -<% include SilverStripe\\CMS\\Controllers\\CMSMain_PageList %> diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_PageList.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_RecordList.ss similarity index 100% rename from templates/SilverStripe/CMS/Controllers/Includes/CMSMain_PageList.ss rename to templates/SilverStripe/CMS/Controllers/Includes/CMSMain_RecordList.ss diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_SubTree.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_SubTree.ss index 34140b294a..adf95e06a2 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_SubTree.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_SubTree.ss @@ -4,13 +4,13 @@ <% if $limited %> <% else_if $children %> <% end_if %> <% if not $node.IsInDB %> diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Tools.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Tools.ss index d3d94aebda..356780a793 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Tools.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_Tools.ss @@ -1,26 +1,13 @@ -
-
-
- - <% include SilverStripe\\CMS\\Controllers\\CMSMain_Filter %> +<%-- If we're editing a record, include the left panel and allow it to be collapsed --%> +<% if $CurrentRecord %> +
+ <% include SilverStripe\\CMS\\Controllers\\CMSMain_LeftPanel %> +
+

$CMSTreeTitle

-
-
-
-
+
+ » + «
- $PageListSidebar -
-
-

$SiteConfig.Title

-
-
- » - «
-
+<% end_if %> diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeNode.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeNode.ss index 3a3f0e8bb1..402c37de7a 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeNode.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeNode.ss @@ -1,6 +1,6 @@ -
  •   -   - {$node.TreeTitle} +
  •   +   + {$TreeTitle} $SubTree
  • diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss index d28b1e8431..05fc7dd27d 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_TreeView.ss @@ -1,6 +1,6 @@ -<% include SilverStripe\\CMS\\Controllers\\CMSPagesController_ContentToolActions View='Tree' %> +<% include SilverStripe\\CMS\\Controllers\\CMSMain_ContentToolActions View='Tree' %> -
    +
    $AddForm
    @@ -17,15 +17,15 @@ $ExtraTreeTools data-url-tree="$LinkWithSearch($Link('getsubtree')).ATT" data-url-savetreenode="$Link('savetreenode').ATT" data-url-updatetreenodes="$Link('updatetreenodes').ATT" - data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1', 'ParentID=%s&PageType=%s').ATT}" - data-url-editpage="$LinkPageEdit('%s').ATT" + data-url-addpage="{$LinkRecordAdd('AddForm/?action_doAdd=1', 'ParentID=%s&RecordType=%s').ATT}" + data-url-editpage="$LinkRecordEdit('%s').ATT" data-url-duplicate="{$Link('duplicate/%s').ATT}" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s').ATT}" data-url-listview="{$Link('?view=list').ATT}" - data-hints="$SiteTreeHints.ATT" + data-hints="$TreeHints.ATT" data-childfilter="$Link('childfilter').ATT" data-extra-params="SecurityID=$SecurityID.ATT"> - $SiteTreeAsUL + $TreeAsUL
    <% else %> @@ -33,14 +33,14 @@ $ExtraTreeTools data-url-tree="$LinkWithSearch($Link('getsubtree')).ATT" data-url-savetreenode="$Link('savetreenode').ATT" data-url-updatetreenodes="$Link('updatetreenodes').ATT" - data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1', 'ParentID=%s&PageType=%s').ATT}" - data-url-editpage="$LinkPageEdit('%s').ATT" + data-url-addpage="{$LinkRecordAdd('AddForm/?action_doAdd=1', 'ParentID=%s&RecordType=%s').ATT}" + data-url-editpage="$LinkRecordEdit('%s').ATT" data-url-duplicate="{$Link('duplicate/%s').ATT}" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s').ATT}" data-url-listview="{$Link('?view=list').ATT}" - data-hints="$SiteTreeHints.ATT" + data-hints="$TreeHints.ATT" data-childfilter="$Link('childfilter').ATT" data-extra-params="SecurityID=$SecurityID.ATT"> - $SiteTreeAsUL + $TreeAsUL
    <% end_if %> diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss index c9709e9ad8..a91c480623 100644 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss +++ b/templates/SilverStripe/CMS/Controllers/Includes/CMSMain_ViewControls.ss @@ -1,18 +1,18 @@
    <% if not $TreeIsFiltered %> - <%-- Change to data-pjax-target="Content-PageList" to enable in-edit listview --%> + <%-- Change to data-pjax-target="Content-RecordList" to enable in-edit listview --%> <% end_if %>
    diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSPageAddController_Content.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSPageAddController_Content.ss deleted file mode 100644 index b8e50ac91e..0000000000 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSPageAddController_Content.ss +++ /dev/null @@ -1,45 +0,0 @@ -
    - <% with $AddForm %> -
    -
    -
    - -
    -
    - -
    - <% if $Message %> -

    $Message

    - <% else %> - - <% end_if %> - -
    - <% if $Legend %>$Legend<% end_if %> - <% loop $Fields %> - $FieldHolder - <% end_loop %> -
    -
    - -
    - <% if $Actions %> -
    - <% loop $Actions %> - $Field - <% end_loop %> -
    - <% end_if %> -
    -
    - <% end_with %> -
    diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Content.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Content.ss deleted file mode 100644 index 956e7536a4..0000000000 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Content.ss +++ /dev/null @@ -1,18 +0,0 @@ -
    -
    -
    - <% if $TreeIsFiltered %> - - <%t SilverStripe\Admin\LeftAndMain.NavigateUp "Return to Pages" %> - - <% end_if %> - <% include SilverStripe\\Admin\\CMSBreadcrumbs %> - <% include SilverStripe\\CMS\\Controllers\\CMSMain_Filter %> -
    -
    - -
    - $Tools - $PageList -
    -
    diff --git a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Tools.ss b/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Tools.ss deleted file mode 100644 index d82c105fb9..0000000000 --- a/templates/SilverStripe/CMS/Controllers/Includes/CMSPagesController_Tools.ss +++ /dev/null @@ -1,3 +0,0 @@ -
    -
    -
    diff --git a/tests/behat/features/create-a-page.feature b/tests/behat/features/create-a-page.feature index cec74cd147..e52d98d926 100644 --- a/tests/behat/features/create-a-page.feature +++ b/tests/behat/features/create-a-page.feature @@ -31,12 +31,12 @@ Feature: Create a page # Virtual page doesn't allow children, page radio button below should be disabled And I select "MyVirtualPage" in the "#Form_AddForm_ParentID_Holder" tree dropdown And I wait for 2 seconds - Then I should see a "#Form_AddForm_PageType_Page[disabled]" element + Then I should see a "#Form_AddForm_RecordType_Page[disabled]" element # Normal pages allows children, page radio button below should not be disabled When I select "MyPage" in the "#Form_AddForm_ParentID_Holder" tree dropdown And I wait for 2 seconds And I select the "Page" radio button - Then I should not see a "#Form_AddForm_PageType_Page[disabled]" element + Then I should not see a "#Form_AddForm_RecordType_Page[disabled]" element And I press the "Create" button Then I should see an edit page form diff --git a/tests/behat/features/duplicate-a-page.feature b/tests/behat/features/duplicate-a-page.feature index 2a3884ac62..129f97a457 100644 --- a/tests/behat/features/duplicate-a-page.feature +++ b/tests/behat/features/duplicate-a-page.feature @@ -18,7 +18,7 @@ Feature: Duplicate a page And I right click on "Page1" in the tree And I hover on "Duplicate" in the context menu And I click on "This page and subpages" in the context menu - Then I should see a "Duplicated 'Page1' and children successfully" success toast + Then I should see a "Duplicated Page "Page1" and children" success toast When I fill in "MenuTitle" with "Duplicate Page" And I press the "Publish" button diff --git a/tests/behat/features/insert-a-link.feature b/tests/behat/features/insert-a-link.feature index 960967622e..2466bb402c 100644 --- a/tests/behat/features/insert-a-link.feature +++ b/tests/behat/features/insert-a-link.feature @@ -29,7 +29,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "awesome" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can wrap an image in a link to an internal page Given I fill in the "Content" HTML field with "

    " @@ -43,7 +43,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can edit a link to an internal page Given I fill in the "Content" HTML field with "awesome" @@ -60,7 +60,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "awesome" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can link to an external URL Given I select "awesome" in the "Content" HTML field @@ -73,7 +73,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "awesome" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can wrap an image in a link to an external URL Given I fill in the "Content" HTML field with "

    " @@ -87,7 +87,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can edit an external link Given I fill in the "Content" HTML field with "

    My awesome content" @@ -102,7 +102,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "awesome" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can remove an external link Given I fill in the "Content" HTML field with "My awesome content" @@ -112,7 +112,7 @@ So that I can link to a external website or a page on my site And the "Content" HTML field should not contain "http://silverstripe.org" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can navigate list of Site tree links by clicking on the dropdown elements When I select "awesome" in the "Content" HTML field diff --git a/tests/behat/features/insert-anchor-link.feature b/tests/behat/features/insert-anchor-link.feature index 931405f2f7..8527d55eea 100644 --- a/tests/behat/features/insert-anchor-link.feature +++ b/tests/behat/features/insert-anchor-link.feature @@ -28,7 +28,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "awesome" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can wrap an image in a link to an anchor in an internal page Given I fill in the "Content" HTML field with "

    " @@ -44,7 +44,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can link to an anchor from a dataobject on the current page When I select "awesome" in the "Content" HTML field @@ -59,7 +59,7 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "awesome" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast Scenario: I can link to an unsaved anchor in the current page Given I fill in the "Content" HTML field with "

    My awesome content

    unsaved content

    " @@ -77,4 +77,4 @@ So that I can link to a external website or a page on my site Then the "Content" HTML field should contain "awesome" # Required to avoid "unsaved changes" browser dialog When I press the "Save" button - Then I should see a "Saved 'About Us' successfully" success toast + Then I should see a "Saved Page "About Us"" success toast diff --git a/tests/behat/features/sitetree.feature b/tests/behat/features/sitetree.feature index d6ac54971b..bb38d84921 100644 --- a/tests/behat/features/sitetree.feature +++ b/tests/behat/features/sitetree.feature @@ -44,7 +44,7 @@ Feature: Sitetree When I click on the ".toast__close" element # Wait a little time to ensure the last toast is cleared And I wait for 2 seconds - + # Unpublish pages in a batch When I click on the "#record-2 .jstree-checkbox" element And I select "Unpublish" from the "Form_BatchActionsForm_Action" field with javascript @@ -66,10 +66,10 @@ Feature: Sitetree Then I should not see "Three" # Toggle list and tree views - Then I should not see a "#Form_ListViewForm_Page" element + Then I should not see a "#Form_ListViewForm_Record" element When I click on the "[data-view='listview']" element And I wait for 5 seconds - Then I should see a "#Form_ListViewForm_Page" element + Then I should see a "#Form_ListViewForm_Record" element When I click on the "[data-view='treeview']" element And I wait for 5 seconds - Then I should not see a "#Form_ListViewForm_Page" element + Then I should not see a "#Form_ListViewForm_Record" element diff --git a/tests/php/Controllers/CMSMainTest.php b/tests/php/Controllers/CMSMainTest.php index 0a9d0c8543..0312f74e37 100644 --- a/tests/php/Controllers/CMSMainTest.php +++ b/tests/php/Controllers/CMSMainTest.php @@ -2,11 +2,16 @@ namespace SilverStripe\CMS\Tests\Controllers; +use LogicException; +use PHPUnit\Framework\Attributes\DataProvider; use Psr\SimpleCache\CacheInterface; +use ReflectionMethod; use SilverStripe\Admin\CMSBatchActionHandler; use SilverStripe\CMS\Controllers\CMSMain; use SilverStripe\CMS\Model\RedirectorPage; use SilverStripe\CMS\Model\SiteTree; +use SilverStripe\CMS\Tests\Controllers\CMSMainTest\TestHierarchicalDataObject; +use SilverStripe\CMS\Tests\Controllers\CMSMainTest\TestHierarchicalDataObjectWithSort; use SilverStripe\CMS\Tests\Controllers\CMSMainTest\TestStatusFlagsPage; use SilverStripe\Control\Controller; use SilverStripe\Control\HTTPRequest; @@ -35,6 +40,8 @@ class CMSMainTest extends FunctionalTest protected static $extraDataObjects = [ TestStatusFlagsPage::class, + TestHierarchicalDataObject::class, + TestHierarchicalDataObjectWithSort::class, ]; protected function setUp(): void @@ -50,15 +57,15 @@ protected function setUp(): void } } - public function testSiteTreeHints() + public function testTreeHints() { - $cache = Injector::inst()->get(CacheInterface::class . '.CMSMain_SiteTreeHints'); + $cache = Injector::inst()->get(CacheInterface::class . '.CMSMain_TreeHints'); // Login as user with root creation privileges $user = $this->objFromFixture(Member::class, 'rootedituser'); Security::setCurrentUser($user); $cache->clear(); - $rawHints = singleton(CMSMain::class)->SiteTreeHints(); + $rawHints = singleton(CMSMain::class)->TreeHints(); $this->assertNotNull($rawHints); $rawHints = preg_replace('/^"(.*)"$/', '$1', Convert::xml2raw($rawHints) ?? ''); @@ -127,7 +134,7 @@ public function testChildFilter() * Test that getCMSFields works on each page type. * Mostly, this is just checking that the method doesn't return an error */ - public function testThatGetCMSFieldsWorksOnEveryPageType() + public function testThatGetCMSFieldsWorksOnEveryRecordType() { $classes = ClassInfo::subclassesFor(SiteTree::class); array_shift($classes); @@ -245,10 +252,10 @@ public function testCreationOfTopLevelPage() Security::setCurrentUser($cmsUser); $this->get('admin/pages/add'); $response = $this->post( - 'admin/pages/add/AddForm', + 'admin/pages/AddForm', [ 'ParentID' => '0', - 'PageType' => RedirectorPage::class, + 'RecordType' => RedirectorPage::class, 'Locale' => 'en_US', 'action_doAdd' => 1, 'ajax' => 1, @@ -265,10 +272,10 @@ public function testCreationOfTopLevelPage() $response = $this->get('admin/pages/add'); $response = $this->post( - 'admin/pages/add/AddForm', + 'admin/pages/AddForm', [ 'ParentID' => '0', - 'PageType' => RedirectorPage::class, + 'RecordType' => RedirectorPage::class, 'Locale' => 'en_US', 'action_doAdd' => 1, 'ajax' => 1, @@ -296,10 +303,10 @@ public function testCreationOfRestrictedPage() // Create toplevel page $this->get('admin/pages/add'); $response = $this->post( - 'admin/pages/add/AddForm', + 'admin/pages/AddForm', [ 'ParentID' => '0', - 'PageType' => CMSMainTest_ClassA::class, + 'RecordType' => CMSMainTest_ClassA::class, 'Locale' => 'en_US', 'action_doAdd' => 1, 'ajax' => 1, @@ -316,10 +323,10 @@ public function testCreationOfRestrictedPage() // Create allowed child $this->get('admin/pages/add'); $response = $this->post( - 'admin/pages/add/AddForm', + 'admin/pages/AddForm', [ 'ParentID' => $newPageId, - 'PageType' => CMSMainTest_ClassB::class, + 'RecordType' => CMSMainTest_ClassB::class, 'Locale' => 'en_US', 'action_doAdd' => 1, 'ajax' => 1, @@ -342,10 +349,10 @@ public function testCreationOfRestrictedPage() // Create disallowed child $this->get('admin/pages/add'); $response = $this->post( - 'admin/pages/add/AddForm', + 'admin/pages/AddForm', [ 'ParentID' => $newPageId, - 'PageType' => RedirectorPage::class, + 'RecordType' => RedirectorPage::class, 'Locale' => 'en_US', 'action_doAdd' => 1, 'ajax' => 1, @@ -669,13 +676,15 @@ public function testChangeClass() $this->assertEquals('Class A', $newPage->Title); } - public function testSiteTreeHintsCache() + public function testTreeHintsCache() { $cms = CMSMain::create(); + $reflectionAllowedSubclasses = new ReflectionMethod($cms, 'getAllowedSubClasses'); + $reflectionAllowedSubclasses->setAccessible(true); /** @var Member $user */ $user = $this->objFromFixture(Member::class, 'rootedituser'); Security::setCurrentUser($user); - $pageClass = array_values(SiteTree::page_type_classes())[0]; + $pageClass = array_values($reflectionAllowedSubclasses->invoke($cms))[0]; $mockPageMissesCache = $this->getMockBuilder($pageClass) ->onlyMethods(['canCreate']) ->getMock(); @@ -693,31 +702,31 @@ public function testSiteTreeHintsCache() // Initially, cache misses (1) Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Now it hits Injector::inst()->registerService($mockPageHitsCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Mutating member record invalidates cache. Misses (2) $user->FirstName = 'changed'; $user->write(); Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Now it hits again Injector::inst()->registerService($mockPageHitsCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); // Different user. Misses. (3) $user = $this->objFromFixture(Member::class, 'allcmssectionsuser'); Security::setCurrentUser($user); Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $hints = $cms->SiteTreeHints(); + $hints = $cms->TreeHints(); $this->assertNotNull($hints); } @@ -761,21 +770,118 @@ public function testSearchField() ); } - public function testCanOrganiseSitetree() + public function testCanOrganiseTree() { $cms = CMSMain::create(); - $this->assertFalse($cms->CanOrganiseSitetree()); + $this->assertFalse($cms->CanOrganiseTree()); $this->logInWithPermission('CMS_ACCESS_CMSMain'); - $this->assertFalse($cms->CanOrganiseSitetree()); + $this->assertFalse($cms->CanOrganiseTree()); $this->logOut(); $this->logInWithPermission('SITETREE_REORGANISE'); - $this->assertTrue($cms->CanOrganiseSitetree()); + $this->assertTrue($cms->CanOrganiseTree()); $this->logOut(); $this->logInWithPermission('ADMIN'); - $this->assertTrue($cms->CanOrganiseSitetree()); + $this->assertTrue($cms->CanOrganiseTree()); + } + + public function testGetCreatableSubClassesCache() + { + // Use injector because CMSMain defines some injectable dependencies + $cms = CMSMain::create(); + $reflectionMethod = new ReflectionMethod($cms, 'getCreatableSubClasses'); + $reflectionMethod->setAccessible(true); + + $siteTree = new SiteTree(); + $user = $this->objFromFixture(Member::class, 'allcmssectionsuser'); + Security::setCurrentUser($user); + $classes = ClassInfo::getValidSubClasses(SiteTree::class); + SiteTree::singleton()->updateAllowedSubClasses($classes); + $pageClass = array_values($classes)[0]; + + $mockPageMissesCache = $this->getMockBuilder($pageClass) + ->onlyMethods(['canCreate']) + ->getMock(); + $mockPageMissesCache + ->expects($this->exactly(3)) + ->method('canCreate'); + + $mockPageHitsCache = $this->getMockBuilder($pageClass) + ->onlyMethods(['canCreate']) + ->getMock(); + $mockPageHitsCache + ->expects($this->never()) + ->method('canCreate'); + + // Initially, cache misses (1) + Injector::inst()->registerService($mockPageMissesCache, $pageClass); + $result = $reflectionMethod->invoke($cms, $siteTree); + $this->assertNotNull($result); + + // Now it hits + Injector::inst()->registerService($mockPageHitsCache, $pageClass); + $result = $reflectionMethod->invoke($cms, $siteTree); + $this->assertNotNull($result); + + + // Mutating member record invalidates cache. Misses (2) + $user->FirstName = 'changed'; + $user->write(); + Injector::inst()->registerService($mockPageMissesCache, $pageClass); + $result = $reflectionMethod->invoke($cms, $siteTree); + $this->assertNotNull($result); + + // Now it hits again + Injector::inst()->registerService($mockPageHitsCache, $pageClass); + $result = $reflectionMethod->invoke($cms, $siteTree); + $this->assertNotNull($result); + + // Different user. Misses. (3) + $user = $this->objFromFixture(Member::class, 'rootedituser'); + Security::setCurrentUser($user); + Injector::inst()->registerService($mockPageMissesCache, $pageClass); + $result = $reflectionMethod->invoke($cms, $siteTree); + $this->assertNotNull($result); + } + + public static function provideInit(): array + { + return [ + [ + 'class' => DataObject::class, + 'throwsException' => true, + ], + [ + 'class' => TestHierarchicalDataObject::class, + 'throwsException' => true, + ], + [ + 'class' => TestHierarchicalDataObjectWithSort::class, + 'throwsException' => false, + ], + [ + 'class' => SiteTree::class, + 'throwsException' => false, + ], + ]; + } + + #[DataProvider('provideInit')] + public function testInit(string $class, bool $throwsException): void + { + CMSMain::config()->set('model_class', $class); + // Use injector because CMSMain defines some injectable dependencies + $cms = CMSMain::create(); + $initReflection = new ReflectionMethod($cms, 'init'); + $initReflection->setAccessible(true); + if ($throwsException) { + $this->expectException(LogicException::class); + } else { + $this->expectNotToPerformAssertions(); + } + $initReflection->invoke($cms); } } diff --git a/tests/php/Controllers/CMSMainTest/TestHierarchicalDataObject.php b/tests/php/Controllers/CMSMainTest/TestHierarchicalDataObject.php new file mode 100644 index 0000000000..78ea216b6f --- /dev/null +++ b/tests/php/Controllers/CMSMainTest/TestHierarchicalDataObject.php @@ -0,0 +1,18 @@ + 'Int', + ]; + + private static array $extensions = [ + Hierarchy::class, + ]; +} diff --git a/tests/php/Controllers/CMSMainTest/TestHierarchicalDataObjectWithSort.php b/tests/php/Controllers/CMSMainTest/TestHierarchicalDataObjectWithSort.php new file mode 100644 index 0000000000..bc380c5545 --- /dev/null +++ b/tests/php/Controllers/CMSMainTest/TestHierarchicalDataObjectWithSort.php @@ -0,0 +1,20 @@ + 'Int', + ]; + + private static string $sort_field = 'Sort'; + + private static array $extensions = [ + Hierarchy::class, + ]; +} diff --git a/tests/php/Controllers/CMSSiteTreeFilterTest.php b/tests/php/Controllers/CMSSiteTreeFilterTest.php index bf2a925323..9eefb1ce1e 100644 --- a/tests/php/Controllers/CMSSiteTreeFilterTest.php +++ b/tests/php/Controllers/CMSSiteTreeFilterTest.php @@ -24,8 +24,8 @@ public function testSearchFilterEmpty() $f = new CMSSiteTreeFilter_Search(); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($page1)); - $this->assertTrue($f->isPageIncluded($page2)); + $this->assertTrue($f->isRecordIncluded($page1)); + $this->assertTrue($f->isRecordIncluded($page2)); } public function testSearchFilterByTitle() @@ -36,8 +36,8 @@ public function testSearchFilterByTitle() $f = new CMSSiteTreeFilter_Search(['Title' => 'Page 1']); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($page1)); - $this->assertFalse($f->isPageIncluded($page2)); + $this->assertTrue($f->isRecordIncluded($page1)); + $this->assertFalse($f->isRecordIncluded($page2)); $this->assertEquals(1, count($results ?? [])); $this->assertEquals( ['ID' => $page1->ID, 'ParentID' => 0], @@ -50,10 +50,10 @@ public function testUrlSegmentFilter() $page = $this->objFromFixture(SiteTree::class, 'page8'); $filter = CMSSiteTreeFilter_Search::create(['Term' => 'lake-wanaka+adventure']); - $this->assertTrue($filter->isPageIncluded($page)); + $this->assertTrue($filter->isRecordIncluded($page)); $filter = CMSSiteTreeFilter_Search::create(['URLSegment' => 'lake-wanaka+adventure']); - $this->assertTrue($filter->isPageIncluded($page)); + $this->assertTrue($filter->isRecordIncluded($page)); } public function testIncludesParentsForNestedMatches() @@ -64,8 +64,8 @@ public function testIncludesParentsForNestedMatches() $f = new CMSSiteTreeFilter_Search(['Title' => 'Page 3b']); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($parent)); - $this->assertTrue($f->isPageIncluded($child)); + $this->assertTrue($f->isRecordIncluded($parent)); + $this->assertTrue($f->isRecordIncluded($child)); $this->assertEquals(1, count($results ?? [])); $this->assertEquals( ['ID' => $child->ID, 'ParentID' => $parent->ID], @@ -91,8 +91,8 @@ public function testChangedPagesFilter() $f = new CMSSiteTreeFilter_ChangedPages(['Term' => 'Changed']); $results = $f->pagesIncluded(); - $this->assertTrue($f->isPageIncluded($changedPage)); - $this->assertFalse($f->isPageIncluded($unchangedPage)); + $this->assertTrue($f->isRecordIncluded($changedPage)); + $this->assertFalse($f->isRecordIncluded($unchangedPage)); $this->assertEquals(1, count($results ?? [])); $this->assertEquals( ['ID' => $changedPage->ID, 'ParentID' => 0], @@ -130,11 +130,11 @@ public function testDeletedPagesFilter() ); $f = new CMSSiteTreeFilter_DeletedPages(['Term' => 'Page']); - $this->assertTrue($f->isPageIncluded($deletedPage)); + $this->assertTrue($f->isRecordIncluded($deletedPage)); // Check that only changed pages are returned $f = new CMSSiteTreeFilter_DeletedPages(['Term' => 'No Matches']); - $this->assertFalse($f->isPageIncluded($deletedPage)); + $this->assertFalse($f->isRecordIncluded($deletedPage)); } public function testStatusDraftPagesFilter() @@ -148,16 +148,16 @@ public function testStatusDraftPagesFilter() // Check draft page is shown $f = new CMSSiteTreeFilter_StatusDraftPages(['Term' => 'Page']); - $this->assertTrue($f->isPageIncluded($draftPage)); + $this->assertTrue($f->isRecordIncluded($draftPage)); // Check filter respects parameters $f = new CMSSiteTreeFilter_StatusDraftPages(['Term' => 'No Match']); - $this->assertEmpty($f->isPageIncluded($draftPage)); + $this->assertEmpty($f->isRecordIncluded($draftPage)); // Ensures empty array returned if no data to show $f = new CMSSiteTreeFilter_StatusDraftPages(); $draftPage->delete(); - $this->assertEmpty($f->isPageIncluded($draftPage)); + $this->assertEmpty($f->isRecordIncluded($draftPage)); } public function testDateFromToLastSameDate() @@ -171,7 +171,7 @@ public function testDateFromToLastSameDate() 'LastEditedTo' => $date, ]); $this->assertTrue( - $filter->isPageIncluded($draftPage), + $filter->isRecordIncluded($draftPage), 'Using the same date for from and to should show find that page' ); } @@ -189,16 +189,16 @@ public function testStatusRemovedFromDraftFilter() // Check live-only page is included $f = new CMSSiteTreeFilter_StatusRemovedFromDraftPages(['LastEditedFrom' => '2000-01-01 00:00']); - $this->assertTrue($f->isPageIncluded($removedDraftPage)); + $this->assertTrue($f->isRecordIncluded($removedDraftPage)); // Check filter is respected $f = new CMSSiteTreeFilter_StatusRemovedFromDraftPages(['LastEditedTo' => '1999-01-01 00:00']); - $this->assertEmpty($f->isPageIncluded($removedDraftPage)); + $this->assertEmpty($f->isRecordIncluded($removedDraftPage)); // Ensures empty array returned if no data to show $f = new CMSSiteTreeFilter_StatusRemovedFromDraftPages(); $removedDraftPage->delete(); - $this->assertEmpty($f->isPageIncluded($removedDraftPage)); + $this->assertEmpty($f->isRecordIncluded($removedDraftPage)); } public function testStatusDeletedFilter() @@ -214,10 +214,10 @@ public function testStatusDeletedFilter() // Check deleted page is included $f = new CMSSiteTreeFilter_StatusDeletedPages(['Title' => 'Page']); - $this->assertTrue($f->isPageIncluded($checkParentExists)); + $this->assertTrue($f->isRecordIncluded($checkParentExists)); // Check filter is respected $f = new CMSSiteTreeFilter_StatusDeletedPages(['Title' => 'Bobby']); - $this->assertFalse($f->isPageIncluded($checkParentExists)); + $this->assertFalse($f->isRecordIncluded($checkParentExists)); } } diff --git a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest.php b/tests/php/Controllers/LeftAndMainPageIconsExtensionTest.php deleted file mode 100644 index 4529a826d7..0000000000 --- a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest.php +++ /dev/null @@ -1,30 +0,0 @@ -generatePageIconsCss(); - $this->assertStringNotContainsString('some invalid string', $css); - $this->assertStringContainsString( - 'tests/php/Controllers/LeftAndMainPageIconsExtensionTest/icon_b.jpg?m=', - $css - ); - $this->assertStringContainsString( - 'tests/php/Controllers/LeftAndMainPageIconsExtensionTest/icon_c.jpg?m=', - $css - ); - } -} diff --git a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest/ModuleIconB.php b/tests/php/Controllers/LeftAndMainPageIconsExtensionTest/ModuleIconB.php deleted file mode 100644 index 147a044ea0..0000000000 --- a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest/ModuleIconB.php +++ /dev/null @@ -1,11 +0,0 @@ -generateRecordIconsCss(); + $this->assertStringNotContainsString('some invalid string', $css); + $this->assertStringContainsString( + 'tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_b.jpg?m=', + $css + ); + $this->assertStringContainsString( + 'tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_c.jpg?m=', + $css + ); + $this->assertStringNotContainsString( + 'tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_d.jpg?m=', + $css + ); + } +} diff --git a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest/ModuleIconA.php b/tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/ModuleIconA.php similarity index 50% rename from tests/php/Controllers/LeftAndMainPageIconsExtensionTest/ModuleIconA.php rename to tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/ModuleIconA.php index 3b503a34bf..5c1d907104 100644 --- a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest/ModuleIconA.php +++ b/tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/ModuleIconA.php @@ -1,11 +1,11 @@ $path, + 'cms_icon' => $path, ]; } } diff --git a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest/icon_b.jpg b/tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_b.jpg similarity index 100% rename from tests/php/Controllers/LeftAndMainPageIconsExtensionTest/icon_b.jpg rename to tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_b.jpg diff --git a/tests/php/Controllers/LeftAndMainPageIconsExtensionTest/icon_c.jpg b/tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_c.jpg similarity index 100% rename from tests/php/Controllers/LeftAndMainPageIconsExtensionTest/icon_c.jpg rename to tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_c.jpg diff --git a/tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_d.jpg b/tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_d.jpg new file mode 100644 index 0000000000..beb5a91b0a Binary files /dev/null and b/tests/php/Controllers/LeftAndMainRecordIconsExtensionTest/icon_d.jpg differ diff --git a/tests/php/Model/SiteTreePermissionsTest.php b/tests/php/Model/SiteTreePermissionsTest.php index ad7c17850c..02a8df272b 100644 --- a/tests/php/Model/SiteTreePermissionsTest.php +++ b/tests/php/Model/SiteTreePermissionsTest.php @@ -96,7 +96,7 @@ public function testPermissionCheckingWorksOnDeletedPages() // Test can_edit_multiple $this->assertEquals( [ $pageID => true ], - SiteTree::getPermissionChecker()->canEditMultiple([$pageID], $member) + $page->getPermissionChecker()->canEditMultiple([$pageID], $member) ); // Test canEdit @@ -119,7 +119,7 @@ public function testPermissionCheckingWorksOnUnpublishedPages() // Test can_edit_multiple $this->assertEquals( [ $pageID => true ], - SiteTree::getPermissionChecker()->canEditMultiple([$pageID], $member) + $page->getPermissionChecker()->canEditMultiple([$pageID], $member) ); // Test canEdit diff --git a/tests/php/Model/SiteTreeTest.php b/tests/php/Model/SiteTreeTest.php index 1de289eee4..2e5c894e5b 100644 --- a/tests/php/Model/SiteTreeTest.php +++ b/tests/php/Model/SiteTreeTest.php @@ -43,6 +43,7 @@ use const RESOURCES_DIR; use PHPUnit\Framework\Attributes\DataProvider; +use SilverStripe\Core\ClassInfo; class SiteTreeTest extends SapphireTest { @@ -64,7 +65,6 @@ class SiteTreeTest extends SapphireTest SiteTreeTest_ClassC::class, SiteTreeTest_ClassD::class, SiteTreeTest_NotRoot::class, - SiteTreeTest_StageStatusInherit::class, SiteTreeTest_DataObject::class, PageWithChild::class, BelongsToPage::class, @@ -445,7 +445,7 @@ public function testRestoreToStage() public function testNoCascadingDeleteWithoutID() { - Config::inst()->set('SiteTree', 'enforce_strict_hierarchy', true); + SiteTree::config()->set('enforce_strict_hierarchy', true); $count = SiteTree::get()->count(); $this->assertNotEmpty($count); $obj = new SiteTree(); @@ -806,7 +806,7 @@ public function testEditPermissionsOnDraftVsLive() // Clear permission cache /** @var InheritedPermissions $checker */ - $checker = SiteTree::getPermissionChecker(); + $checker = SiteTree::singleton()->getPermissionChecker(); $checker->clearCache(); // Confirm that Member.editor can now edit the page @@ -1182,19 +1182,22 @@ public function testVersionsAreCreated() public function testHidePagetypes() { SiteTree::config()->set('hide_pagetypes', ['Page']); - $classes = SiteTree::page_type_classes(); + $classes = ClassInfo::getValidSubClasses(SiteTree::class); + SiteTree::singleton()->updateAllowedSubClasses($classes); $this->assertNotContains('Page', $classes); } public function testPageTypeClasses() { - $classes = SiteTree::page_type_classes(); + $classes = ClassInfo::getValidSubClasses(SiteTree::class); + SiteTree::singleton()->updateAllowedSubClasses($classes); $this->assertNotContains(SiteTree::class, $classes, 'Page types do not include base class'); $this->assertContains('Page', $classes, 'Page types do contain subclasses'); // Testing what happens in an incorrect config value is set - hide_ancestor should be a string Config::modify()->set(SiteTreeTest_ClassA::class, 'hide_ancestor', true); - $newClasses = SiteTree::page_type_classes(); + $newClasses = ClassInfo::getValidSubClasses(SiteTree::class); + SiteTree::singleton()->updateAllowedSubClasses($newClasses); $this->assertEquals( $classes, $newClasses, @@ -1203,7 +1206,8 @@ public function testPageTypeClasses() // Testing what happens if a valid config value is set Config::modify()->set(SiteTreeTest_ClassA::class, 'hide_ancestor', 'Page'); - $classes = SiteTree::page_type_classes(); + $classes = ClassInfo::getValidSubClasses(SiteTree::class); + SiteTree::singleton()->updateAllowedSubClasses($classes); $this->assertNotContains('Page', $classes); } @@ -1289,14 +1293,6 @@ public function testCanBeRoot() } } - public function testModifyStatusFlagByInheritance() - { - $node = new SiteTreeTest_StageStatusInherit(); - $treeTitle = $node->getTreeTitle(); - $this->assertStringContainsString('InheritedTitle', $treeTitle); - $this->assertStringContainsString('inherited-class', $treeTitle); - } - public function testMenuTitleIsUnsetWhenEqualsTitle() { $page = new SiteTree(); @@ -1622,7 +1618,7 @@ public function testGetControllerName() */ public function testGetControllerNameFromConfig() { - Config::inst()->set(SiteTree::class, 'controller_name', 'This\\Is\\A\\New\\Controller'); + SiteTree::config()->set('controller_name', 'This\\Is\\A\\New\\Controller'); $page = new SiteTree(); $this->assertSame('This\\Is\\A\\New\\Controller', $page->getControllerName()); } @@ -1632,7 +1628,7 @@ public function testGetControllerNameFromConfig() */ public function testGetControllerNameFromNamespaceMappingConfig() { - Config::inst()->merge(SiteTree::class, 'namespace_mapping', [ + SiteTree::config()->merge('namespace_mapping', [ 'SilverStripe\\CMS\\Tests\\Page' => 'SilverStripe\\CMS\\Tests\\Controllers', ]); @@ -1640,58 +1636,6 @@ public function testGetControllerNameFromNamespaceMappingConfig() $this->assertSame(SiteTreeTest_NamespaceMapTestNodeController::class, $namespacedSiteTree->getControllerName()); } - public function testTreeTitleCache() - { - $siteTree = new SiteTree(); - $user = $this->objFromFixture(Member::class, 'allsections'); - Security::setCurrentUser($user); - $pageClass = array_values(SiteTree::page_type_classes())[0]; - - $mockPageMissesCache = $this->getMockBuilder($pageClass) - ->onlyMethods(['canCreate']) - ->getMock(); - $mockPageMissesCache - ->expects($this->exactly(3)) - ->method('canCreate'); - - $mockPageHitsCache = $this->getMockBuilder($pageClass) - ->onlyMethods(['canCreate']) - ->getMock(); - $mockPageHitsCache - ->expects($this->never()) - ->method('canCreate'); - - // Initially, cache misses (1) - Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $title = $siteTree->getTreeTitle(); - $this->assertNotNull($title); - - // Now it hits - Injector::inst()->registerService($mockPageHitsCache, $pageClass); - $title = $siteTree->getTreeTitle(); - $this->assertNotNull($title); - - - // Mutating member record invalidates cache. Misses (2) - $user->FirstName = 'changed'; - $user->write(); - Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $title = $siteTree->getTreeTitle(); - $this->assertNotNull($title); - - // Now it hits again - Injector::inst()->registerService($mockPageHitsCache, $pageClass); - $title = $siteTree->getTreeTitle(); - $this->assertNotNull($title); - - // Different user. Misses. (3) - $user = $this->objFromFixture(Member::class, 'editor'); - Security::setCurrentUser($user); - Injector::inst()->registerService($mockPageMissesCache, $pageClass); - $title = $siteTree->getTreeTitle(); - $this->assertNotNull($title); - } - public function testDependentPagesOnUnsavedRecord() { $record = new SiteTree(); diff --git a/tests/php/Model/SiteTreeTest_ClassA.php b/tests/php/Model/SiteTreeTest_ClassA.php index 7cf2020f4d..bf878c9676 100644 --- a/tests/php/Model/SiteTreeTest_ClassA.php +++ b/tests/php/Model/SiteTreeTest_ClassA.php @@ -9,11 +9,6 @@ class SiteTreeTest_ClassA extends SiteTree implements TestOnly { private static $table_name = 'SiteTreeTest_ClassA'; - private static $need_permission = [ - 'ADMIN', - 'CMS_ACCESS_CMSMain', - ]; - private static $allowed_children = [ SiteTreeTest_ClassB::class, ]; diff --git a/tests/php/Model/SiteTreeTest_StageStatusInherit.php b/tests/php/Model/SiteTreeTest_StageStatusInherit.php deleted file mode 100644 index 690c748629..0000000000 --- a/tests/php/Model/SiteTreeTest_StageStatusInherit.php +++ /dev/null @@ -1,18 +0,0 @@ -