mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
API Make CMSMain more generic
Remove hardcoded references to pages and SiteTree Remove assumption that records are versioned Remove or validate assumptions about methods on the model class Improve general architecture of CMSMain
This commit is contained in:
parent
bd48b04731
commit
91cfcb8229
@ -2,7 +2,6 @@
|
||||
|
||||
use SilverStripe\Admin\CMSMenu;
|
||||
use SilverStripe\CMS\Controllers\CMSMain;
|
||||
use SilverStripe\CMS\Controllers\CMSPageAddController;
|
||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||
use SilverStripe\CMS\Controllers\CMSPageSettingsController;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
@ -35,4 +34,3 @@ ShortcodeParser::get('default')->register(
|
||||
CMSMenu::remove_menu_class(CMSMain::class);
|
||||
CMSMenu::remove_menu_class(CMSPageEditController::class);
|
||||
CMSMenu::remove_menu_class(CMSPageSettingsController::class);
|
||||
CMSMenu::remove_menu_class(CMSPageAddController::class);
|
||||
|
@ -4,10 +4,10 @@ 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:
|
||||
@ -15,4 +15,4 @@ SilverStripe\Core\Injector\Injector:
|
||||
Psr\SimpleCache\CacheInterface.SiteTree_PageIcons:
|
||||
factory: SilverStripe\Core\Cache\CacheFactory
|
||||
constructor:
|
||||
namespace: "SiteTree_PageIcons"
|
||||
namespace: "SiteTree_PageIcons"
|
||||
|
2
client/dist/js/bundle.js
vendored
2
client/dist/js/bundle.js
vendored
@ -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("<button />",{class:"update btn btn-outline-secondary form__field-update-url",text:o.default._t("CMS.UpdateURL"),type:"button",click:function(e){e.preventDefault(),n.updateURLSegment(n.val())}})).insertAfter(n),t.hide()}}),e(".cms-edit-form .parentTypeSelector").entwine({onmatch:function(){var e=this;this.find(":input[name=ParentType]").on("click",(function(t){e._toggleSelection(t)})),this.find(".TreeDropdownField").on("change",(function(t){e._changeParentId(t)})),this._changeParentId(),this._toggleSelection(),this._super()},onunmatch:function(){this._super()},_toggleSelection:function(t){var n=this.find(":input[name=ParentType]:checked").val(),a=this.find("#Form_EditForm_ParentID_Holder");"root"==n?this.find(":input[name=ParentID]").val(0):this.find(":input[name=ParentID]").val(this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue")),"root"!=n?a.slideDown(400,(function(){e(this).css("overflow","visible")})):a.slideUp()},_changeParentId:function(e){var t=this.find(":input[name=ParentID]").val();this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue",t)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_doRollback, .cms-edit-form .btn-toolbar #Form_EditForm_action_rollback").entwine({onclick:function(e){if(this.is(":disabled"))return e.preventDefault(),!1;const t=this.parents("form:first").find(":input[name=Version]").val(),n=t?o.default.sprintf(o.default._t("CMS.RollbackToVersion","Do you really want to roll back to version #%s of this page?"),t):o.default._t("CMS.ConfirmRestoreFromLive","Are you sure you want to revert draft to when the page was last published?");return confirm(n)?(this.parents("form:first").addClass("loading"),this._super(e)):(e.preventDefault(),!1)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_archive:not(.homepage-warning)").entwine({onclick:function(e){var t;return t=this.parents("form:first").find("input[name=ArchiveWarningMessage]").val().replace(/\\n/g,"\n"),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_restore").entwine({onclick:function(e){var t,n=this.parents("form:first").find(":input[name=Version]").val(),a=this.data("toRoot");return t=o.default.sprintf(o.default._t(a?"CMS.RestoreToRoot":"CMS.Restore"),n),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish:not(.homepage-warning)").entwine({onclick:function(e){var t,n=this.parents("form:first").find(":input[name=Version]").val();return t=o.default.sprintf(o.default._t("CMS.Unpublish"),n),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form.changed").entwine({onmatch:function(t){this.find("button[data-text-alternate]").each((function(){const t=e(this),n=t.find(".btn__title"),a=t.data("textAlternate");a&&(t.data("textStandard",n.text()),n.text(a));const o=t.data("btnAlternate");o&&(t.data("btnStandard",t.attr("class")),t.attr("class",o),t.removeClass("btn-outline-secondary").addClass("btn-primary"));const i=t.data("btnAlternateAdd");i&&t.addClass(i);const r=t.data("btnAlternateRemove");r&&t.removeClass(r)})),this._super(t)},onunmatch:function(t){this.find("button[data-text-alternate]").each((function(){const t=e(this),n=t.find(".btn__title"),a=t.data("textStandard");a&&n.text(a);const o=t.data("btnStandard");o&&(t.attr("class",o),t.addClass("btn-outline-secondary").removeClass("btn-primary"));const i=t.data("btnAlternateAdd");i&&t.removeClass(i);const r=t.data("btnAlternateRemove");r&&t.addClass(r)})),this._super(t)}}),e(".cms-edit-form .btn-toolbar button[name=action_publish]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e(".cms-edit-form .btn-toolbar button[name=action_save]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').entwine({onmatch:function(){this.redraw(),this._super()},onunmatch:function(){this._super()},redraw:function(){var t=e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder");"Form_EditForm_ParentType_root"==e(this).attr("id")?t.slideUp():t.slideDown()},onclick:function(){this.redraw()}}),"Form_EditForm_ParentType_root"==e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').attr("id")&&e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder").hide();var t=!1;e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish.homepage-warning,.cms-edit-form .btn-toolbar #Form_EditForm_action_archive.homepage-warning,#Form_EditForm_URLSegment_Holder.homepage-warning .btn.update").entwine({onclick:async function(e){if(t)return this._super(e);e.stopPropagation();var n=o.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it.");return await(0,i.default)({title:o.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),message:n,confirmText:o.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"})&&(t=!0,this.trigger("click"),t=!1),!1}})}))},55:function(e,t,n){var a=s(n(669)),o=s(n(815)),i=s(n(216)),r=n(125);function s(e){return e&&e.__esModule?e:{default:e}}a.default.entwine("ss.tree",(function(e){e(".cms-tree").entwine({fromDocument:{"oncontext_show.vakata":function(e){this.adjustContextClass()}},adjustContextClass:function(){var t=e("#vakata-contextmenu").find("ul ul");t.each((function(n){var a="1",o=e(t[n]).find("li").length;o>20?a="3":o>10&&(a="2"),e(t[n]).addClass("vakata-col-"+a).removeClass("right"),e(t[n]).find("li").on("mouseenter",(function(t){e(this).parent("ul").removeClass("right")}))}))},showListViewFor:function(t){localStorage.setItem("ss.pages-view-type","listview");const n=this.closest(".cms-content-view").data("url-listviewroot"),a=e.path.addSearchParams(n,{ParentID:t}),o=e("base").attr("href")||"";window.location.assign((0,r.joinUrlPaths)(o,a))},getTreeConfig:function(){var t=this,n=this._super();this.getHints();return n.plugins.push("contextmenu"),n.contextmenu={items:function(n){var a={edit:{label:n.hasClass("edit-disabled")?o.default._t("CMS.EditPage","Edit page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"):o.default._t("CMS.ViewPage","View page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(o.default.sprintf(t.data("urlEditpage"),n.data("id")))}}};n.hasClass("nochildren")||(a.showaslist={label:o.default._t("CMS.ShowAsList"),action:function(e){t.showListViewFor(e.data("id"))}});n.data("pagetype");var i=n.data("id"),r=n.find(">a .item").data("allowedchildren"),s={},l=!1;return e.each(r,(function(n,a){l=!0,s["allowedchildren-"+a.ClassName]={label:'<span class="jstree-pageicon '+a.IconClass+'"></span>'+a.Title,_class:"class-"+a.ClassName.replace(/[^a-zA-Z0-9\-_:.]+/g,"_"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlAddpage"),i,a.ClassName),t.data("extraParams")))}}})),l&&(a.addsubpage={label:o.default._t("CMS.AddSubPage","Add page under this page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),submenu:s}),n.hasClass("edit-disabled")||(a.duplicate={label:o.default._t("CMS.Duplicate"),submenu:[{label:o.default._t("CMS.ThisPageOnly"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlDuplicate"),n.data("id")),t.data("extraParams")))}},{label:o.default._t("CMS.ThisPageAndSubpages"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlDuplicatewithchildren"),n.data("id")),t.data("extraParams")))}}]}),a}},n},canMove:async function(e){if(!(e.rslt.o.find(".homepage").first().length>0))return!0;if(e.rslt.op.data("id")===e.rslt.np.data("id"))return!0;var t=o.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it.");return await(0,i.default)({title:o.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),message:t,confirmText:o.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"})}}),e(".cms-tree a.jstree-clicked").entwine({onmatch:function(){var e=this,t=e.parents(".cms-tree-view-sidebar");if(e.offset().top<0||e.offset().top>t.height()-e.height()){var n=e.parent();n.prev().length&&(n=n.prev()),n.get(0).scrollIntoView()}}}),e(".cms-tree-filtered .clear-filter").entwine({onclick:function(){window.location=location.protocol+"//"+location.host+location.pathname}}),e(".cms-tree .subtree-list-link").entwine({onclick:function(e){e.preventDefault(),this.closest(".cms-tree").showListViewFor(this.data("id"))}})}))},881:function(e,t,n){var a,o=(a=n(669))&&a.__esModule?a:{default:a},i=n(125);o.default.entwine("ss",(function(e){const t="treeview",n="listview";e(".cms-content-header-info").entwine({"from .cms-panel":{ontoggle:function(e){var t=this.closest(".cms-content").find(e.target);0!==t.length&&this.parent()[t.hasClass("collapsed")?"addClass":"removeClass"]("collapsed")}}}),e(".cms-panel-deferred.cms-content-view").entwine({onadd:function(){if(this.data("no-ajax"))return;var e=localStorage.getItem("ss.pages-view-type")||t;this.closest(".cms-content-tools").length>0&&(e=t);const a=this.data(`url-${e}`);let o=localStorage.getItem("ss.pages-view-filtered");"string"==typeof o&&"false"===o.toLowerCase()&&(o=!1),localStorage.setItem("ss.pages-view-filtered",!1),this.data("deferredNoCache",o||e===n),this.data("url",a+location.search),this._super()}}),e(".js-injector-boot .search-holder--cms").entwine({search(e){localStorage.setItem("ss.pages-view-filtered",!0),this._super(e)}}),e(".cms .page-view-link").entwine({onclick:function(t){t.preventDefault();const a=e(this).data("view"),o=this.closest(".cms-content-view"),r=o.data(`url-${a}`),s=0!==o.closest(".cms-content-tools").length;if(localStorage.setItem("ss.pages-view-type",a),s&&a===n){const t=e("base").attr("href")||"";window.location.assign((0,i.joinUrlPaths)(t,o.data("url-listviewroot")))}else o.data("url",r+location.search),o.redraw()}}),e(".cms .cms-clear-filter").entwine({onclick:function(t){t.preventDefault(),window.location=e(this).prop("href")}}),e(".cms-content-toolbar").entwine({onmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var n=e(this),a=n.data("toolid");n.hasClass("active");void 0!==a&&(n.data("active",!1).removeClass("active"),e("#"+a).hide(),t.bindActionButtonEvents(n))}))},onunmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var n=e(this);t.unbindActionButtonEvents(n)}))},bindActionButtonEvents:function(e){var t=this;e.on("click.cmsContentToolbar",(function(n){t.showHideTool(e)}))},unbindActionButtonEvents:function(e){e.off(".cmsContentToolbar")},showHideTool:function(t){var n=t.data("active"),a=t.data("toolid"),o=e("#"+a);e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var t=e(this),n=e("#"+t.data("toolid"));t.data("toolid")!==a&&(n.hide(),t.data("active",!1))})),t[n?"removeClass":"addClass"]("active"),o[n?"hide":"show"](),t.data("active",!n)}})}))},739:function(e,t,n){var a;((a=n(669))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e("#Form_EditForm_RedirectionType input").entwine({onmatch:function(){e(this).attr("checked")&&this.toggle(),this._super()},onunmatch:function(){this._super()},onclick:function(){this.toggle()},toggle:function(){"Internal"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").show(),e("#Form_EditForm_LinkToFile_Holder").hide()):"External"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").show(),e("#Form_EditForm_LinkToID_Holder").hide(),e("#Form_EditForm_LinkToFile_Holder").hide()):(e("#Form_EditForm_LinkToFile_Holder").show(),e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").hide())}})}))},978:function(e,t,n){var a;((a=n(669))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e(".field.urlsegment:not(.readonly)").entwine({MaxPreviewLength:55,Ellipsis:"...",onmatch:function(){this.find(":text").length&&this.toggleEdit(!1),this.redraw(),this._super()},redraw:function(){var e=this.find(":text"),t=decodeURI(e.data("prefix")+e.val()),n=t;t.length>this.getMaxPreviewLength()&&(n=this.getEllipsis()+t.substr(t.length-this.getMaxPreviewLength(),t.length)),this.find(".URL-link").attr("href",encodeURI(t+e.data("suffix"))).text(n)},toggleEdit:function(e){var t=this.find(":text");this.find(".preview-holder")[e?"hide":"show"](),this.find(".edit-holder")[e?"show":"hide"](),e&&(t.data("origval",t.val()),t.focus())},update:function(){var e=this,t=this.find(":text"),n=t.data("origval"),a=arguments[0],o=a&&""!==a?a:t.val();n!=o?(this.addClass("loading"),this.suggest(o,(function(n){t.val(decodeURIComponent(n.value)),e.toggleEdit(!1),e.removeClass("loading"),e.redraw()}))):(this.toggleEdit(!1),this.redraw())},cancel:function(){var e=this.find(":text");e.val(e.data("origval")),this.toggleEdit(!1)},suggest:function(t,n){var a=this,o=a.find(":text"),i=e.path.parseUrl(a.closest("form").attr("action")),r=i.hrefNoSearch+"/field/"+o.attr("name")+"/suggest/?value="+encodeURIComponent(t);i.search&&(r+="&"+i.search.replace(/^\?/,"")),e.ajax({url:r,success:function(e){n.apply(this,arguments)},error:function(e,t){e.statusText=e.responseText},complete:function(){a.removeClass("loading")}})}}),e(".field.urlsegment .text").entwine({onkeydown:function(e){13===e.keyCode&&(e.preventDefault(),this.closest(".field").update())}}),e(".field.urlsegment .edit").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").toggleEdit(!0)}}),e(".field.urlsegment .update").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").update()}}),e(".field.urlsegment .cancel").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").cancel()}})}))},803:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},979:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=function(e){return{type:o.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}},t.updateFailed=function(e){return{type:o.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}},t.updated=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:o.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}},t.updatedCurrentField=function(e,t,n){return{type:o.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}};var a,o=(a=n(803))&&a.__esModule?a:{default:a}},796:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:s,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=(n,o)=>{const i=t.payload.pageId;return(0,a.default)({pages:[...e.pages.filter((e=>e.id!==i)),{id:i,loadingState:n,anchors:o}].sort(((e,t)=>e.id-t.id))})};switch(t.type){case o.default.ANCHORSELECTOR_UPDATING:return n(i.default.UPDATING,[]);case o.default.ANCHORSELECTOR_UPDATED:{const{anchors:e,cacheResult:a}=t.payload,{SUCCESS:o,DIRTY:r}=i.default;return n(a?o:r,e)}case o.default.ANCHORSELECTOR_CURRENT_FIELD:{const{anchors:e}=t.payload;return n(i.default.FIELD_ONLY,e)}case o.default.ANCHORSELECTOR_UPDATE_FAILED:return n(i.default.FAILED,[]);default:return e}};var a=r(n(923)),o=r(n(803)),i=r(n(996));function r(e){return e&&e.__esModule?e:{default:e}}const s=(0,a.default)({pages:[]})},996:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={SUCCESS:"SUCCESS",DIRTY:"DIRTY",FIELD_ONLY:"FIELD_ONLY",UPDATING:"UPDATING",FAILED:"FAILED"}},560:function(e,t,n){function a(e){return a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o,i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=l(t);if(n&&n.has(e))return n.get(e);var o={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var r in e)if("default"!==r&&Object.prototype.hasOwnProperty.call(e,r)){var s=i?Object.getOwnPropertyDescriptor(e,r):null;s&&(s.get||s.set)?Object.defineProperty(o,r,s):o[r]=e[r]}o.default=e,n&&n.set(e,o);return o}(n(594)),r=(o=n(935))&&o.__esModule?o:{default:o},s=n(556);function l(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(l=function(e){return e?n:t})(e)}function d(){return d=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},d.apply(this,arguments)}var u=function(e){var t=e.onClose,n=e.message,a=e.title,o=e.confirmText,r=e.cancelText,l=e.confirmColor,u=e.cancelColor,c=e.className,f=e.buttonsComponent,h=e.size,p=e.bodyComponent,m=e.modalProps,g=i.default.createElement(i.Fragment,null,r&&i.default.createElement(s.Button,{color:u,onClick:function(){return t(!1)}},r)," ",i.default.createElement(s.Button,{color:l,onClick:function(){return t(!0)}},o));if(f){var v=f;g=i.default.createElement(v,{onClose:t})}var _=p;return i.default.createElement(s.Modal,d({size:h,isOpen:!0,toggle:function(){return t(!1)},className:"reactstrap-confirm ".concat(c)},m),a&&i.default.createElement(s.ModalHeader,{toggle:function(){return t(!1)}},a||null),i.default.createElement(s.ModalBody,null,p?i.default.createElement(_,null):n),i.default.createElement(s.ModalFooter,null,g))};u.defaultProps={message:"Are you sure?",title:"Warning!",confirmText:"Ok",cancelText:"Cancel",confirmColor:"primary",cancelColor:"",className:"",buttonsComponent:null,size:null,bodyComponent:null,modalProps:{}},u.propTypes={onClose:r.default.func.isRequired,message:r.default.node,title:r.default.node,confirmText:r.default.node,cancelText:r.default.node,confirmColor:r.default.string,cancelColor:r.default.string,className:r.default.string,size:r.default.string,buttonsComponent:r.default.func,bodyComponent:r.default.func,modalProps:r.default.object};var c=u;t.default=c},216:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=r(n(594)),o=n(518),i=r(n(560));function r(e){return e&&e.__esModule?e:{default:e}}function s(){return s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},s.apply(this,arguments)}var l=function(e){return new Promise((function(t){var n=document.createElement("div");(0,o.render)(a.default.createElement(i.default,s({},e,{onClose:function(e){(0,o.unmountComponentAtNode)(n),n=null,t(e)}})),n)}))};t.default=l},923:function(e){e.exports=DeepFreezeStrict},657:function(e){e.exports=EmotionCssCacheProvider},623:function(e){e.exports=FieldHolder},207:function(e){e.exports=Injector},950:function(e){e.exports=IsomorphicFetch},935:function(e){e.exports=PropTypes},594:function(e){e.exports=React},518:function(e){e.exports=ReactDom},40:function(e){e.exports=ReactRedux},315:function(e){e.exports=ReactSelectCreatable},556:function(e){e.exports=Reactstrap},367:function(e){e.exports=Redux},381:function(e){e.exports=ReduxForm},898:function(e){e.exports=SilverStripeComponent},304:function(e){e.exports=classnames},432:function(e){e.exports=getFormState},815:function(e){e.exports=i18n},669:function(e){e.exports=jQuery},125:function(e){e.exports=ssUrlLib}},t={};function n(a){var o=t[a];if(void 0!==o)return o.exports;var i=t[a]={exports:{}};return e[a](i,i.exports,n),i.exports}n(586),n(677),n(881),n(55),n(739),n(978),n(38)}();
|
||||
!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)),c=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)),u=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===u.default.UPDATING||e.loadingState===u.default.SUCCESS||!e.pageId)return Promise.resolve();let t=[];e.loadingState===u.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(u.default).map((e=>u.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!==u.default.SUCCESS&&r.loadingState!==u.default.DIRTY&&r.loadingState!==u.default.FIELD_ONLY||(i=r.anchors);let s=null;return s=r?r.loadingState:o?u.default.DIRTY:u.default.SUCCESS,{pageId:o,anchors:i,loadingState:s}}),(function(e){return{actions:{anchorSelector:(0,s.bindActionCreators)(c,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,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 a=this.find("#Form_AddForm_RecordType div.radio.selected")[0],o=!1,i=null;if(this.find("#Form_AddForm_RecordType 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_RecordType div.radio input[value="+n+"]").parents("li:first");else r=this.find("#Form_AddForm_RecordType div.radio:not(.disabled):first");r.setSelected(!0),r.siblings().setSelected(!1),this.find("#Form_AddForm_RecordType 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_RecordType 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("<button />",{class:"update btn btn-outline-secondary form__field-update-url",text:o.default._t("CMS.UpdateURL"),type:"button",click:function(e){e.preventDefault(),n.updateURLSegment(n.val())}})).insertAfter(n),t.hide()}}),e(".cms-edit-form .parentTypeSelector").entwine({onmatch:function(){var e=this;this.find(":input[name=ParentType]").on("click",(function(t){e._toggleSelection(t)})),this.find(".TreeDropdownField").on("change",(function(t){e._changeParentId(t)})),this._changeParentId(),this._toggleSelection(),this._super()},onunmatch:function(){this._super()},_toggleSelection:function(t){var n=this.find(":input[name=ParentType]:checked").val(),a=this.find("#Form_EditForm_ParentID_Holder");"root"==n?this.find(":input[name=ParentID]").val(0):this.find(":input[name=ParentID]").val(this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue")),"root"!=n?a.slideDown(400,(function(){e(this).css("overflow","visible")})):a.slideUp()},_changeParentId:function(e){var t=this.find(":input[name=ParentID]").val();this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue",t)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_doRollback, .cms-edit-form .btn-toolbar #Form_EditForm_action_rollback").entwine({onclick:function(e){if(this.is(":disabled"))return e.preventDefault(),!1;const t=this.parents("form:first").find(":input[name=Version]").val(),n=t?o.default.sprintf(o.default._t("CMS.RollbackToVersion","Do you really want to roll back to version #%s of this page?"),t):o.default._t("CMS.ConfirmRestoreFromLive","Are you sure you want to revert draft to when the page was last published?");return confirm(n)?(this.parents("form:first").addClass("loading"),this._super(e)):(e.preventDefault(),!1)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_archive:not(.homepage-warning)").entwine({onclick:function(e){var t;return t=this.parents("form:first").find("input[name=ArchiveWarningMessage]").val().replace(/\\n/g,"\n"),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_restore").entwine({onclick:function(e){var t,n=this.parents("form:first").find(":input[name=Version]").val(),a=this.data("toRoot");return t=o.default.sprintf(o.default._t(a?"CMS.RestoreToRoot":"CMS.Restore"),n),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish:not(.homepage-warning)").entwine({onclick:function(e){var t,n=this.parents("form:first").find(":input[name=Version]").val();return t=o.default.sprintf(o.default._t("CMS.Unpublish"),n),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form.changed").entwine({onmatch:function(t){this.find("button[data-text-alternate]").each((function(){const t=e(this),n=t.find(".btn__title"),a=t.data("textAlternate");a&&(t.data("textStandard",n.text()),n.text(a));const o=t.data("btnAlternate");o&&(t.data("btnStandard",t.attr("class")),t.attr("class",o),t.removeClass("btn-outline-secondary").addClass("btn-primary"));const i=t.data("btnAlternateAdd");i&&t.addClass(i);const r=t.data("btnAlternateRemove");r&&t.removeClass(r)})),this._super(t)},onunmatch:function(t){this.find("button[data-text-alternate]").each((function(){const t=e(this),n=t.find(".btn__title"),a=t.data("textStandard");a&&n.text(a);const o=t.data("btnStandard");o&&(t.attr("class",o),t.addClass("btn-outline-secondary").removeClass("btn-primary"));const i=t.data("btnAlternateAdd");i&&t.removeClass(i);const r=t.data("btnAlternateRemove");r&&t.addClass(r)})),this._super(t)}}),e(".cms-edit-form .btn-toolbar button[name=action_publish]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e(".cms-edit-form .btn-toolbar button[name=action_save]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').entwine({onmatch:function(){this.redraw(),this._super()},onunmatch:function(){this._super()},redraw:function(){var t=e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder");"Form_EditForm_ParentType_root"==e(this).attr("id")?t.slideUp():t.slideDown()},onclick:function(){this.redraw()}}),"Form_EditForm_ParentType_root"==e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').attr("id")&&e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder").hide();var t=!1;e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish.homepage-warning,.cms-edit-form .btn-toolbar #Form_EditForm_action_archive.homepage-warning,#Form_EditForm_URLSegment_Holder.homepage-warning .btn.update").entwine({onclick:async function(e){if(t)return this._super(e);e.stopPropagation();var n=o.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it.");return await(0,i.default)({title:o.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),message:n,confirmText:o.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"})&&(t=!0,this.trigger("click"),t=!1),!1}})}))},55:function(e,t,n){var a=s(n(669)),o=s(n(815)),i=s(n(216)),r=n(125);function s(e){return e&&e.__esModule?e:{default:e}}a.default.entwine("ss.tree",(function(e){e(".cms-tree").entwine({fromDocument:{"oncontext_show.vakata":function(e){this.adjustContextClass()}},adjustContextClass:function(){var t=e("#vakata-contextmenu").find("ul ul");t.each((function(n){var a="1",o=e(t[n]).find("li").length;o>20?a="3":o>10&&(a="2"),e(t[n]).addClass("vakata-col-"+a).removeClass("right"),e(t[n]).find("li").on("mouseenter",(function(t){e(this).parent("ul").removeClass("right")}))}))},showListViewFor:function(t){localStorage.setItem("ss.pages-view-type","listview");const n=this.closest(".cms-content-view").data("url-listviewroot"),a=e.path.addSearchParams(n,{ParentID:t}),o=e("base").attr("href")||"";window.location.assign((0,r.joinUrlPaths)(o,a))},getTreeConfig:function(){var t=this,n=this._super();this.getHints();return n.plugins.push("contextmenu"),n.contextmenu={items:function(n){var a={edit:{label:n.hasClass("edit-disabled")?o.default._t("CMS.EditPage","Edit page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"):o.default._t("CMS.ViewPage","View page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(o.default.sprintf(t.data("urlEditpage"),n.data("id")))}}};n.hasClass("nochildren")||(a.showaslist={label:o.default._t("CMS.ShowAsList"),action:function(e){t.showListViewFor(e.data("id"))}});n.data("pagetype");var i=n.data("id"),r=n.find(">a .item").data("allowedchildren"),s={},l=!1;return e.each(r,(function(n,a){l=!0,s["allowedchildren-"+a.ClassName]={label:'<span class="jstree-pageicon '+a.IconClass+'"></span>'+a.Title,_class:"class-"+a.ClassName.replace(/[^a-zA-Z0-9\-_:.]+/g,"_"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlAddpage"),i,a.ClassName),t.data("extraParams")))}}})),l&&(a.addsubpage={label:o.default._t("CMS.AddSubPage","Add page under this page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),submenu:s}),n.hasClass("edit-disabled")||(a.duplicate={label:o.default._t("CMS.Duplicate"),submenu:[{label:o.default._t("CMS.ThisPageOnly"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlDuplicate"),n.data("id")),t.data("extraParams")))}},{label:o.default._t("CMS.ThisPageAndSubpages"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlDuplicatewithchildren"),n.data("id")),t.data("extraParams")))}}]}),a}},n},canMove:async function(e){if(!(e.rslt.o.find(".homepage").first().length>0))return!0;if(e.rslt.op.data("id")===e.rslt.np.data("id"))return!0;var t=o.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it.");return await(0,i.default)({title:o.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),message:t,confirmText:o.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"})}}),e(".cms-tree a.jstree-clicked").entwine({onmatch:function(){var e=this,t=e.parents(".cms-tree-view-sidebar");if(e.offset().top<0||e.offset().top>t.height()-e.height()){var n=e.parent();n.prev().length&&(n=n.prev()),n.get(0).scrollIntoView()}}}),e(".cms-tree-filtered .clear-filter").entwine({onclick:function(){window.location=location.protocol+"//"+location.host+location.pathname}}),e(".cms-tree .subtree-list-link").entwine({onclick:function(e){e.preventDefault(),this.closest(".cms-tree").showListViewFor(this.data("id"))}})}))},881:function(e,t,n){var a,o=(a=n(669))&&a.__esModule?a:{default:a},i=n(125);o.default.entwine("ss",(function(e){const t="treeview",n="listview";e(".cms-content-header-info").entwine({"from .cms-panel":{ontoggle:function(e){var t=this.closest(".cms-content").find(e.target);0!==t.length&&this.parent()[t.hasClass("collapsed")?"addClass":"removeClass"]("collapsed")}}}),e(".cms-panel-deferred.cms-content-view").entwine({onadd:function(){if(this.data("no-ajax"))return;var e=localStorage.getItem("ss.pages-view-type")||t;this.closest(".cms-content-tools").length>0&&(e=t);const a=this.data(`url-${e}`);let o=localStorage.getItem("ss.pages-view-filtered");"string"==typeof o&&"false"===o.toLowerCase()&&(o=!1),localStorage.setItem("ss.pages-view-filtered",!1),this.data("deferredNoCache",o||e===n),this.data("url",a+location.search),this._super()}}),e(".js-injector-boot .search-holder--cms").entwine({search(e){localStorage.setItem("ss.pages-view-filtered",!0),this._super(e)}}),e(".cms .page-view-link").entwine({onclick:function(t){t.preventDefault();const a=e(this).data("view"),o=this.closest(".cms-content-view"),r=o.data(`url-${a}`),s=0!==o.closest(".cms-content-tools").length;if(localStorage.setItem("ss.pages-view-type",a),s&&a===n){const t=e("base").attr("href")||"";window.location.assign((0,i.joinUrlPaths)(t,o.data("url-listviewroot")))}else o.data("url",r+location.search),o.redraw()}}),e(".cms .cms-clear-filter").entwine({onclick:function(t){t.preventDefault(),window.location=e(this).prop("href")}}),e(".cms-content-toolbar").entwine({onmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var n=e(this),a=n.data("toolid");n.hasClass("active");void 0!==a&&(n.data("active",!1).removeClass("active"),e("#"+a).hide(),t.bindActionButtonEvents(n))}))},onunmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var n=e(this);t.unbindActionButtonEvents(n)}))},bindActionButtonEvents:function(e){var t=this;e.on("click.cmsContentToolbar",(function(n){t.showHideTool(e)}))},unbindActionButtonEvents:function(e){e.off(".cmsContentToolbar")},showHideTool:function(t){var n=t.data("active"),a=t.data("toolid"),o=e("#"+a);e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var t=e(this),n=e("#"+t.data("toolid"));t.data("toolid")!==a&&(n.hide(),t.data("active",!1))})),t[n?"removeClass":"addClass"]("active"),o[n?"hide":"show"](),t.data("active",!n)}})}))},739:function(e,t,n){var a;((a=n(669))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e("#Form_EditForm_RedirectionType input").entwine({onmatch:function(){e(this).attr("checked")&&this.toggle(),this._super()},onunmatch:function(){this._super()},onclick:function(){this.toggle()},toggle:function(){"Internal"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").show(),e("#Form_EditForm_LinkToFile_Holder").hide()):"External"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").show(),e("#Form_EditForm_LinkToID_Holder").hide(),e("#Form_EditForm_LinkToFile_Holder").hide()):(e("#Form_EditForm_LinkToFile_Holder").show(),e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").hide())}})}))},978:function(e,t,n){var a;((a=n(669))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e(".field.urlsegment:not(.readonly)").entwine({MaxPreviewLength:55,Ellipsis:"...",onmatch:function(){this.find(":text").length&&this.toggleEdit(!1),this.redraw(),this._super()},redraw:function(){var e=this.find(":text"),t=decodeURI(e.data("prefix")+e.val()),n=t;t.length>this.getMaxPreviewLength()&&(n=this.getEllipsis()+t.substr(t.length-this.getMaxPreviewLength(),t.length)),this.find(".URL-link").attr("href",encodeURI(t+e.data("suffix"))).text(n)},toggleEdit:function(e){var t=this.find(":text");this.find(".preview-holder")[e?"hide":"show"](),this.find(".edit-holder")[e?"show":"hide"](),e&&(t.data("origval",t.val()),t.focus())},update:function(){var e=this,t=this.find(":text"),n=t.data("origval"),a=arguments[0],o=a&&""!==a?a:t.val();n!=o?(this.addClass("loading"),this.suggest(o,(function(n){t.val(decodeURIComponent(n.value)),e.toggleEdit(!1),e.removeClass("loading"),e.redraw()}))):(this.toggleEdit(!1),this.redraw())},cancel:function(){var e=this.find(":text");e.val(e.data("origval")),this.toggleEdit(!1)},suggest:function(t,n){var a=this,o=a.find(":text"),i=e.path.parseUrl(a.closest("form").attr("action")),r=i.hrefNoSearch+"/field/"+o.attr("name")+"/suggest/?value="+encodeURIComponent(t);i.search&&(r+="&"+i.search.replace(/^\?/,"")),e.ajax({url:r,success:function(e){n.apply(this,arguments)},error:function(e,t){e.statusText=e.responseText},complete:function(){a.removeClass("loading")}})}}),e(".field.urlsegment .text").entwine({onkeydown:function(e){13===e.keyCode&&(e.preventDefault(),this.closest(".field").update())}}),e(".field.urlsegment .edit").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").toggleEdit(!0)}}),e(".field.urlsegment .update").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").update()}}),e(".field.urlsegment .cancel").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").cancel()}})}))},803:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},979:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=function(e){return{type:o.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}},t.updateFailed=function(e){return{type:o.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}},t.updated=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:o.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}},t.updatedCurrentField=function(e,t,n){return{type:o.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}};var a,o=(a=n(803))&&a.__esModule?a:{default:a}},796:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:s,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=(n,o)=>{const i=t.payload.pageId;return(0,a.default)({pages:[...e.pages.filter((e=>e.id!==i)),{id:i,loadingState:n,anchors:o}].sort(((e,t)=>e.id-t.id))})};switch(t.type){case o.default.ANCHORSELECTOR_UPDATING:return n(i.default.UPDATING,[]);case o.default.ANCHORSELECTOR_UPDATED:{const{anchors:e,cacheResult:a}=t.payload,{SUCCESS:o,DIRTY:r}=i.default;return n(a?o:r,e)}case o.default.ANCHORSELECTOR_CURRENT_FIELD:{const{anchors:e}=t.payload;return n(i.default.FIELD_ONLY,e)}case o.default.ANCHORSELECTOR_UPDATE_FAILED:return n(i.default.FAILED,[]);default:return e}};var a=r(n(923)),o=r(n(803)),i=r(n(996));function r(e){return e&&e.__esModule?e:{default:e}}const s=(0,a.default)({pages:[]})},996:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={SUCCESS:"SUCCESS",DIRTY:"DIRTY",FIELD_ONLY:"FIELD_ONLY",UPDATING:"UPDATING",FAILED:"FAILED"}},560:function(e,t,n){function a(e){return a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o,i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=l(t);if(n&&n.has(e))return n.get(e);var o={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var r in e)if("default"!==r&&Object.prototype.hasOwnProperty.call(e,r)){var s=i?Object.getOwnPropertyDescriptor(e,r):null;s&&(s.get||s.set)?Object.defineProperty(o,r,s):o[r]=e[r]}o.default=e,n&&n.set(e,o);return o}(n(594)),r=(o=n(935))&&o.__esModule?o:{default:o},s=n(556);function l(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(l=function(e){return e?n:t})(e)}function d(){return d=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},d.apply(this,arguments)}var c=function(e){var t=e.onClose,n=e.message,a=e.title,o=e.confirmText,r=e.cancelText,l=e.confirmColor,c=e.cancelColor,u=e.className,f=e.buttonsComponent,h=e.size,p=e.bodyComponent,m=e.modalProps,g=i.default.createElement(i.Fragment,null,r&&i.default.createElement(s.Button,{color:c,onClick:function(){return t(!1)}},r)," ",i.default.createElement(s.Button,{color:l,onClick:function(){return t(!0)}},o));if(f){var v=f;g=i.default.createElement(v,{onClose:t})}var _=p;return i.default.createElement(s.Modal,d({size:h,isOpen:!0,toggle:function(){return t(!1)},className:"reactstrap-confirm ".concat(u)},m),a&&i.default.createElement(s.ModalHeader,{toggle:function(){return t(!1)}},a||null),i.default.createElement(s.ModalBody,null,p?i.default.createElement(_,null):n),i.default.createElement(s.ModalFooter,null,g))};c.defaultProps={message:"Are you sure?",title:"Warning!",confirmText:"Ok",cancelText:"Cancel",confirmColor:"primary",cancelColor:"",className:"",buttonsComponent:null,size:null,bodyComponent:null,modalProps:{}},c.propTypes={onClose:r.default.func.isRequired,message:r.default.node,title:r.default.node,confirmText:r.default.node,cancelText:r.default.node,confirmColor:r.default.string,cancelColor:r.default.string,className:r.default.string,size:r.default.string,buttonsComponent:r.default.func,bodyComponent:r.default.func,modalProps:r.default.object};var u=c;t.default=u},216:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=r(n(594)),o=n(518),i=r(n(560));function r(e){return e&&e.__esModule?e:{default:e}}function s(){return s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},s.apply(this,arguments)}var l=function(e){return new Promise((function(t){var n=document.createElement("div");(0,o.render)(a.default.createElement(i.default,s({},e,{onClose:function(e){(0,o.unmountComponentAtNode)(n),n=null,t(e)}})),n)}))};t.default=l},923:function(e){e.exports=DeepFreezeStrict},657:function(e){e.exports=EmotionCssCacheProvider},623:function(e){e.exports=FieldHolder},207:function(e){e.exports=Injector},950:function(e){e.exports=IsomorphicFetch},935:function(e){e.exports=PropTypes},594:function(e){e.exports=React},518:function(e){e.exports=ReactDom},40:function(e){e.exports=ReactRedux},315:function(e){e.exports=ReactSelectCreatable},556:function(e){e.exports=Reactstrap},367:function(e){e.exports=Redux},381:function(e){e.exports=ReduxForm},898:function(e){e.exports=SilverStripeComponent},304:function(e){e.exports=classnames},432:function(e){e.exports=getFormState},815:function(e){e.exports=i18n},669:function(e){e.exports=jQuery},125:function(e){e.exports=ssUrlLib}},t={};function n(a){var o=t[a];if(void 0!==o)return o.exports;var i=t[a]={exports:{}};return e[a](i,i.exports,n),i.exports}n(586),n(677),n(881),n(55),n(739),n(978),n(38)}();
|
@ -123,16 +123,16 @@ $.entwine('ss', function($){
|
||||
* @param {string} defaultChildClass
|
||||
*/
|
||||
updateSelectionFilter: function(disallowedChildren, defaultChildClass) {
|
||||
var currentSelection = this.find('#Form_AddForm_PageType div.radio.selected')[0];
|
||||
var currentSelection = this.find('#Form_AddForm_RecordType div.radio.selected')[0];
|
||||
var keepSelection = false;
|
||||
|
||||
// Limit selection
|
||||
var allAllowed = null; // troolian
|
||||
this.find('#Form_AddForm_PageType div.radio').each(function (i, el) {
|
||||
this.find('#Form_AddForm_RecordType div.radio').each(function (i, el) {
|
||||
var className = $(this).find('input').val(),
|
||||
isAllowed = ($.inArray(className, disallowedChildren) === -1);
|
||||
|
||||
// Avoid changing the selected pagetype if still allowed
|
||||
// Avoid changing the selected record type if still allowed
|
||||
if (el === currentSelection && isAllowed) {
|
||||
keepSelection = true;
|
||||
}
|
||||
@ -153,16 +153,16 @@ $.entwine('ss', function($){
|
||||
var selectedEl = $(currentSelection).parents('li:first');
|
||||
} else if (defaultChildClass) {
|
||||
var selectedEl = this
|
||||
.find('#Form_AddForm_PageType div.radio input[value=' + defaultChildClass + ']')
|
||||
.find('#Form_AddForm_RecordType div.radio input[value=' + defaultChildClass + ']')
|
||||
.parents('li:first');
|
||||
} else {
|
||||
var selectedEl = this.find('#Form_AddForm_PageType div.radio:not(.disabled):first');
|
||||
var selectedEl = this.find('#Form_AddForm_RecordType div.radio:not(.disabled):first');
|
||||
}
|
||||
selectedEl.setSelected(true);
|
||||
selectedEl.siblings().setSelected(false);
|
||||
|
||||
// Disable the "Create" button if none of the pagetypes are available
|
||||
if(this.find('#Form_AddForm_PageType div.radio:not(.disabled)').length) {
|
||||
// Disable the "Create" button if none of the record types are available
|
||||
if(this.find('#Form_AddForm_RecordType div.radio:not(.disabled)').length) {
|
||||
this.find('button[name=action_doAdd]').removeAttr('disabled');
|
||||
} else {
|
||||
this.find('button[name=action_doAdd]').attr('disabled', 'disabled');
|
||||
@ -172,7 +172,7 @@ $.entwine('ss', function($){
|
||||
}
|
||||
});
|
||||
|
||||
$(".cms-add-form #Form_AddForm_PageType div.radio").entwine({
|
||||
$(".cms-add-form #Form_AddForm_RecordType div.radio").entwine({
|
||||
onclick: function(e) {
|
||||
this.setSelected(true);
|
||||
},
|
||||
|
@ -13,10 +13,12 @@ use SilverStripe\CMS\BatchActions\CMSBatchAction_Archive;
|
||||
use SilverStripe\CMS\BatchActions\CMSBatchAction_Publish;
|
||||
use SilverStripe\CMS\BatchActions\CMSBatchAction_Unpublish;
|
||||
use SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search;
|
||||
use SilverStripe\CMS\Model\CurrentPageIdentifier;
|
||||
use SilverStripe\CMS\Forms\CMSMainAddForm;
|
||||
use SilverStripe\CMS\Model\CurrentRecordIdentifier;
|
||||
use SilverStripe\CMS\Model\RedirectorPage;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\CMS\Model\VirtualPage;
|
||||
use SilverStripe\CMS\Search\SearchForm;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
@ -25,7 +27,6 @@ use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\Control\PjaxResponseNegotiator;
|
||||
use SilverStripe\Core\Cache\MemberCacheFlusher;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Environment;
|
||||
use SilverStripe\Core\Flushable;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
@ -47,10 +48,8 @@ use SilverStripe\Forms\LabelField;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\Tab;
|
||||
use SilverStripe\Forms\TabSet;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\Model\List\ArrayList;
|
||||
use SilverStripe\ORM\CMSPreviewable;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||
@ -60,7 +59,6 @@ use SilverStripe\ORM\Hierarchy\MarkedSet;
|
||||
use SilverStripe\Model\List\SS_List;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Security\InheritedPermissions;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\PermissionProvider;
|
||||
use SilverStripe\Security\Security;
|
||||
@ -71,6 +69,7 @@ use SilverStripe\Versioned\ChangeSetItem;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController;
|
||||
use SilverStripe\Model\ArrayData;
|
||||
use SilverStripe\Versioned\RecursivePublishable;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
/**
|
||||
@ -78,53 +77,47 @@ use SilverStripe\View\Requirements;
|
||||
*
|
||||
* This class creates a 2-frame layout - left-tree and right-form - to sit beneath the main
|
||||
* admin menu.
|
||||
*
|
||||
* @mixin LeftAndMainPageIconsExtension
|
||||
*/
|
||||
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider, Flushable, MemberCacheFlusher
|
||||
class CMSMain extends LeftAndMain implements CurrentRecordIdentifier, PermissionProvider, Flushable, MemberCacheFlusher
|
||||
{
|
||||
/**
|
||||
* Unique ID for page icons CSS block
|
||||
*/
|
||||
const PAGE_ICONS_ID = 'PageIcons';
|
||||
public const PAGE_ICONS_ID = 'PageIcons'; // @TODO AHHHH!!!
|
||||
|
||||
private static $url_segment = 'pages';
|
||||
private static string $url_segment = 'pages';
|
||||
|
||||
private static $url_rule = '/$Action/$ID/$OtherID';
|
||||
private static string $url_rule = '/$Action/$ID/$OtherID';
|
||||
|
||||
// Maintain a lower priority than other administration sections
|
||||
// so that Director does not think they are actions of CMSMain
|
||||
private static $url_priority = 39;
|
||||
private static int $url_priority = 39;
|
||||
|
||||
private static $menu_title = 'Edit Page';
|
||||
private static $menu_title = 'Pages';
|
||||
|
||||
private static $menu_icon_class = 'font-icon-sitemap';
|
||||
private static string $menu_icon_class = 'font-icon-sitemap';
|
||||
|
||||
private static $menu_priority = 10;
|
||||
private static int $menu_priority = 10;
|
||||
|
||||
private static $tree_class = SiteTree::class;
|
||||
private static string $tree_class = SiteTree::class;
|
||||
|
||||
private static $session_namespace = CMSMain::class;
|
||||
private static string $session_namespace = CMSMain::class;
|
||||
|
||||
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
||||
private static string|array $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
||||
|
||||
/**
|
||||
* Should the archive warning message be dynamic based on the specific content? This is slow on larger sites and can be disabled.
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $enable_dynamic_archive_warning_message = true;
|
||||
private static bool $enable_dynamic_archive_warning_message = true;
|
||||
|
||||
/**
|
||||
* Amount of results showing on a single page.
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $page_length = 15;
|
||||
private static int $page_length = 15;
|
||||
|
||||
private static $allowed_actions = [
|
||||
private static array $allowed_actions = [
|
||||
'add',
|
||||
'AddForm',
|
||||
'archive',
|
||||
'deleteitems',
|
||||
'DeleteItemsForm',
|
||||
@ -138,7 +131,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
'EditForm',
|
||||
'schema',
|
||||
'SearchForm',
|
||||
'SiteTreeAsUL',
|
||||
'TreeAsUL',
|
||||
'getshowdeletedsubtree',
|
||||
'savetreenode',
|
||||
'getsubtree',
|
||||
@ -150,26 +143,26 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
'childfilter',
|
||||
];
|
||||
|
||||
private static $url_handlers = [
|
||||
private static array $url_handlers = [
|
||||
'EditForm/$ID' => 'EditForm',
|
||||
];
|
||||
|
||||
private static $casting = [
|
||||
private static array $casting = [
|
||||
'TreeIsFiltered' => 'Boolean',
|
||||
'AddForm' => 'HTMLFragment',
|
||||
'LinkPages' => 'Text',
|
||||
'LinkRecords' => 'Text',
|
||||
'Link' => 'Text',
|
||||
'ListViewForm' => 'HTMLFragment',
|
||||
'ExtraTreeTools' => 'HTMLFragment',
|
||||
'PageList' => 'HTMLFragment',
|
||||
'PageListSidebar' => 'HTMLFragment',
|
||||
'SiteTreeHints' => 'HTMLFragment',
|
||||
'RecordList' => 'HTMLFragment',
|
||||
'RecordListSidebar' => 'HTMLFragment',
|
||||
'TreeHints' => 'HTMLFragment',
|
||||
'SecurityID' => 'Text',
|
||||
'SiteTreeAsUL' => 'HTMLFragment',
|
||||
'TreeAsUL' => 'HTMLFragment',
|
||||
];
|
||||
|
||||
private static $dependencies = [
|
||||
'HintsCache' => '%$' . CacheInterface::class . '.CMSMain_SiteTreeHints',
|
||||
private static array $dependencies = [
|
||||
'HintsCache' => '%$' . CacheInterface::class . '.CMSMain_TreeHints',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -197,7 +190,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// In case we're not showing a specific record, explicitly remove any session state,
|
||||
// to avoid it being highlighted in the tree, and causing an edit form to show.
|
||||
if (!$request->param('Action')) {
|
||||
$this->setCurrentPageID(null);
|
||||
$this->setCurrentRecordID(null);
|
||||
}
|
||||
|
||||
return parent::index($request);
|
||||
@ -216,51 +209,42 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pages listing area
|
||||
*
|
||||
* @return DBHTMLText
|
||||
* Get record listing area
|
||||
*/
|
||||
public function PageList()
|
||||
public function RecordList(): DBHTMLText
|
||||
{
|
||||
return $this->renderWith($this->getTemplatesWithSuffix('_PageList'));
|
||||
return $this->renderWith($this->getTemplatesWithSuffix('_RecordList'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Page list view for edit-form
|
||||
*
|
||||
* @return DBHTMLText
|
||||
* Record list view for edit-form
|
||||
*/
|
||||
public function PageListSidebar()
|
||||
public function RecordListSidebar(): DBHTMLText
|
||||
{
|
||||
return $this->renderWith($this->getTemplatesWithSuffix('_PageList_Sidebar'));
|
||||
return $this->renderWith($this->getTemplatesWithSuffix('_RecordList_Sidebar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is set to true, the "switchView" context in the
|
||||
* template is shown, with links to the staging and publish site.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function ShowSwitchView()
|
||||
public function ShowSwitchView(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloads the LeftAndMain::ShowView. Allows to pass a page as a parameter, so we are able
|
||||
* Overloads the LeftAndMain::ShowView. Allows to pass a record as a parameter, so we are able
|
||||
* to switch view also for archived versions.
|
||||
*
|
||||
* @param SiteTree $page
|
||||
* @return array
|
||||
*/
|
||||
public function SwitchView($page = null)
|
||||
public function SwitchView(?DataObject $record = null): array
|
||||
{
|
||||
if (!$page) {
|
||||
$page = $this->currentPage();
|
||||
if (!$record) {
|
||||
$record = $this->currentRecord();
|
||||
}
|
||||
|
||||
if ($page) {
|
||||
$nav = SilverStripeNavigator::get_for_record($page);
|
||||
if ($record) {
|
||||
$nav = SilverStripeNavigator::get_for_record($record);
|
||||
return $nav['items'];
|
||||
}
|
||||
}
|
||||
@ -289,14 +273,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return $link;
|
||||
}
|
||||
|
||||
public function LinkPages()
|
||||
public function LinkRecords()
|
||||
{
|
||||
return CMSPagesController::singleton()->Link();
|
||||
}
|
||||
|
||||
public function LinkPagesWithSearch()
|
||||
public function LinkRecordsWithSearch()
|
||||
{
|
||||
return $this->LinkWithSearch($this->LinkPages());
|
||||
return $this->LinkWithSearch($this->LinkRecords());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,7 +290,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
*/
|
||||
public function LinkTreeView()
|
||||
{
|
||||
// Tree view is just default link to main pages section (no /treeview suffix)
|
||||
// Tree view is just default link to main section (no /treeview suffix)
|
||||
return CMSMain::singleton()->Link();
|
||||
}
|
||||
|
||||
@ -317,12 +301,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
*/
|
||||
public function LinkListView()
|
||||
{
|
||||
// Note : Force redirect to top level page controller (no parentid)
|
||||
// Note : Force redirect to top level record controller (no parentid)
|
||||
return $this->LinkWithSearch(CMSMain::singleton()->Link('listview'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to list view for children of a parent page
|
||||
* Link to list view for children of a parent record
|
||||
*
|
||||
* @param int|string $parentID Literal parentID, or placeholder (e.g. '%d') for
|
||||
* client side substitution
|
||||
@ -366,28 +350,31 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the link for editing a page.
|
||||
* Get the link for editing a record.
|
||||
*
|
||||
* @see CMSEditLinkExtension::getCMSEditLinkForManagedDataObject()
|
||||
*/
|
||||
public function getCMSEditLinkForManagedDataObject(SiteTree $obj): string
|
||||
public function getCMSEditLinkForManagedDataObject(DataObject $obj): string
|
||||
{
|
||||
return Controller::join_links(CMSPageEditController::singleton()->Link('show'), $obj->ID);
|
||||
}
|
||||
|
||||
public function LinkPageEdit($id = null)
|
||||
public function LinkRecordEdit($id = null)
|
||||
{
|
||||
if (!$id) {
|
||||
$id = $this->currentPageID();
|
||||
$id = $this->currentRecordID();
|
||||
}
|
||||
return $this->LinkWithSearch(
|
||||
Controller::join_links(CMSPageEditController::singleton()->Link('show'), $id)
|
||||
);
|
||||
}
|
||||
|
||||
public function LinkPageSettings()
|
||||
public function LinkRecordSettings()
|
||||
{
|
||||
if ($id = $this->currentPageID()) {
|
||||
if (!DataObject::singleton($this->getModelClass())->hasMethod('getSettingsFields')) { // @TODO This is awful, I'd much rather it just be part of the main form.
|
||||
return null;
|
||||
}
|
||||
if ($id = $this->currentRecordID()) {
|
||||
return $this->LinkWithSearch(
|
||||
Controller::join_links(CMSPageSettingsController::singleton()->Link('show'), $id)
|
||||
);
|
||||
@ -396,10 +383,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
}
|
||||
|
||||
public function LinkPageHistory()
|
||||
public function LinkRecordHistory()
|
||||
{
|
||||
$controller = Injector::inst()->get(CMSPageHistoryViewerController::class);
|
||||
if (($id = $this->currentPageID()) && $controller) {
|
||||
if (($id = $this->currentRecordID()) && $controller) {
|
||||
if ($controller) {
|
||||
return $this->LinkWithSearch(
|
||||
Controller::join_links($controller->Link('show'), $id)
|
||||
@ -463,10 +450,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return $link;
|
||||
}
|
||||
|
||||
public function LinkPageAdd($extra = null, $placeholders = null)
|
||||
public function LinkRecordAdd($extra = null, $placeholders = null)
|
||||
{
|
||||
$link = CMSPageAddController::singleton()->Link();
|
||||
$this->extend('updateLinkPageAdd', $link);
|
||||
$link = $this->Link('add');
|
||||
$this->extend('updateLinkRecordAdd', $link);
|
||||
|
||||
if ($extra) {
|
||||
$link = Controller::join_links($link, $extra);
|
||||
@ -484,10 +471,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
*/
|
||||
public function LinkPreview()
|
||||
{
|
||||
$record = $this->getRecord($this->currentPageID());
|
||||
$record = $this->getRecord($this->currentRecordID());
|
||||
$baseLink = Director::absoluteBaseURL();
|
||||
if ($record && $record instanceof SiteTree) {
|
||||
// if we are an external redirector don't show a link
|
||||
if ($record && $record->hasMethod('Link')) {
|
||||
// if we are an external redirector don't show a link //@TODO generalise this!!!!!
|
||||
if ($record instanceof RedirectorPage && $record->RedirectionType == 'External') {
|
||||
$baseLink = false;
|
||||
} else {
|
||||
@ -497,12 +484,27 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return $baseLink;
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
if ($this->getRequest()->isAjax()) {
|
||||
return $this->AddForm()->forTemplate();
|
||||
}
|
||||
return $this->render([
|
||||
'Content' => DBHTMLText::create()->setValue($this->AddForm()->forTemplate()),
|
||||
]);
|
||||
}
|
||||
|
||||
public function AddForm(): Form
|
||||
{
|
||||
return CMSMainAddForm::create($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the entire site tree as a nested set of ULs
|
||||
*/
|
||||
public function SiteTreeAsUL()
|
||||
public function TreeAsUL()
|
||||
{
|
||||
$treeClass = $this->config()->get('tree_class');
|
||||
$treeClass = $this->getModelClass();
|
||||
$filter = $this->getSearchFilter();
|
||||
|
||||
DataObject::singleton($treeClass)->prepopulateTreeDataCache(null, [
|
||||
@ -510,9 +512,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
'numChildrenMethod' => $filter ? $filter->getNumChildrenMethod() : 'numChildren',
|
||||
]);
|
||||
|
||||
$html = $this->getSiteTreeFor($treeClass);
|
||||
$html = $this->getTreeFor($treeClass);
|
||||
|
||||
$this->extend('updateSiteTreeAsUL', $html);
|
||||
$this->extend('updateTreeAsUL', $html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
@ -528,9 +530,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
* @param string $numChildrenMethod
|
||||
* @param callable $filterFunction
|
||||
* @param int $nodeCountThreshold
|
||||
* @return string Nested unordered list with links to each page
|
||||
* @return string Nested unordered list with links to each record
|
||||
*/
|
||||
public function getSiteTreeFor(
|
||||
public function getTreeFor(
|
||||
$className,
|
||||
$rootID = null,
|
||||
$childrenMethod = null,
|
||||
@ -550,7 +552,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
if (!$filterFunction) {
|
||||
$filterFunction = function ($node) use ($filter) {
|
||||
return $filter->isPageIncluded($node);
|
||||
return $filter->isRecordIncluded($node);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -568,20 +570,23 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// Mark tree from this node
|
||||
$markingSet->markPartialTree();
|
||||
|
||||
// Ensure current page is exposed
|
||||
$currentPage = $this->currentPage();
|
||||
if ($currentPage) {
|
||||
$markingSet->markToExpose($currentPage);
|
||||
// Ensure current record is exposed
|
||||
$currentRecord = $this->currentRecord();
|
||||
if ($currentRecord) {
|
||||
$markingSet->markToExpose($currentRecord);
|
||||
}
|
||||
|
||||
// Pre-cache permissions
|
||||
$checker = SiteTree::getPermissionChecker();
|
||||
$modelClass = $this->getModelClass();
|
||||
$singleton = DataObject::singleton($modelClass);
|
||||
$checker = $singleton->hasMethod('getPermissionChecker') ? $modelClass::getPermissionChecker() : null; // @TODO eww why is it static?
|
||||
if ($checker instanceof InheritedPermissions) {
|
||||
$checker->prePopulatePermissionCache(
|
||||
InheritedPermissions::EDIT,
|
||||
$markingSet->markedNodeIDs()
|
||||
);
|
||||
}
|
||||
// @TODO if we don't have inherited permissions, make sure we still DO do permission checks where needed!!
|
||||
|
||||
// Render using full-subtree template
|
||||
return $markingSet->renderChildren(
|
||||
@ -590,7 +595,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get callback to determine template customisations for nodes
|
||||
*
|
||||
@ -599,14 +603,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
protected function getTreeNodeCustomisations()
|
||||
{
|
||||
$rootTitle = $this->getCMSTreeTitle();
|
||||
return function (SiteTree $node) use ($rootTitle) {
|
||||
return function (DataObject $node) use ($rootTitle) {
|
||||
return [
|
||||
'listViewLink' => $this->LinkListViewChildren($node->ID),
|
||||
'rootTitle' => $rootTitle,
|
||||
'extraClass' => $this->getTreeNodeClasses($node),
|
||||
'Title' => _t(
|
||||
CMSMain::class . '.PAGETYPE_TITLE',
|
||||
'(Page type: {type}) {title}',
|
||||
CMSMain::class . '.RECORD_TYPE_TITLE',
|
||||
'(Record type: {type}) {title}',
|
||||
[
|
||||
'type' => $node->i18n_singular_name(),
|
||||
'title' => $node->Title,
|
||||
@ -617,15 +621,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra CSS classes for a page's tree node
|
||||
*
|
||||
* @param SiteTree $node
|
||||
* @return string
|
||||
* Get extra CSS classes for a record's tree node
|
||||
*/
|
||||
public function getTreeNodeClasses(SiteTree $node)
|
||||
public function getTreeNodeClasses(DataObject $node): string
|
||||
{
|
||||
// Get classes from object
|
||||
$classes = $node->CMSTreeClasses();
|
||||
$classes = $node->CMSTreeClasses(); // @TODO that's obviously not a thing.
|
||||
|
||||
// Get status flag classes
|
||||
$flags = $node->getStatusFlags();
|
||||
@ -638,7 +639,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
// Get additional filter classes
|
||||
$filter = $this->getSearchFilter();
|
||||
if ($filter && ($filterClasses = $filter->getPageClasses($node))) {
|
||||
if ($filter && ($filterClasses = $filter->getRecordClasses($node))) { // @TODO rename getRecordClasses or similar (though this is probably part of https://github.com/silverstripe/silverstripe-cms/issues/2949)
|
||||
if (is_array($filterClasses)) {
|
||||
$filterClasses = implode(' ', $filterClasses);
|
||||
}
|
||||
@ -654,8 +655,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
*/
|
||||
public function getsubtree(HTTPRequest $request): HTTPResponse
|
||||
{
|
||||
$html = $this->getSiteTreeFor(
|
||||
$this->config()->get('tree_class'),
|
||||
$html = $this->getTreeFor(
|
||||
$this->getModelClass(),
|
||||
$request->getVar('ID'),
|
||||
null,
|
||||
null,
|
||||
@ -687,7 +688,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
$record = $this->getRecord($id);
|
||||
if (!$record) {
|
||||
continue; // In case a page is no longer available
|
||||
continue; // In case a record is no longer available
|
||||
}
|
||||
|
||||
// Create marking set with sole marked root
|
||||
@ -700,7 +701,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// Find the next & previous nodes, for proper positioning (Sort isn't good enough - it's not a raw offset)
|
||||
$prev = null;
|
||||
|
||||
$className = $this->config()->get('tree_class');
|
||||
$className = $this->getModelClass();
|
||||
$next = DataObject::get($className)
|
||||
->filter('ParentID', $record->ParentID)
|
||||
->filter('Sort:GreaterThan', $record->Sort)
|
||||
@ -750,17 +751,17 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
if (!SecurityToken::inst()->checkRequest($request)) {
|
||||
$this->httpError(400);
|
||||
}
|
||||
if (!$this->CanOrganiseSitetree()) {
|
||||
if (!$this->CanOrganiseTree()) {
|
||||
$this->httpError(
|
||||
403,
|
||||
_t(
|
||||
__CLASS__.'.CANT_REORGANISE',
|
||||
"You do not have permission to rearange the site tree. Your change was not saved."
|
||||
__CLASS__.'.CANT_REORGANISE2',
|
||||
"You do not have permission to rearange the tree. Your change was not saved.",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$className = $this->config()->get('tree_class');
|
||||
$className = $this->getModelClass();
|
||||
$id = $request->requestVar('ID');
|
||||
$parentID = $request->requestVar('ParentID');
|
||||
if (!is_numeric($id) || !is_numeric($parentID)) {
|
||||
@ -768,26 +769,25 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
// Check record exists in the DB
|
||||
/** @var SiteTree $node */
|
||||
$node = DataObject::get_by_id($className, $id);
|
||||
if (!$node) {
|
||||
$this->httpError(
|
||||
500,
|
||||
_t(
|
||||
__CLASS__.'.PLEASESAVE',
|
||||
"Please Save Page: This page could not be updated because it hasn't been saved yet."
|
||||
__CLASS__.'.PLEASESAVE2',
|
||||
"Please Save Record: This record could not be updated because it hasn't been saved yet."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check top level permissions
|
||||
$root = $node->getParentType();
|
||||
$root = $node->getParentType(); // @TODO generalise. POC has `$node->ParentID == 0 ? 'root' : 'subpage';`
|
||||
if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) {
|
||||
$this->httpError(
|
||||
403,
|
||||
_t(
|
||||
__CLASS__.'.CANT_REORGANISE',
|
||||
"You do not have permission to alter Top level pages. Your change was not saved."
|
||||
__CLASS__.'.CANT_REORGANISE_TOPLEVEL',
|
||||
'You do not have permission to alter Top level records. Your change was not saved.'
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -805,20 +805,20 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$node->write();
|
||||
|
||||
$statusUpdates['modified'][$node->ID] = [
|
||||
'TreeTitle' => $node->TreeTitle
|
||||
'TreeTitle' => $node->TreeTitle // @TODO generalise.
|
||||
];
|
||||
|
||||
// Update all dependent pages
|
||||
// Update all dependent pages // @TODO generalise!! We shouldn't reference explicitly page types here.
|
||||
$virtualPages = VirtualPage::get()->filter("CopyContentFromID", $node->ID);
|
||||
foreach ($virtualPages as $virtualPage) {
|
||||
$statusUpdates['modified'][$virtualPage->ID] = [
|
||||
'TreeTitle' => $virtualPage->TreeTitle
|
||||
'TreeTitle' => $virtualPage->TreeTitle // @TODO generalise.
|
||||
];
|
||||
}
|
||||
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.') ?? '')
|
||||
rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL2', 'Reorganised the tree successfully.') ?? '')
|
||||
);
|
||||
}
|
||||
|
||||
@ -830,7 +830,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$node->Sort = ++$counter;
|
||||
$node->write();
|
||||
$statusUpdates['modified'][$node->ID] = [
|
||||
'TreeTitle' => $node->TreeTitle
|
||||
'TreeTitle' => $node->TreeTitle // @TODO generalise.
|
||||
];
|
||||
} elseif (is_numeric($id)) {
|
||||
// Nodes that weren't "actually moved" shouldn't be registered as
|
||||
@ -846,7 +846,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.') ?? '')
|
||||
rawurlencode(_t(__CLASS__.'.REORGANISATIONSUCCESSFUL2', 'Reorganised the tree successfully.') ?? '')
|
||||
);
|
||||
}
|
||||
|
||||
@ -857,64 +857,43 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current member has the permission to reorganise SiteTree objects.
|
||||
* @return bool
|
||||
* Whether the current member has the permission to reorganise records.
|
||||
*/
|
||||
public function CanOrganiseSitetree()
|
||||
public function CanOrganiseTree(): bool
|
||||
{
|
||||
return Permission::check('SITETREE_REORGANISE');
|
||||
return (bool) Permission::check('SITETREE_REORGANISE'); // @TODO model needs a method or config to say "this is the permission for that!!"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* Whether the tree has been filtered in this request or not.
|
||||
*/
|
||||
public function TreeIsFiltered()
|
||||
public function TreeIsFiltered(): bool
|
||||
{
|
||||
$query = $this->getRequest()->getVar('q');
|
||||
return !empty($query);
|
||||
}
|
||||
|
||||
public function ExtraTreeTools()
|
||||
public function ExtraTreeTools(): string
|
||||
{
|
||||
$html = '';
|
||||
$this->extend('updateExtraTreeTools', $html);
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* This provides information required to generate the search form
|
||||
* and can be modified on extensions through updateSearchContext
|
||||
*
|
||||
* @return \SilverStripe\ORM\Search\SearchContext
|
||||
*/
|
||||
public function getSearchContext()
|
||||
{
|
||||
$context = SiteTree::singleton()->getDefaultSearchContext();
|
||||
|
||||
$this->extend('updateSearchContext', $context);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the search form schema for the current model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSearchFieldSchema()
|
||||
public function getSearchFieldSchema(): string
|
||||
{
|
||||
$schemaUrl = $this->Link('schema/SearchForm');
|
||||
|
||||
$context = $this->getSearchContext();
|
||||
$singleton = DataObject::singleton($this->getModelClass());
|
||||
$context = $singleton->getDefaultSearchContext();
|
||||
$params = $this->getRequest()->requestVar('q') ?: [];
|
||||
$context->setSearchParams($params);
|
||||
|
||||
$placeholder = _t('SilverStripe\\CMS\\Search\\SearchForm.FILTERLABELTEXT', 'Search') . ' "' .
|
||||
SiteTree::singleton()->i18n_plural_name() . '"';
|
||||
|
||||
$placeholder = _t(SearchForm::class . '.FILTERLABELTEXT2', 'Search "{model}"', ['model' => $singleton->i18n_plural_name()]);
|
||||
$searchParams = $context->getSearchParams();
|
||||
|
||||
$searchParams = array_combine(array_map(function ($key) {
|
||||
return 'Search__' . $key;
|
||||
}, array_keys($searchParams ?? [])), $searchParams ?? []);
|
||||
@ -930,50 +909,59 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Form for page searching for use in templates.
|
||||
* Returns a Form for record searching for use in templates.
|
||||
*
|
||||
* Can be modified from a decorator by a 'updateSearchForm' method
|
||||
*
|
||||
* @return Form
|
||||
*/
|
||||
public function getSearchForm()
|
||||
public function getSearchForm(): Form
|
||||
{
|
||||
$modelClass = $this->getModelClass();
|
||||
$singleton = DataObject::singleton($modelClass);
|
||||
// Create the fields
|
||||
$dateFrom = DateField::create(
|
||||
'Search__LastEditedFrom',
|
||||
_t('SilverStripe\\CMS\\Search\\SearchForm.FILTERDATEFROM', 'From')
|
||||
_t(SearchForm::class . '.FILTERDATEFROM', 'From')
|
||||
)->setLocale(Security::getCurrentUser()->Locale);
|
||||
$dateTo = DateField::create(
|
||||
'Search__LastEditedTo',
|
||||
_t('SilverStripe\\CMS\\Search\\SearchForm.FILTERDATETO', 'To')
|
||||
_t(SearchForm::class . '.FILTERDATETO', 'To')
|
||||
)->setLocale(Security::getCurrentUser()->Locale);
|
||||
$filters = CMSSiteTreeFilter::get_all_filters();
|
||||
// Remove 'All pages' as we set that to empty/default value
|
||||
// Remove 'All records' as we set that to empty/default value
|
||||
unset($filters[CMSSiteTreeFilter_Search::class]);
|
||||
$pageFilter = DropdownField::create(
|
||||
$recordFilter = DropdownField::create(
|
||||
'Search__FilterClass',
|
||||
_t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGES', 'Page status'),
|
||||
_t(SearchForm::class . '.RECORD_STATUS', '{model} status', ['model' => $singleton->i18n_singular_name()]),
|
||||
$filters
|
||||
);
|
||||
$pageFilter->setEmptyString(_t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGESALLOPT', 'All pages'));
|
||||
$pageClasses = DropdownField::create(
|
||||
$recordFilter->setEmptyString(_t(
|
||||
SearchForm::class . '.RECORDS_ALLOPT',
|
||||
'All {model}',
|
||||
['model' => mb_strtolower($singleton->i18n_plural_name())]
|
||||
));
|
||||
$classes = DropdownField::create(
|
||||
'Search__ClassName',
|
||||
_t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGETYPEOPT', 'Page type', 'Dropdown for limiting search to a page type'),
|
||||
$this->getPageTypes()
|
||||
_t(
|
||||
SearchForm::class . '.RECORD_TYPEOPT',
|
||||
'{model} type',
|
||||
'Dropdown for limiting search to a record type',
|
||||
['model' => $singleton->i18n_singular_name()]
|
||||
),
|
||||
$this->getRecordTypes()
|
||||
);
|
||||
$pageClasses->setEmptyString(_t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGETYPEANYOPT', 'Any'));
|
||||
$classes->setEmptyString(_t(SearchForm::class . '.RECORD_TYPEANYOPT', 'Any'));
|
||||
|
||||
// Group the Datefields
|
||||
$dateGroup = FieldGroup::create(
|
||||
_t('SilverStripe\\CMS\\Search\\SearchForm.PAGEFILTERDATEHEADING', 'Last edited'),
|
||||
_t(SearchForm::class . '.RECORD_FILTERDATEHEADING', 'Last edited'),
|
||||
[$dateFrom, $dateTo]
|
||||
)->setName('Search__LastEdited')
|
||||
->addExtraClass('fieldgroup--fill-width');
|
||||
|
||||
// Create the Field list
|
||||
$fields = new FieldList(
|
||||
$pageFilter,
|
||||
$pageClasses,
|
||||
$recordFilter,
|
||||
$classes,
|
||||
$dateGroup
|
||||
);
|
||||
|
||||
@ -1000,18 +988,16 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sorted array suitable for a dropdown with pagetypes and their translated name
|
||||
*
|
||||
* @return array
|
||||
* Returns a sorted array suitable for a dropdown with classes and their localised name
|
||||
*/
|
||||
protected function getPageTypes()
|
||||
protected function getRecordTypes(): array
|
||||
{
|
||||
$pageTypes = [];
|
||||
foreach (SiteTree::page_type_classes() as $pageTypeClass) {
|
||||
$pageTypes[$pageTypeClass] = SiteTree::singleton($pageTypeClass)->i18n_singular_name();
|
||||
$types = [];
|
||||
foreach (SiteTree::page_type_classes() as $class) { // @TODO generalise!! Not fully into the POC way.
|
||||
$types[$class] = DataObject::singleton($class)->i18n_singular_name();
|
||||
}
|
||||
asort($pageTypes);
|
||||
return $pageTypes;
|
||||
asort($types);
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function doSearch(array $data, Form $form): HTTPResponse
|
||||
@ -1028,7 +1014,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
{
|
||||
$breadcrumbs = $this->Breadcrumbs();
|
||||
if ($breadcrumbs->count() < 2) {
|
||||
return $this->LinkPages();
|
||||
return $this->LinkRecords();
|
||||
}
|
||||
// Get second from end breadcrumb
|
||||
return $breadcrumbs
|
||||
@ -1040,15 +1026,15 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
{
|
||||
$items = ArrayList::create();
|
||||
|
||||
if (($this->getAction() !== 'index') && ($record = $this->currentPage())) {
|
||||
// The page is being edited
|
||||
if (($this->getAction() !== 'index') && ($record = $this->currentRecord())) {
|
||||
// The record is being edited
|
||||
$this->buildEditFormBreadcrumb($items, $record, $unlinked);
|
||||
} else {
|
||||
// Ensure we always have the "Pages" crumb first
|
||||
// Ensure we always have the admin section crumb first
|
||||
$this->pushCrumb(
|
||||
$items,
|
||||
CMSPagesController::menu_title(),
|
||||
$unlinked ? false : $this->LinkPages()
|
||||
$unlinked ? false : $this->LinkRecords()
|
||||
);
|
||||
|
||||
if ($this->TreeIsFiltered()) {
|
||||
@ -1056,13 +1042,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$this->pushCrumb(
|
||||
$items,
|
||||
_t(CMSMain::class . '.SEARCHRESULTS', 'Search results'),
|
||||
($unlinked) ? false : $this->LinkPages()
|
||||
($unlinked) ? false : $this->LinkRecords()
|
||||
);
|
||||
} elseif ($parentID = $this->getRequest()->getVar('ParentID')) {
|
||||
// We're navigating the listview. ParentID is the page whose
|
||||
// We're navigating the listview. ParentID is the record whose
|
||||
// children are currently displayed.
|
||||
if ($page = SiteTree::get()->byID($parentID)) {
|
||||
$this->buildListViewBreadcrumb($items, $page);
|
||||
if ($record = DataObject::get($this->getModelClass())->byID($parentID)) {
|
||||
$this->buildListViewBreadcrumb($items, $record);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1084,30 +1070,32 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Breadcrumb for the Edit page form. Each crumb links back to its own edit form.
|
||||
* Build Breadcrumb for the Edit form. Each crumb links back to its own edit form.
|
||||
*/
|
||||
private function buildEditFormBreadcrumb(ArrayList $items, SiteTree $page, bool $unlinked): void
|
||||
private function buildEditFormBreadcrumb(ArrayList $items, DataObject $record, bool $unlinked): void
|
||||
{
|
||||
// Find all ancestors of the provided page
|
||||
$ancestors = $page->getAncestors(true);
|
||||
// Find all ancestors of the provided record
|
||||
/** @var DataObject&Hierarchy $record */
|
||||
$ancestors = $record->getAncestors(true);
|
||||
$ancestors = array_reverse($ancestors->toArray() ?? []);
|
||||
foreach ($ancestors as $ancestor) {
|
||||
// Link to the ancestor's edit form
|
||||
$this->pushCrumb(
|
||||
$items,
|
||||
$ancestor->getMenuTitle(),
|
||||
$ancestor->getMenuTitle(), // @TODO generalise!!
|
||||
$unlinked ? false : $ancestor->getCMSEditLink()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Breadcrumb for the List view. Each crumb links to the list view for that page.
|
||||
* Build Breadcrumb for the List view. Each crumb links to the list view for that record.
|
||||
*/
|
||||
private function buildListViewBreadcrumb(ArrayList $items, SiteTree $page): void
|
||||
private function buildListViewBreadcrumb(ArrayList $items, DataObject $record): void
|
||||
{
|
||||
// Find all ancestors of the provided page
|
||||
$ancestors = $page->getAncestors(true);
|
||||
// Find all ancestors of the provided record
|
||||
/** @var DataObject&Hierarchy $record */
|
||||
$ancestors = $record->getAncestors(true);
|
||||
$ancestors = array_reverse($ancestors->toArray() ?? []);
|
||||
|
||||
//turns the title and link of the breadcrumbs into template-friendly variables
|
||||
@ -1121,7 +1109,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$params['ParentID'] = $ancestor->ID;
|
||||
$this->pushCrumb(
|
||||
$items,
|
||||
$ancestor->getMenuTitle(),
|
||||
$ancestor->getMenuTitle(), // @TODO generalise!!
|
||||
Controller::join_links($this->Link(), '?' . http_build_query($params ?? []))
|
||||
);
|
||||
}
|
||||
@ -1130,13 +1118,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
/**
|
||||
* Create serialized JSON string with site tree hints data to be injected into
|
||||
* 'data-hints' attribute of root node of jsTree.
|
||||
*
|
||||
* @return string Serialized JSON
|
||||
*/
|
||||
public function SiteTreeHints()
|
||||
public function TreeHints(): string // @TODO rename
|
||||
{
|
||||
$classes = SiteTree::page_type_classes();
|
||||
$memberID = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0;
|
||||
$classes = SiteTree::page_type_classes(); // @TODO generalise!!
|
||||
$memberID = Security::getCurrentUser()?->ID ?? 0;
|
||||
$cache = $this->getHintsCache();
|
||||
$cacheKey = $this->generateHintsCacheKey($memberID);
|
||||
$json = $cache->get($cacheKey);
|
||||
@ -1154,12 +1140,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$def['Root']['disallowedChildren'] = [];
|
||||
|
||||
// Contains all possible classes to support UI controls listing them all,
|
||||
// such as the "add page here" context menu.
|
||||
// such as the "add record here" context menu.
|
||||
$def['All'] = [];
|
||||
|
||||
// Identify disallows and set globals
|
||||
foreach ($classes as $class) {
|
||||
$obj = singleton($class);
|
||||
$obj = DataObject::singleton($class);
|
||||
if ($obj instanceof HiddenClass) {
|
||||
continue;
|
||||
}
|
||||
@ -1170,8 +1156,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
];
|
||||
|
||||
// Check if can be created at the root
|
||||
$needsPerm = $obj->config()->get('need_permission');
|
||||
if (!$obj->config()->get('can_be_root')
|
||||
$needsPerm = $obj::config()->get('need_permission');
|
||||
if ($obj::config()->get('can_be_root') === false
|
||||
|| (!array_key_exists($class, $canCreate ?? []) || !$canCreate[$class])
|
||||
|| ($needsPerm && !$this->can($needsPerm))
|
||||
) {
|
||||
@ -1181,18 +1167,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// Hint data specific to the class
|
||||
$def[$class] = [];
|
||||
|
||||
$defaultChild = $obj->defaultChild();
|
||||
if ($defaultChild !== 'Page' && $defaultChild !== null) {
|
||||
$defaultChild = $obj->defaultChild(); // @TODO generalise!!
|
||||
if ($defaultChild !== 'Page' && $defaultChild !== null) { // @TODO Find out where that 'Page' string comes from and fix it
|
||||
$def[$class]['defaultChild'] = $defaultChild;
|
||||
}
|
||||
|
||||
$defaultParent = $obj->defaultParent();
|
||||
$defaultParent = $obj->defaultParent(); // @TODO generalise!!
|
||||
if ($defaultParent !== 1 && $defaultParent !== null) {
|
||||
$def[$class]['defaultParent'] = $defaultParent;
|
||||
}
|
||||
}
|
||||
|
||||
$this->extend('updateSiteTreeHints', $def);
|
||||
$this->extend('updateTreeHints', $def);
|
||||
|
||||
$json = json_encode($def);
|
||||
$cache->set($cacheKey, $json);
|
||||
@ -1202,24 +1188,22 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
/**
|
||||
* Populates an array of classes in the CMS
|
||||
* which allows the user to change the page type.
|
||||
*
|
||||
* @return SS_List
|
||||
* which allows the user to change the record's ClassName field.
|
||||
*/
|
||||
public function PageTypes()
|
||||
public function RecordTypes(): SS_List
|
||||
{
|
||||
$classes = SiteTree::page_type_classes();
|
||||
$classes = SiteTree::page_type_classes(); // @TODO generalise!!
|
||||
|
||||
$result = new ArrayList();
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$instance = SiteTree::singleton($class);
|
||||
$instance = DataObject::singleton($class);
|
||||
if ($instance instanceof HiddenClass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip this type if it is restricted
|
||||
$needPermissions = $instance->config()->get('need_permission');
|
||||
$needPermissions = $instance::config()->get('need_permission'); // @TODO consider renaming that since it's so vague
|
||||
if ($needPermissions && !$this->can($needPermissions)) {
|
||||
continue;
|
||||
}
|
||||
@ -1227,8 +1211,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$result->push(new ArrayData([
|
||||
'ClassName' => $class,
|
||||
'AddAction' => $instance->i18n_singular_name(),
|
||||
'Description' => $instance->i18n_classDescription(),
|
||||
'IconURL' => $instance->getPageIconURL(),
|
||||
'Description' => $instance->i18n_classDescription(), // @TODO generalise!!
|
||||
'IconURL' => $instance->getPageIconURL(), // @TODO generalise!!
|
||||
'Title' => $instance->i18n_singular_name(),
|
||||
]));
|
||||
}
|
||||
@ -1243,20 +1227,22 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
*
|
||||
* @param int $id Record ID
|
||||
* @param int $versionID optional Version id of the given record
|
||||
* @return SiteTree
|
||||
*/
|
||||
public function getRecord($id, $versionID = null)
|
||||
public function getRecord($id, ?int $versionID = null): ?DataObject
|
||||
{
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
$treeClass = $this->config()->get('tree_class');
|
||||
if ($id instanceof $treeClass) {
|
||||
$modelClass = $this->getModelClass();
|
||||
if ($id instanceof $modelClass) {
|
||||
return $id;
|
||||
}
|
||||
if (substr($id ?? '', 0, 3) == 'new') {
|
||||
return $this->getNewItem($id);
|
||||
}
|
||||
if ($id === 'singleton') {
|
||||
return DataObject::singleton($modelClass);
|
||||
}
|
||||
if (!is_numeric($id)) {
|
||||
return null;
|
||||
}
|
||||
@ -1267,25 +1253,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$versionID = (int) $this->getRequest()->getVar('Version');
|
||||
}
|
||||
|
||||
/** @var SiteTree $record */
|
||||
$isVersioned = $modelClass::has_extension(Versioned::class);
|
||||
if ($versionID) {
|
||||
$record = Versioned::get_version($treeClass, $id, $versionID);
|
||||
if (!$isVersioned) {
|
||||
throw new HTTPResponse_Exception("Cannot get a version of non-versioned $modelClass record", 400);
|
||||
}
|
||||
$record = Versioned::get_version($modelClass, $id, $versionID);
|
||||
} else {
|
||||
$record = DataObject::get_by_id($treeClass, $id);
|
||||
$record = DataObject::get_by_id($modelClass, $id);
|
||||
}
|
||||
|
||||
// Then, try getting a record from the live site
|
||||
if (!$record) {
|
||||
// $record = Versioned::get_one_by_stage($treeClass, "Live", "\"$treeClass\".\"ID\" = $id");
|
||||
if (!$record && $isVersioned) {
|
||||
// $record = Versioned::get_one_by_stage($modelClass, "Live", "\"$modelClass\".\"ID\" = $id");
|
||||
Versioned::set_stage(Versioned::LIVE);
|
||||
singleton($treeClass)->flushCache();
|
||||
DataObject::singleton($modelClass)->flushCache();
|
||||
|
||||
$record = DataObject::get_by_id($treeClass, $id);
|
||||
$record = DataObject::get_by_id($modelClass, $id);
|
||||
}
|
||||
|
||||
// Then, try getting a deleted record
|
||||
if (!$record) {
|
||||
$record = Versioned::get_latest_version($treeClass, $id);
|
||||
if (!$record && $isVersioned) {
|
||||
$record = Versioned::get_latest_version($modelClass, $id);
|
||||
}
|
||||
|
||||
// Set the reading mode back to what it was.
|
||||
@ -1298,11 +1287,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @return Form
|
||||
*/
|
||||
public function EditForm($request = null)
|
||||
public function EditForm($request = null): Form
|
||||
{
|
||||
// set page ID from request
|
||||
// set record ID from request
|
||||
if ($request) {
|
||||
// Validate id is present
|
||||
$id = $request->param('ID');
|
||||
@ -1310,7 +1298,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$this->httpError(400);
|
||||
return null;
|
||||
}
|
||||
$this->setCurrentPageID($id);
|
||||
$this->setCurrentRecordID($id);
|
||||
}
|
||||
return $this->getEditForm();
|
||||
}
|
||||
@ -1318,13 +1306,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
/**
|
||||
* @param int $id
|
||||
* @param FieldList $fields
|
||||
* @return Form
|
||||
*/
|
||||
public function getEditForm($id = null, $fields = null)
|
||||
public function getEditForm($id = null, $fields = null): Form
|
||||
{
|
||||
// Get record
|
||||
if (!$id) {
|
||||
$id = $this->currentPageID();
|
||||
$id = $this->currentRecordID();
|
||||
}
|
||||
$record = $this->getRecord($id);
|
||||
|
||||
@ -1340,18 +1327,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
// Add extra fields
|
||||
$deletedFromStage = !$record->isOnDraft();
|
||||
$fields->push($idField = new HiddenField("ID", false, $id));
|
||||
$fields->push(new HiddenField("ID", false, $id));
|
||||
// Necessary for different subsites
|
||||
$fields->push($liveLinkField = new HiddenField("AbsoluteLink", false, $record->AbsoluteLink()));
|
||||
$fields->push($liveLinkField = new HiddenField("AbsoluteLink", false, $record->AbsoluteLink())); // @TODO generalise!!
|
||||
$fields->push($liveLinkField = new HiddenField("LiveLink"));
|
||||
$fields->push($stageLinkField = new HiddenField("StageLink"));
|
||||
$fields->push($archiveWarningMsgField = new HiddenField("ArchiveWarningMessage"));
|
||||
$fields->push(new HiddenField("TreeTitle", false, $record->getTreeTitle()));
|
||||
$fields->push(new HiddenField("TreeTitle", false, $record->getTreeTitle())); // @TODO generalise!!
|
||||
|
||||
$archiveWarningMsgField->setValue($this->getArchiveWarningMessage($record));
|
||||
|
||||
// Build preview / live links
|
||||
$liveLink = $record->getAbsoluteLiveLink();
|
||||
$liveLink = $record->getAbsoluteLiveLink(); // @TODO generalise!!
|
||||
if ($liveLink) {
|
||||
$liveLinkField->setValue($liveLink);
|
||||
}
|
||||
@ -1437,10 +1424,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function EmptyForm()
|
||||
public function EmptyForm(): Form
|
||||
{
|
||||
$fields = new FieldList(
|
||||
new LabelField('PageDoesntExistLabel', _t('SilverStripe\\CMS\\Controllers\\CMSMain.PAGENOTEXISTS', "This page doesn't exist"))
|
||||
new LabelField('RecordDoesntExistLabel', _t(__CLASS__ . '.RECORDNOTEXISTS', "This record doesn't exist"))
|
||||
);
|
||||
$form = parent::EmptyForm();
|
||||
$form->setFields($fields);
|
||||
@ -1449,39 +1436,32 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an archive warning message based on the page's children
|
||||
*
|
||||
* @param SiteTree $record
|
||||
* @return string
|
||||
* Build an archive warning message based on the record's children
|
||||
*/
|
||||
/**
|
||||
* Build an archive warning message based on the page's children
|
||||
*
|
||||
* @param SiteTree $record
|
||||
* @return string
|
||||
*/
|
||||
protected function getArchiveWarningMessage($record)
|
||||
protected function getArchiveWarningMessage(DataObject $record): string
|
||||
{
|
||||
|
||||
$defaultMessage = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarningWithChildren', 'Warning: This page and all of its child pages will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?');
|
||||
$defaultMessage = _t(
|
||||
LeftAndMain::class . '.ArchiveWarningWithChildren',
|
||||
'Warning: This record and all of its child records will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?'
|
||||
);
|
||||
|
||||
// Option to disable this feature as it is slow on large sites
|
||||
if (!$this->config()->enable_dynamic_archive_warning_message) {
|
||||
if (!static::config()->get('enable_dynamic_archive_warning_message')) {
|
||||
return $defaultMessage;
|
||||
}
|
||||
|
||||
// Get all page's descendants
|
||||
// Get all record's descendants
|
||||
$descendants = [];
|
||||
$this->collateDescendants([$record->ID], $descendants);
|
||||
if (!$descendants) {
|
||||
$descendants = [];
|
||||
}
|
||||
|
||||
// Get the IDs of all changeset including at least one of the pages.
|
||||
// Get the IDs of all changeset including at least one of the records.
|
||||
$descendants[] = $record->ID;
|
||||
$inChangeSetIDs = ChangeSetItem::get()->filter([
|
||||
'ObjectID' => $descendants,
|
||||
'ObjectClass' => SiteTree::class
|
||||
'ObjectClass' => $this->getModelClass(),
|
||||
])->column('ChangeSetID');
|
||||
|
||||
// Count number of affected change set
|
||||
@ -1496,20 +1476,31 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$numCampaigns = mb_strtolower($numCampaigns ?? '');
|
||||
|
||||
if (count($descendants ?? []) > 0 && $affectedChangeSetCount > 0) {
|
||||
$archiveWarningMsg = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarningWithChildrenAndCampaigns', 'Warning: This page and all of its child pages will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?', [ 'NumCampaigns' => $numCampaigns ]);
|
||||
$archiveWarningMsg = _t(
|
||||
LeftAndMain::class . '.ArchiveWarningWithChildrenAndCampaigns',
|
||||
'Warning: This record and all of its child records will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?',
|
||||
[ 'NumCampaigns' => $numCampaigns ]
|
||||
);
|
||||
} elseif (count($descendants ?? []) > 0) {
|
||||
$archiveWarningMsg = $defaultMessage;
|
||||
} elseif ($affectedChangeSetCount > 0) {
|
||||
$archiveWarningMsg = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarningWithCampaigns', 'Warning: This page will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?', [ 'NumCampaigns' => $numCampaigns ]);
|
||||
$archiveWarningMsg = _t(
|
||||
LeftAndMain::class . '.ArchiveWarningWithCampaigns',
|
||||
'Warning: This record will be unpublished and automatically removed from their associated {NumCampaigns} before being sent to the archive.\n\nAre you sure you want to proceed?',
|
||||
[ 'NumCampaigns' => $numCampaigns ]
|
||||
);
|
||||
} else {
|
||||
$archiveWarningMsg = _t('SilverStripe\\CMS\\Controllers\\CMSMain.ArchiveWarning', 'Warning: This page will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?');
|
||||
$archiveWarningMsg = _t(
|
||||
LeftAndMain::class . '.ArchiveWarning',
|
||||
'Warning: This record will be unpublished before being sent to the archive.\n\nAre you sure you want to proceed?'
|
||||
);
|
||||
}
|
||||
|
||||
return $archiveWarningMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find IDs of all descendant pages for the provided ID lists.
|
||||
* Find IDs of all descendant records for the provided ID lists.
|
||||
* @param int[] $recordIDs
|
||||
* @param array $collator
|
||||
* @return bool
|
||||
@ -1517,7 +1508,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
protected function collateDescendants($recordIDs, &$collator)
|
||||
{
|
||||
|
||||
$children = SiteTree::get()->filter(['ParentID' => $recordIDs])->column();
|
||||
$children = DataObject::get($this->getModelClass())->filter(['ParentID' => $recordIDs])->column();
|
||||
if ($children) {
|
||||
foreach ($children as $item) {
|
||||
$collator[] = $item;
|
||||
@ -1528,10 +1519,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method exclusively handles deferred ajax requests to render the
|
||||
* pages tree deferred handler (no pjax-fragment)
|
||||
* records tree deferred handler (no pjax-fragment)
|
||||
*
|
||||
* @return DBHTMLText HTML response with the rendered treeview
|
||||
*/
|
||||
@ -1569,21 +1559,21 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to request the list of page types allowed under a given page instance.
|
||||
* Provides a slower but more precise response over SiteTreeHints
|
||||
* Callback to request the list of record types allowed under a given record instance.
|
||||
* Provides a slower but more precise response over TreeHints
|
||||
*/
|
||||
public function childfilter(HTTPRequest $request): HTTPResponse
|
||||
{
|
||||
// Check valid parent specified
|
||||
$parentID = $request->requestVar('ParentID');
|
||||
$parent = SiteTree::get()->byID($parentID);
|
||||
$parent = DataObject::get($this->getModelClass())->byID($parentID);
|
||||
if (!$parent || !$parent->exists()) {
|
||||
$this->httpError(404);
|
||||
}
|
||||
|
||||
// Build hints specific to this class
|
||||
// Identify disallows and set globals
|
||||
$classes = SiteTree::page_type_classes();
|
||||
$classes = SiteTree::page_type_classes(); // @TODO generalise!!
|
||||
$disallowedChildren = [];
|
||||
foreach ($classes as $class) {
|
||||
$obj = singleton($class);
|
||||
@ -1623,8 +1613,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pages meet a certain criteria as {@see CMSSiteTreeFilter} or the subpages of a parent page
|
||||
* defaulting to no filter and show all pages in first level.
|
||||
* Returns the records meet a certain criteria as {@see CMSSiteTreeFilter} or the subrecords of a parent record
|
||||
* defaulting to no filter and show all records in first level.
|
||||
* Doubles as search results, if any search parameters are set through {@link SearchForm()}.
|
||||
*
|
||||
* @param array $params Search filter criteria
|
||||
@ -1637,7 +1627,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
if ($filter = $this->getQueryFilter($params)) {
|
||||
return $filter->getFilteredPages();
|
||||
} else {
|
||||
$list = DataList::create($this->config()->get('tree_class'));
|
||||
$list = DataObject::get($this->getModelClass());
|
||||
$parentID = is_numeric($parentID) ? $parentID : 0;
|
||||
return $list->filter("ParentID", $parentID);
|
||||
}
|
||||
@ -1658,7 +1648,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$gridFieldConfig = GridFieldConfig::create()->addComponents(
|
||||
Injector::inst()->create(GridFieldSortableHeader::class),
|
||||
Injector::inst()->create(GridFieldDataColumns::class),
|
||||
Injector::inst()->createWithArgs(GridFieldPaginator::class, [$this->config()->get('page_length')])
|
||||
Injector::inst()->createWithArgs(GridFieldPaginator::class, [static::config()->get('page_length')])
|
||||
);
|
||||
if ($parentID) {
|
||||
$linkSpec = $this->LinkListViewChildren('%d');
|
||||
@ -1667,17 +1657,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
->setLinkSpec($linkSpec)
|
||||
->setAttributes(['data-pjax-target' => 'ListViewForm,Breadcrumbs'])
|
||||
);
|
||||
$this->setCurrentPageID($parentID);
|
||||
$this->setCurrentRecordID($parentID);
|
||||
}
|
||||
$gridField = GridField::create('Page', 'Pages', $list, $gridFieldConfig);
|
||||
$gridField = GridField::create('Record', 'Records', $list, $gridFieldConfig); // @TODO make title string i18n
|
||||
$gridField->setAttribute('cms-loading-ignore-url-params', true);
|
||||
$columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
|
||||
|
||||
// Don't allow navigating into children nodes on filtered lists
|
||||
$modelClass = $this->getModelClass();
|
||||
$fields = [
|
||||
'getTreeTitle' => _t('SilverStripe\\CMS\\Model\\SiteTree.PAGETITLE', 'Page Title'),
|
||||
'i18n_singular_name' => _t('SilverStripe\\CMS\\Model\\SiteTree.PAGETYPE', 'Page Type'),
|
||||
'LastEdited' => _t('SilverStripe\\CMS\\Model\\SiteTree.LASTUPDATED', 'Last Updated'),
|
||||
'getTreeTitle' => _t($modelClass . '.TREETITLE', 'Title'),
|
||||
'i18n_singular_name' => _t($modelClass . '.TREETYPE', 'Record Type'),
|
||||
'LastEdited' => _t($modelClass . '.LASTUPDATED', 'Last Updated'),
|
||||
];
|
||||
$sortableHeader = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class);
|
||||
$sortableHeader->setFieldSorting(['getTreeTitle' => 'Title']);
|
||||
@ -1696,24 +1687,24 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
$columns->setFieldFormatting([
|
||||
'listChildrenLink' => function ($value, &$item) {
|
||||
/** @var SiteTree $item */
|
||||
$num = $item ? $item->numChildren() : null;
|
||||
/** @var DataObject&Hierarchy $item */
|
||||
$num = $item?->numChildren();
|
||||
if ($num) {
|
||||
return sprintf(
|
||||
'<a class="btn btn-secondary btn--no-text btn--icon-large font-icon-right-dir cms-panel-link list-children-link" data-pjax-target="ListViewForm,Breadcrumbs" href="%s"><span class="sr-only">%s child pages</span></a>',
|
||||
'<a class="btn btn-secondary btn--no-text btn--icon-large font-icon-right-dir cms-panel-link list-children-link" data-pjax-target="ListViewForm,Breadcrumbs" href="%s"><span class="sr-only">%s child pages</span></a>', // @TODO generalise and i18n-ify
|
||||
$this->LinkListViewChildren((int)$item->ID),
|
||||
$num
|
||||
);
|
||||
}
|
||||
},
|
||||
'getTreeTitle' => function ($value, &$item) {
|
||||
/** @var SiteTree $item */
|
||||
/** @var DataObject $item */
|
||||
$title = sprintf(
|
||||
'<a class="action-detail" href="%s">%s</a>',
|
||||
$item->getCMSEditLink(),
|
||||
$item->TreeTitle // returns HTML, does its own escaping
|
||||
$item->TreeTitle // returns HTML, does its own escaping // @TODO generalise!!
|
||||
);
|
||||
$breadcrumbs = $item->Breadcrumbs(20, true, false, true, '/');
|
||||
$breadcrumbs = $item->Breadcrumbs(20, true, false, true, '/'); // @TODO generalise!!
|
||||
// Remove item's tile
|
||||
$breadcrumbs = preg_replace('/[^\/]+$/', '', trim($breadcrumbs ?? ''));
|
||||
// Trim spaces around delimiters
|
||||
@ -1748,11 +1739,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return $listview;
|
||||
}
|
||||
|
||||
public function currentPageID()
|
||||
public function currentRecordID()
|
||||
{
|
||||
$id = parent::currentPageID();
|
||||
$id = parent::currentRecordID();
|
||||
|
||||
$this->extend('updateCurrentPageID', $id);
|
||||
$this->extend('updateCurrentRecordID', $id);
|
||||
|
||||
return $id;
|
||||
}
|
||||
@ -1761,18 +1752,17 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// Data saving handlers
|
||||
|
||||
/**
|
||||
* Save and Publish page handler
|
||||
* Save and Publish record handler
|
||||
*
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function save(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$className = $this->config()->get('tree_class');
|
||||
$className = $this->getModelClass();
|
||||
|
||||
// Existing or new record?
|
||||
$id = $data['ID'];
|
||||
if (substr($id ?? '', 0, 3) != 'new') {
|
||||
/** @var SiteTree $record */
|
||||
$record = DataObject::get_by_id($className, $id);
|
||||
// Check edit permissions
|
||||
if ($record && !$record->canEdit()) {
|
||||
@ -1790,14 +1780,15 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
// Check publishing permissions
|
||||
$doPublish = !empty($data['publish']);
|
||||
if ($record && $doPublish && !$record->canPublish()) {
|
||||
$isVersioned = $record->hasExtension(Versioned::class);
|
||||
if ($isVersioned && $doPublish && !$record->canPublish()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
$record->HasBrokenLink = 0;
|
||||
$record->HasBrokenFile = 0;
|
||||
$record->HasBrokenLink = 0; // @TODO generalise!!
|
||||
$record->HasBrokenFile = 0; // @TODO generalise!!
|
||||
|
||||
if (!$record->ObsoleteClassName) {
|
||||
if ($isVersioned && !$record->ObsoleteClassName) { // @TODO I think that's specific to SiteTree??
|
||||
$record->writeWithoutVersion();
|
||||
}
|
||||
|
||||
@ -1812,19 +1803,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$form->saveInto($record);
|
||||
$record->write();
|
||||
|
||||
// If the 'Publish' button was clicked, also publish the page
|
||||
// If the 'Publish' button was clicked, also publish the record
|
||||
if ($doPublish) {
|
||||
if (!$record->hasExtension(RecursivePublishable::class)) {
|
||||
throw new HTTPResponse_Exception(get_class($record) . ' record is not publishable.', 400);
|
||||
}
|
||||
$record->publishRecursive();
|
||||
$message = _t(
|
||||
__CLASS__ . '.PUBLISHED',
|
||||
"Published '{title}' successfully.",
|
||||
['title' => $record->Title]
|
||||
LeftAndMain::class . '.PUBLISHED_RECORD',
|
||||
'Published {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$message = _t(
|
||||
__CLASS__ . '.SAVED',
|
||||
"Saved '{title}' successfully.",
|
||||
['title' => $record->Title]
|
||||
LeftAndMain::class . '.SAVED_RECORD',
|
||||
'Saved {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@ -1835,12 +1835,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
/**
|
||||
* @param int|string $id
|
||||
* @param bool $setID
|
||||
* @return mixed|DataObject
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function getNewItem($id, $setID = true)
|
||||
public function getNewItem($id, $setID = true): DataObject
|
||||
{
|
||||
$parentClass = $this->config()->get('tree_class');
|
||||
$parentClass = $this->getModelClass();
|
||||
list(, $className, $parentID) = array_pad(explode('-', $id ?? ''), 3, null);
|
||||
|
||||
if (!is_a($className, $parentClass ?? '', true)) {
|
||||
@ -1851,23 +1850,20 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
throw new HTTPResponse_Exception($response);
|
||||
}
|
||||
|
||||
/** @var SiteTree $newItem */
|
||||
/** @var DataObject $newItem */
|
||||
$newItem = Injector::inst()->create($className);
|
||||
$newItem->Title = _t(
|
||||
__CLASS__ . '.NEWPAGE',
|
||||
"New {pagetype}",
|
||||
'followed by a page type title',
|
||||
['pagetype' => singleton($className)->i18n_singular_name()]
|
||||
LeftAndMain::class . '.NEW_RECORD',
|
||||
'New {recordtype}',
|
||||
['recordtype' => DataObject::singleton($className)->i18n_singular_name()]
|
||||
);
|
||||
$newItem->ClassName = $className;
|
||||
$newItem->ParentID = $parentID;
|
||||
|
||||
// DataObject::fieldExists only checks the current class, not the hierarchy
|
||||
// This allows the CMS to set the correct sort value
|
||||
if ($newItem->castingHelper('Sort')) {
|
||||
$table = DataObject::singleton(SiteTree::class)->baseTable();
|
||||
if ($newItem->hasDatabaseField('Sort')) { // @TODO Let the field name be configurable
|
||||
$table = DataObject::singleton($parentClass)->baseTable();
|
||||
$maxSort = DB::prepared_query(
|
||||
"SELECT MAX(\"Sort\") FROM \"$table\" WHERE \"ParentID\" = ?",
|
||||
"SELECT MAX(\"Sort\") FROM \"$table\" WHERE \"ParentID\" = ?", // @TODO this won't work if sort is on a subclass! Use ORM instead
|
||||
[$parentID]
|
||||
)->value();
|
||||
$newItem->Sort = (int)$maxSort + 1;
|
||||
@ -1878,55 +1874,43 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
# Some modules like subsites add extra fields that need to be set when the new item is created
|
||||
$this->extend('augmentNewSiteTreeItem', $newItem);
|
||||
$this->extend('updateNewItem', $newItem); // @TODO rename
|
||||
|
||||
return $newItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually perform the publication step
|
||||
*
|
||||
* @param Versioned|DataObject $record
|
||||
* @return mixed
|
||||
*/
|
||||
public function performPublish($record)
|
||||
{
|
||||
if ($record && !$record->canPublish()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
$record->publishRecursive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts a page by publishing it to live.
|
||||
* Use {@link restorepage()} if you want to restore a page
|
||||
* Reverts a record by publishing it to live.
|
||||
* Use {@link restoreRecord()} if you want to restore a record
|
||||
* which was deleted from draft without publishing.
|
||||
*
|
||||
* @uses SiteTree->doRevertToLive()
|
||||
*
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function revert(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$modelClass = $this->getModelClass();
|
||||
if (!isset($data['ID'])) {
|
||||
throw new HTTPResponse_Exception("Please pass an ID in the form content", 400);
|
||||
}
|
||||
|
||||
$id = (int) $data['ID'];
|
||||
$restoredPage = Versioned::get_latest_version(SiteTree::class, $id);
|
||||
if (!$restoredPage) {
|
||||
throw new HTTPResponse_Exception("SiteTree #$id not found", 400);
|
||||
if (!$modelClass::has_extension(Versioned::class)) {
|
||||
throw new HTTPResponse_Exception("$modelClass record cannot be reverted", 400);
|
||||
}
|
||||
|
||||
$table = DataObject::singleton(SiteTree::class)->baseTable();
|
||||
$liveTable = DataObject::singleton(SiteTree::class)->stageTable($table, Versioned::LIVE);
|
||||
$record = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [
|
||||
$id = (int) $data['ID'];
|
||||
$restoredRecord = Versioned::get_latest_version($modelClass, $id);
|
||||
if (!$restoredRecord) {
|
||||
throw new HTTPResponse_Exception("Record #$id not found", 400);
|
||||
}
|
||||
|
||||
$table = DataObject::singleton($modelClass)->baseTable();
|
||||
$liveTable = DataObject::singleton($modelClass)->stageTable($table, Versioned::LIVE);
|
||||
$record = Versioned::get_one_by_stage($modelClass, Versioned::LIVE, [
|
||||
"\"$liveTable\".\"ID\"" => $id
|
||||
]);
|
||||
|
||||
// a user can restore a page without publication rights, as it just adds a new draft state
|
||||
// (this action should just be available when page has been "deleted from draft")
|
||||
// a user can restore a record without publication rights, as it just adds a new draft state
|
||||
// (this action should just be available when the record has been "deleted from draft")
|
||||
if ($record && !$record->canEdit()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
@ -1939,27 +1923,27 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(
|
||||
__CLASS__ . '.RESTORED',
|
||||
"Restored '{title}' successfully",
|
||||
'Param {title} is a title',
|
||||
['title' => $record->Title]
|
||||
) ?? '')
|
||||
LeftAndMain::class . '.RESTORED_RECORD',
|
||||
'Restored {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
))
|
||||
);
|
||||
|
||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current page from draft stage.
|
||||
*
|
||||
* @see deletefromlive()
|
||||
* Delete the current record from draft stage.
|
||||
*
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function delete(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$id = $data['ID'];
|
||||
$record = SiteTree::get()->byID($id);
|
||||
$record = DataObject::get($this->getModelClass())->byID($id);
|
||||
if ($record && !$record->canDelete()) {
|
||||
return Security::permissionFailure();
|
||||
}
|
||||
@ -1970,13 +1954,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
// Delete record
|
||||
$record->delete();
|
||||
|
||||
if ($record->hasExtension(Versioned::class) && $record->hasStages()) {
|
||||
$message = _t(
|
||||
LeftAndMain::class . '.ARCHIVED_RECORD',
|
||||
'Archived {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$message = _t(
|
||||
LeftAndMain::class . '.DELETED_RECORD',
|
||||
'Deleted {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
);
|
||||
}
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(
|
||||
__CLASS__ . '.REMOVEDPAGEFROMDRAFT',
|
||||
"Removed '{title}' from the draft site",
|
||||
['title' => $record->Title]
|
||||
) ?? '')
|
||||
rawurlencode($message)
|
||||
);
|
||||
|
||||
// Even if the record has been deleted from stage and live, it can be viewed in "archive mode"
|
||||
@ -1984,14 +1983,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this page from both live and stage
|
||||
* Delete this record from both live and stage
|
||||
*
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function archive(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$id = $data['ID'];
|
||||
$record = SiteTree::get()->byID($id);
|
||||
$record = DataObject::get($this->getModelClass())->byID($id);
|
||||
if (!$record || !$record->exists()) {
|
||||
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
}
|
||||
@ -2005,10 +2004,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(
|
||||
__CLASS__ . '.ARCHIVEDPAGE',
|
||||
"Archived page '{title}'",
|
||||
['title' => $record->Title]
|
||||
) ?? '')
|
||||
LeftAndMain::class . '.ARCHIVED_RECORD',
|
||||
'Archived {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
))
|
||||
);
|
||||
|
||||
// Even if the record has been deleted from stage and live, it can be viewed in "archive mode"
|
||||
@ -2024,10 +2026,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
public function unpublish(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$className = $this->config()->get('tree_class');
|
||||
/** @var SiteTree $record */
|
||||
$className = $this->getModelClass();
|
||||
$record = DataObject::get_by_id($className, $data['ID']);
|
||||
|
||||
if (!$record->hasExtension(Versioned::class)) {
|
||||
throw new HTTPResponse_Exception(get_class($record) . ' record cannot be unpublished.', 400);
|
||||
}
|
||||
|
||||
if ($record && !$record->canUnpublish()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
@ -2040,10 +2045,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(
|
||||
__CLASS__ . '.REMOVEDPAGE',
|
||||
"Removed '{title}' from the published site",
|
||||
['title' => $record->Title]
|
||||
) ?? '')
|
||||
LeftAndMain::class . '.UNPUBLISHED_RECORD',
|
||||
'Unpublished {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
))
|
||||
);
|
||||
|
||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||
@ -2055,7 +2063,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
public function rollback()
|
||||
{
|
||||
return $this->doRollback([
|
||||
'ID' => $this->currentPageID(),
|
||||
'ID' => $this->currentRecordID(),
|
||||
'Version' => $this->getRequest()->param('VersionID')
|
||||
], null);
|
||||
}
|
||||
@ -2074,8 +2082,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$id = (isset($data['ID'])) ? (int) $data['ID'] : null;
|
||||
$version = (isset($data['Version'])) ? (int) $data['Version'] : null;
|
||||
|
||||
/** @var SiteTree|Versioned $record */
|
||||
$record = Versioned::get_latest_version($this->config()->get('tree_class'), $id);
|
||||
$modelClass = $this->getModelClass();
|
||||
if (!$modelClass::has_extension(Versioned::class)) {
|
||||
throw new HTTPResponse_Exception("$modelClass record cannot be rolled back", 400);
|
||||
}
|
||||
|
||||
/** @var DataObject&Versioned $record */
|
||||
$record = Versioned::get_latest_version($modelClass, $id);
|
||||
if ($record && !$record->canEdit()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
@ -2083,22 +2096,22 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
if ($version) {
|
||||
$record->rollbackRecursive($version);
|
||||
$message = _t(
|
||||
__CLASS__ . '.ROLLEDBACKVERSIONv2',
|
||||
"Rolled back to version #{version}.",
|
||||
LeftAndMain::class . '.ROLLEDBACK_VERSION',
|
||||
'Rolled back to version #{version}.',
|
||||
['version' => $data['Version']]
|
||||
);
|
||||
} else {
|
||||
$record->doRevertToLive();
|
||||
$record->publishRecursive();
|
||||
$message = _t(
|
||||
__CLASS__ . '.ROLLEDBACKPUBv2',
|
||||
"Rolled back to published version."
|
||||
LeftAndMain::class . '.ROLLEDBACK_PUBLISHED',
|
||||
'Rolled back to published version.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->getResponse()->addHeader('X-Status', rawurlencode($message ?? ''));
|
||||
|
||||
// Can be used in different contexts: In normal page edit view, in which case the redirect won't have any effect.
|
||||
// Can be used in different contexts: In normal record edit view, in which case the redirect won't have any effect.
|
||||
// Or in history view, in which case a revert causes the CMS to re-load the edit view.
|
||||
// The X-Pjax header forces a "full" content refresh on redirect.
|
||||
$url = $record->getCMSEditLink();
|
||||
@ -2142,11 +2155,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$forms[$urlSegment] = $formHtml;
|
||||
}
|
||||
}
|
||||
$pageHtml = '';
|
||||
$recordHtml = '';
|
||||
foreach ($forms as $urlSegment => $html) {
|
||||
$pageHtml .= '<div class="params" id="BatchActionParameters_' . $urlSegment . '" style="display:none">' . $html . '</div>';
|
||||
$recordHtml .= '<div class="params" id="BatchActionParameters_' . $urlSegment . '" style="display:none">' . $html . '</div>';
|
||||
}
|
||||
return new LiteralField('BatchActionParameters', '<div id="BatchActionParameters" class="action-parameters" style="display:none">' . $pageHtml . '</div>');
|
||||
return new LiteralField('BatchActionParameters', '<div id="BatchActionParameters" class="action-parameters" style="display:none">' . $recordHtml . '</div>');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2158,7 +2171,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a completely deleted page from the SiteTree_versions table.
|
||||
* Restore a completely deleted record from the *_versions table.
|
||||
*/
|
||||
public function restore(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
@ -2166,21 +2179,29 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
return new HTTPResponse("Please pass an ID in the form content", 400);
|
||||
}
|
||||
|
||||
$id = (int)$data['ID'];
|
||||
$restoredPage = Versioned::get_latest_version(SiteTree::class, $id);
|
||||
if (!$restoredPage) {
|
||||
return new HTTPResponse("SiteTree #$id not found", 400);
|
||||
$modelClass = $this->getModelClass();
|
||||
if (!$modelClass::has_extension(Versioned::class)) {
|
||||
throw new HTTPResponse_Exception("$modelClass record cannot be restored", 400);
|
||||
}
|
||||
|
||||
$restoredPage = $restoredPage->doRestoreToStage();
|
||||
$id = (int)$data['ID'];
|
||||
$restoredRecord = Versioned::get_latest_version($modelClass, $id);
|
||||
if (!$restoredRecord) {
|
||||
return new HTTPResponse("Record #$id not found", 400);
|
||||
}
|
||||
|
||||
$restoredRecord = $restoredRecord->doRestoreToStage();
|
||||
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(
|
||||
__CLASS__ . '.RESTORED',
|
||||
"Restored '{title}' successfully",
|
||||
['title' => $restoredPage->Title]
|
||||
) ?? '')
|
||||
LeftAndMain::class . '.RESTORED_RECORD',
|
||||
'Restored {name} "{title}"',
|
||||
[
|
||||
'name' => $restoredRecord->i18n_singular_name(),
|
||||
'title' => $restoredRecord->Title,
|
||||
]
|
||||
))
|
||||
);
|
||||
|
||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||
@ -2194,31 +2215,34 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
if (($id = $this->urlParams['ID']) && is_numeric($id)) {
|
||||
$page = SiteTree::get()->byID($id);
|
||||
if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) {
|
||||
$record = DataObject::get($this->getModelClass())->byID($id);
|
||||
if ($record && !$record->canCreate(null, ['Parent' => $record->Parent()])) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
if (!$page || !$page->ID) {
|
||||
if (!$record || !$record->ID) {
|
||||
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
}
|
||||
|
||||
$newPage = $page->duplicate();
|
||||
$newRecord = $record->duplicate();
|
||||
|
||||
// ParentID can be hard-set in the URL. This is useful for pages with multiple parents
|
||||
if (isset($_GET['parentID']) && is_numeric($_GET['parentID'])) {
|
||||
$newPage->ParentID = $_GET['parentID'];
|
||||
$newPage->write();
|
||||
$newRecord->ParentID = $_GET['parentID'];
|
||||
$newRecord->write();
|
||||
}
|
||||
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(
|
||||
__CLASS__ . '.DUPLICATED',
|
||||
"Duplicated '{title}' successfully",
|
||||
['title' => $newPage->Title]
|
||||
) ?? '')
|
||||
LeftAndMain::class . '.DUPLICATED_RECORD',
|
||||
'Duplicated {name} "{title}"',
|
||||
[
|
||||
'name' => $newRecord->i18n_singular_name(),
|
||||
'title' => $newRecord->Title,
|
||||
]
|
||||
))
|
||||
);
|
||||
$url = $newPage->getCMSEditLink();
|
||||
$url = $newRecord->getCMSEditLink();
|
||||
$this->getResponse()->addHeader('X-ControllerURL', $url);
|
||||
$this->getRequest()->addHeader('X-Pjax', 'Content');
|
||||
$this->getResponse()->addHeader('X-Pjax', 'Content');
|
||||
@ -2236,25 +2260,28 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
Environment::increaseTimeLimitTo();
|
||||
if (($id = $this->urlParams['ID']) && is_numeric($id)) {
|
||||
$page = SiteTree::get()->byID($id);
|
||||
if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) {
|
||||
$record = DataObject::get($this->getModelClass())->byID($id);
|
||||
if ($record && !$record->canCreate(null, ['Parent' => $record->Parent()])) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
if (!$page || !$page->ID) {
|
||||
if (!$record || !$record->ID) {
|
||||
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||
}
|
||||
|
||||
$newPage = $page->duplicateWithChildren();
|
||||
$newRecord = $record->duplicateWithChildren();
|
||||
|
||||
$this->getResponse()->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(
|
||||
__CLASS__ . '.DUPLICATEDWITHCHILDREN',
|
||||
"Duplicated '{title}' and children successfully",
|
||||
['title' => $newPage->Title]
|
||||
LeftAndMain::class . '.DUPLICATED_RECORD_WITH_CHILDREN',
|
||||
'Duplicated {name} "{title}" and children',
|
||||
[
|
||||
'name' => $newRecord->i18n_singular_name(),
|
||||
'title' => $newRecord->Title,
|
||||
]
|
||||
) ?? '')
|
||||
);
|
||||
$url = $newPage->getCMSEditLink();
|
||||
$url = $newRecord->getCMSEditLink();
|
||||
$this->getResponse()->addHeader('X-ControllerURL', $url);
|
||||
$this->getRequest()->addHeader('X-Pjax', 'Content');
|
||||
$this->getResponse()->addHeader('X-Pjax', 'Content');
|
||||
@ -2269,8 +2296,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
$title = CMSPagesController::menu_title();
|
||||
return [
|
||||
"CMS_ACCESS_CMSMain" => [
|
||||
'name' => _t(__CLASS__ . '.ACCESS', "Access to '{title}' section", ['title' => $title]),
|
||||
'category' => _t('SilverStripe\\Security\\Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
|
||||
'name' => _t(LeftAndMain::class . '.ACCESS', "Access to '{title}' section", ['title' => $title]),
|
||||
'category' => _t(LeftAndMain::class . '.CMS_ACCESS_CATEGORY', 'CMS Access'),
|
||||
'help' => _t(
|
||||
__CLASS__ . '.ACCESS_HELP',
|
||||
'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".'
|
||||
@ -2293,7 +2320,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache key for SiteTreeHints() method
|
||||
* Cache key for TreeHints() method
|
||||
*
|
||||
* @param $memberID
|
||||
* @return string
|
||||
|
@ -1,239 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\CMS\Controllers;
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\OptionsetField;
|
||||
use SilverStripe\Forms\SelectionGroup;
|
||||
use SilverStripe\Forms\SelectionGroup_Item;
|
||||
use SilverStripe\Forms\TreeDropdownField;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\FieldType\DBField;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
|
||||
class CMSPageAddController extends CMSPageEditController
|
||||
{
|
||||
|
||||
private static $url_segment = 'pages/add';
|
||||
private static $url_rule = '/$Action/$ID/$OtherID';
|
||||
private static $url_priority = 42;
|
||||
private static $menu_title = 'Add page';
|
||||
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
||||
|
||||
private static $allowed_actions = [
|
||||
'AddForm',
|
||||
'doAdd',
|
||||
'doCancel'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return Form
|
||||
*/
|
||||
public function AddForm()
|
||||
{
|
||||
$pageTypes = [];
|
||||
$defaultIcon = Config::inst()->get(SiteTree::class, 'icon_class');
|
||||
|
||||
foreach ($this->PageTypes() as $type) {
|
||||
$class = $type->getField('ClassName');
|
||||
$icon = Config::inst()->get($class, 'icon_class') ?: $defaultIcon;
|
||||
|
||||
// If the icon is the SiteTree default and there's some specific icon being provided by `getPageIconURL`
|
||||
// then we don't need to add the icon class. Otherwise the class take precedence.
|
||||
if ($icon === $defaultIcon && !empty(singleton($class)->getPageIconURL())) {
|
||||
$icon = '';
|
||||
}
|
||||
|
||||
$html = sprintf(
|
||||
'<span class="page-icon %s class-%s"></span><span class="title">%s</span><span class="form__field-description">%s</span>',
|
||||
$icon,
|
||||
Convert::raw2htmlid($class),
|
||||
$type->getField('AddAction'),
|
||||
$type->getField('Description')
|
||||
);
|
||||
$pageTypes[$class] = DBField::create_field('HTMLFragment', $html);
|
||||
}
|
||||
// Ensure generic page type shows on top
|
||||
if (isset($pageTypes['Page'])) {
|
||||
$pageTitle = $pageTypes['Page'];
|
||||
$pageTypes = array_merge(['Page' => $pageTitle], $pageTypes);
|
||||
}
|
||||
|
||||
$numericLabelTmpl = '<span class="step-label"><span class="flyout">Step %d. </span><span class="title">%s</span></span>';
|
||||
|
||||
$topTitle = _t('SilverStripe\\CMS\\Controllers\\CMSPageAddController.ParentMode_top', 'Top level');
|
||||
$childTitle = _t('SilverStripe\\CMS\\Controllers\\CMSPageAddController.ParentMode_child', 'Under another page');
|
||||
|
||||
$fields = new FieldList(
|
||||
$parentModeField = new SelectionGroup(
|
||||
"ParentModeField",
|
||||
[
|
||||
$topField = new SelectionGroup_Item(
|
||||
"top",
|
||||
null,
|
||||
$topTitle
|
||||
),
|
||||
new SelectionGroup_Item(
|
||||
'child',
|
||||
$parentField = new TreeDropdownField(
|
||||
"ParentID",
|
||||
"",
|
||||
SiteTree::class,
|
||||
'ID',
|
||||
'TreeTitle'
|
||||
),
|
||||
$childTitle
|
||||
)
|
||||
]
|
||||
),
|
||||
new LiteralField(
|
||||
'RestrictedNote',
|
||||
sprintf(
|
||||
'<p class="alert alert-info message-restricted">%s</p>',
|
||||
_t(
|
||||
'SilverStripe\\CMS\\Controllers\\CMSMain.AddPageRestriction',
|
||||
'Note: Some page types are not allowed for this selection'
|
||||
)
|
||||
)
|
||||
),
|
||||
$typeField = new OptionsetField(
|
||||
"PageType",
|
||||
DBField::create_field(
|
||||
'HTMLFragment',
|
||||
sprintf($numericLabelTmpl ?? '', 2, _t('SilverStripe\\CMS\\Controllers\\CMSMain.ChoosePageType', 'Choose page type'))
|
||||
),
|
||||
$pageTypes,
|
||||
'Page'
|
||||
)
|
||||
);
|
||||
|
||||
$parentModeField->setTitle(DBField::create_field(
|
||||
'HTMLFragment',
|
||||
sprintf($numericLabelTmpl ?? '', 1, _t('SilverStripe\\CMS\\Controllers\\CMSMain.ChoosePageParentMode', 'Choose where to create this page'))
|
||||
));
|
||||
|
||||
$parentField->setSearchFunction(function ($sourceObject, $labelField, $search) {
|
||||
return DataObject::get($sourceObject)
|
||||
->filterAny([
|
||||
'MenuTitle:PartialMatch' => $search,
|
||||
'Title:PartialMatch' => $search,
|
||||
]);
|
||||
});
|
||||
|
||||
$parentModeField->addExtraClass('parent-mode');
|
||||
|
||||
// CMSMain->currentPageID() automatically sets the homepage,
|
||||
// which we need to counteract in the default selection (which should default to root, ID=0)
|
||||
if ($parentID = $this->getRequest()->getVar('ParentID')) {
|
||||
$parentModeField->setValue('child');
|
||||
$parentField->setValue((int)$parentID);
|
||||
} else {
|
||||
$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')
|
||||
->setUseButtonTag(true),
|
||||
FormAction::create("doCancel", _t('SilverStripe\\CMS\\Controllers\\CMSMain.Cancel', "Cancel"))
|
||||
->addExtraClass('btn-secondary')
|
||||
->setUseButtonTag(true)
|
||||
);
|
||||
|
||||
$this->extend('updatePageOptions', $fields);
|
||||
|
||||
$negotiator = $this->getResponseNegotiator();
|
||||
$form = Form::create(
|
||||
$this,
|
||||
"AddForm",
|
||||
$fields,
|
||||
$actions
|
||||
)->setHTMLID('Form_AddForm')->setStrictFormMethodCheck(false);
|
||||
$form->setAttribute('data-hints', $this->SiteTreeHints());
|
||||
$form->setAttribute('data-childfilter', $this->Link('childfilter'));
|
||||
$form->setValidationResponseCallback(function (ValidationResult $errors) use ($negotiator, $form) {
|
||||
$request = $this->getRequest();
|
||||
if ($request->isAjax() && $negotiator) {
|
||||
$result = $form->forTemplate();
|
||||
return $negotiator->respond($request, [
|
||||
'CurrentForm' => function () use ($result) {
|
||||
return $result;
|
||||
}
|
||||
]);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
$form->addExtraClass('flexbox-area-grow fill-height cms-add-form cms-content cms-edit-form ' . $this->BaseCSSClasses());
|
||||
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function doAdd(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$className = isset($data['PageType']) ? $data['PageType'] : "Page";
|
||||
$parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;
|
||||
|
||||
if (!$parentID && isset($data['Parent'])) {
|
||||
$page = SiteTree::get_by_link($data['Parent']);
|
||||
if ($page) {
|
||||
$parentID = $page->ID;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_numeric($parentID) && $parentID > 0) {
|
||||
$parentObj = SiteTree::get()->byID($parentID);
|
||||
} else {
|
||||
$parentObj = null;
|
||||
}
|
||||
|
||||
if (!$parentObj || !$parentObj->ID) {
|
||||
$parentID = 0;
|
||||
}
|
||||
|
||||
if (!singleton($className)->canCreate(Security::getCurrentUser(), ['Parent' => $parentObj])) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
$record = $this->getNewItem("new-$className-$parentID", false);
|
||||
$this->extend('updateDoAdd', $record, $form);
|
||||
$record->write();
|
||||
|
||||
$editController = CMSPageEditController::singleton();
|
||||
$editController->setRequest($this->getRequest());
|
||||
$editController->setCurrentPageID($record->ID);
|
||||
|
||||
$session = $this->getRequest()->getSession();
|
||||
$session->set(
|
||||
"FormInfo.Form_EditForm.formError.message",
|
||||
_t('SilverStripe\\CMS\\Controllers\\CMSMain.PageAdded', 'Successfully created page')
|
||||
);
|
||||
$session->set("FormInfo.Form_EditForm.formError.type", 'good');
|
||||
|
||||
return $this->redirect(Controller::join_links($editController->Link('show'), $record->ID));
|
||||
}
|
||||
|
||||
public function doCancel(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
return $this->redirect(CMSMain::singleton()->Link());
|
||||
}
|
||||
}
|
@ -2,17 +2,15 @@
|
||||
|
||||
namespace SilverStripe\CMS\Controllers;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Admin\LeftAndMain;
|
||||
use SilverStripe\CampaignAdmin\AddToCampaignHandler;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Core\ArrayLib;
|
||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||
use SilverStripe\Core\Validation\ValidationResult;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
|
||||
/**
|
||||
* @package cms
|
||||
@ -57,7 +55,7 @@ class CMSPageEditController extends CMSMain
|
||||
public function addtocampaign(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$id = $data['ID'];
|
||||
$record = \Page::get()->byID($id);
|
||||
$record = DataObject::get($this->getModelClass())->byID($id);
|
||||
|
||||
$handler = AddToCampaignHandler::create($this, $record);
|
||||
$response = $handler->addToCampaign($record, $data);
|
||||
@ -95,15 +93,16 @@ class CMSPageEditController extends CMSMain
|
||||
*/
|
||||
public function getAddToCampaignForm($id)
|
||||
{
|
||||
$modelClass = $this->getModelClass();
|
||||
// Get record-specific fields
|
||||
$record = SiteTree::get()->byID($id);
|
||||
$record = DataObject::get($modelClass)->byID($id);
|
||||
|
||||
if (!$record) {
|
||||
$this->httpError(404, _t(
|
||||
__CLASS__ . '.ErrorNotFound',
|
||||
'That {Type} couldn\'t be found',
|
||||
'',
|
||||
['Type' => Page::singleton()->i18n_singular_name()]
|
||||
['Type' => DataObject::singleton($modelClass)->i18n_singular_name()]
|
||||
));
|
||||
return null;
|
||||
}
|
||||
@ -112,7 +111,7 @@ class CMSPageEditController extends CMSMain
|
||||
__CLASS__.'.ErrorItemPermissionDenied',
|
||||
'It seems you don\'t have the necessary permissions to add {ObjectTitle} to a campaign',
|
||||
'',
|
||||
['ObjectTitle' => Page::singleton()->i18n_singular_name()]
|
||||
['ObjectTitle' => DataObject::singleton($modelClass)->i18n_singular_name()]
|
||||
));
|
||||
return null;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\CMS\Controllers;
|
||||
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Model\ArrayData;
|
||||
|
||||
class CMSPageSettingsController extends CMSMain
|
||||
@ -15,11 +16,19 @@ class CMSPageSettingsController extends CMSMain
|
||||
|
||||
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
||||
|
||||
public function getEditForm($id = null, $fields = null)
|
||||
public function getEditForm($id = null, $fields = null): Form
|
||||
{
|
||||
$record = $this->getRecord($id ?: $this->currentPageID());
|
||||
$record = $this->getRecord($id ?: $this->currentRecordID());
|
||||
|
||||
return parent::getEditForm($id, ($record) ? $record->getSettingsFields() : null);
|
||||
// @TODO ideally settings isn't its own special thing...
|
||||
// can we refactor this so it's just another tab in the main form? And just have it lazyload or something?
|
||||
// At the very least this tab must NOT appear if there are no fields for it.
|
||||
if ($record && $record->hasMethod('getSettingsFields')) {
|
||||
$fields = $record->getSettingsFields();
|
||||
} else {
|
||||
$fields = null;
|
||||
}
|
||||
return parent::getEditForm($id, $fields);
|
||||
}
|
||||
|
||||
public function getTabIdentifier()
|
||||
|
@ -2,16 +2,11 @@
|
||||
|
||||
namespace SilverStripe\CMS\Controllers;
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Model\List\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Model\ArrayData;
|
||||
use stdClass;
|
||||
|
||||
// @TODO What a pointless class!!!!!!
|
||||
class CMSPagesController extends CMSMain
|
||||
{
|
||||
|
||||
private static $url_segment = 'pages';
|
||||
|
||||
private static $url_rule = '/$Action/$ID/$OtherID';
|
||||
@ -27,7 +22,7 @@ class CMSPagesController extends CMSMain
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isCurrentPage(DataObject $record)
|
||||
public function isCurrentRecord(DataObject $record)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ abstract class CMSSiteTreeFilter implements LeftAndMain_SearchFilter
|
||||
return $this->numChildrenMethod;
|
||||
}
|
||||
|
||||
public function getPageClasses($page)
|
||||
public function getRecordClasses($page)
|
||||
{
|
||||
if ($this->_cache_ids === null) {
|
||||
$this->populateIDs();
|
||||
@ -178,7 +178,7 @@ abstract class CMSSiteTreeFilter implements LeftAndMain_SearchFilter
|
||||
}
|
||||
}
|
||||
|
||||
public function isPageIncluded($page)
|
||||
public function isRecordIncluded($page)
|
||||
{
|
||||
if ($this->_cache_ids === null) {
|
||||
$this->populateIDs();
|
||||
|
@ -18,6 +18,8 @@ use SilverStripe\View\Requirements;
|
||||
/**
|
||||
* Extension to include custom page icons
|
||||
*
|
||||
* @TODO AAAHHHHHHHHHHH
|
||||
*
|
||||
* @extends Extension<LeftAndMain>
|
||||
*/
|
||||
class LeftAndMainPageIconsExtension extends Extension implements Flushable
|
||||
|
232
code/Forms/CMSMainAddForm.php
Normal file
232
code/Forms/CMSMainAddForm.php
Normal file
@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\CMS\Forms;
|
||||
|
||||
use SilverStripe\Admin\LeftAndMain;
|
||||
use SilverStripe\CMS\Controllers\CMSMain;
|
||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\OptionsetField;
|
||||
use SilverStripe\Forms\SelectionGroup;
|
||||
use SilverStripe\Forms\SelectionGroup_Item;
|
||||
use SilverStripe\Forms\TreeDropdownField;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\FieldType\DBField;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
|
||||
class CMSMainAddForm extends Form
|
||||
{
|
||||
public function __construct(CMSMain $controller)
|
||||
{
|
||||
$modelClass = $controller->getModelClass();
|
||||
$singleton = DataObject::singleton($modelClass);
|
||||
$recordTypes = [];
|
||||
$defaultIcon = Config::inst()->get($modelClass, 'icon_class'); // @TODO need a better place for default - maybe try default on class, and fallback to default on cmsmain?
|
||||
$defaultRecordType = 'Page'; // @TODO GENERICALIFICATE THIS! Note it's not necessarily the same as $modelClass, 'cause SiteTree aint Page
|
||||
|
||||
foreach ($controller->RecordTypes() as $type) {
|
||||
$class = $type->getField('ClassName');
|
||||
$icon = Config::inst()->get($class, 'icon_class') ?: $defaultIcon;
|
||||
|
||||
// If the icon is the default and there's some specific icon being provided by `getPageIconURL`
|
||||
// then we don't need to add the icon class. Otherwise the class take precedence.
|
||||
if ($icon === $defaultIcon && !empty(DataObject::singleton($class)->getPageIconURL())) { // @TODO AHHH
|
||||
$icon = '';
|
||||
}
|
||||
|
||||
$html = sprintf(
|
||||
'<span class="page-icon %s class-%s"></span><span class="title">%s</span><span class="form__field-description">%s</span>',
|
||||
$icon,
|
||||
Convert::raw2htmlid($class),
|
||||
$type->getField('AddAction'),
|
||||
$type->getField('Description')
|
||||
);
|
||||
$recordTypes[$class] = DBField::create_field('HTMLFragment', $html);
|
||||
}
|
||||
// Ensure default record type shows on top
|
||||
if (isset($recordTypes[$defaultRecordType])) {
|
||||
$typeName = $recordTypes[$defaultRecordType];
|
||||
$recordTypes = array_merge([$defaultRecordType => $typeName], $recordTypes);
|
||||
}
|
||||
|
||||
$numericLabelTmpl = '<span class="step-label"><span class="flyout">Step %d. </span><span class="title">%s</span></span>';
|
||||
|
||||
$topTitle = _t(__CLASS__ . '.ParentMode_top', 'Top level');
|
||||
$childTitle = _t(
|
||||
__CLASS__ . '.ParentMode_child',
|
||||
'Under another {type}',
|
||||
['type' => mb_strtolower($singleton->i18n_singular_name())]
|
||||
);
|
||||
|
||||
$fields = FieldList::create(
|
||||
$parentModeField = SelectionGroup::create(
|
||||
'ParentModeField',
|
||||
[
|
||||
$topField = SelectionGroup_Item::create(
|
||||
'top',
|
||||
null,
|
||||
$topTitle
|
||||
),
|
||||
SelectionGroup_Item::create(
|
||||
'child',
|
||||
$parentField = TreeDropdownField::create(
|
||||
'ParentID',
|
||||
'',
|
||||
$modelClass,
|
||||
'ID',
|
||||
'TreeTitle'
|
||||
),
|
||||
$childTitle
|
||||
)
|
||||
]
|
||||
),
|
||||
LiteralField::create(
|
||||
'RestrictedNote',
|
||||
sprintf(
|
||||
'<p class="alert alert-info message-restricted">%s</p>',
|
||||
_t(
|
||||
__CLASS__ . '.AddRecordRestriction',
|
||||
'Note: Some {model} types are not allowed for this selection',
|
||||
['model' => mb_strtolower($singleton->i18n_singular_name())]
|
||||
)
|
||||
)
|
||||
),
|
||||
OptionsetField::create(
|
||||
'RecordType',
|
||||
DBField::create_field(
|
||||
'HTMLFragment',
|
||||
sprintf($numericLabelTmpl, 2, _t(
|
||||
__CLASS__ . '.ChooseRecordType',
|
||||
'Choose {model} type',
|
||||
['model' => mb_strtolower($singleton->i18n_singular_name())]
|
||||
))
|
||||
),
|
||||
$recordTypes,
|
||||
$defaultRecordType
|
||||
)
|
||||
);
|
||||
|
||||
$parentModeField->setTitle(DBField::create_field(
|
||||
'HTMLFragment',
|
||||
sprintf($numericLabelTmpl, 1, _t(__CLASS__ . '.ChooseParentMode', 'Choose where to create this record'))
|
||||
));
|
||||
|
||||
$parentField->setSearchFunction(function ($sourceObject, $labelField, $search) {
|
||||
return DataObject::get($sourceObject)
|
||||
->filterAny([
|
||||
'MenuTitle:PartialMatch' => $search,
|
||||
'Title:PartialMatch' => $search,
|
||||
]);
|
||||
});
|
||||
|
||||
$parentModeField->addExtraClass('parent-mode');
|
||||
|
||||
// CMSMain->currentRecordID() automatically sets the homepage, // @TODO find out what this is about
|
||||
// which we need to counteract in the default selection (which should default to root, ID=0)
|
||||
if ($parentID = $controller->getRequest()->getVar('ParentID')) {
|
||||
$parentModeField->setValue('child');
|
||||
$parentField->setValue((int)$parentID);
|
||||
} else {
|
||||
$parentModeField->setValue('top');
|
||||
}
|
||||
|
||||
// Check if the current user has enough permissions to create top level records
|
||||
// If not, then disable the option to do that
|
||||
if (is_a($modelClass, SiteTree::class, true) && !SiteConfig::current_site_config()->canCreateTopLevel()) { // @TODO probably need to make this generic
|
||||
$topField->setDisabled(true);
|
||||
$parentModeField->setValue('child');
|
||||
}
|
||||
|
||||
$actions = FieldList::create(
|
||||
FormAction::create('doAdd', _t(CMSMain::class . '.Create', 'Create'))
|
||||
->addExtraClass('btn-primary font-icon-plus-circled')
|
||||
->setUseButtonTag(true),
|
||||
FormAction::create('doCancel', _t(CMSMain::class . '.Cancel', 'Cancel'))
|
||||
->addExtraClass('btn-secondary')
|
||||
->setUseButtonTag(true)
|
||||
);
|
||||
|
||||
$this->extend('updateFields', $fields);
|
||||
parent::__construct($controller, 'AddForm', $fields, $actions);
|
||||
|
||||
$negotiator = $controller->getResponseNegotiator();
|
||||
$this->setHTMLID('Form_AddForm')->setStrictFormMethodCheck(false);
|
||||
$this->setAttribute('data-hints', $controller->TreeHints());
|
||||
$this->setAttribute('data-childfilter', $controller->Link('childfilter'));
|
||||
$this->setValidationResponseCallback(function () use ($negotiator, $controller) {
|
||||
$request = $controller->getRequest();
|
||||
if ($request->isAjax() && $negotiator) {
|
||||
$result = $this->forTemplate();
|
||||
return $negotiator->respond($request, [
|
||||
'CurrentForm' => function () use ($result) {
|
||||
return $result;
|
||||
}
|
||||
]);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
$this->addExtraClass('flexbox-area-grow fill-height cms-add-form cms-content cms-edit-form ' . $controller->BaseCSSClasses());
|
||||
$this->setTemplate($controller->getTemplatesWithSuffix('_AddForm'));
|
||||
}
|
||||
|
||||
public function doAdd(array $data, Form $form): HTTPResponse
|
||||
{
|
||||
$defaultRecordType = 'Page'; // @TODO GENERICALIFICATE THIS
|
||||
$controller = $this->getController();
|
||||
$modelClass = $controller->getModelClass();
|
||||
$className = isset($data['RecordType']) ? $data['RecordType'] : $defaultRecordType; // @TODO shouldn't this throw an error??
|
||||
$parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;
|
||||
|
||||
if (!$parentID && isset($data['Parent'])) {
|
||||
$parentRecord = $modelClass::get_by_link($data['Parent']); // @TODO Obviously no good
|
||||
if ($parentRecord) {
|
||||
$parentID = $parentRecord->ID;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_numeric($parentID) && $parentID > 0) {
|
||||
$parentObj = DataObject::get($modelClass)->byID($parentID);
|
||||
} else {
|
||||
$parentObj = null;
|
||||
}
|
||||
|
||||
if (!$parentObj || !$parentObj->ID) {
|
||||
$parentID = 0;
|
||||
}
|
||||
|
||||
if (!DataObject::singleton($className)->canCreate(Security::getCurrentUser(), ['Parent' => $parentObj])) {
|
||||
return Security::permissionFailure($controller);
|
||||
}
|
||||
|
||||
$record = $controller->getNewItem("new-$className-$parentID", false);
|
||||
$controller->extend('updateDoAdd', $record, $form);
|
||||
$record->write();
|
||||
|
||||
$editController = CMSPageEditController::singleton();
|
||||
$editController->setRequest($controller->getRequest());
|
||||
$editController->setCurrentRecordID($record->ID);
|
||||
|
||||
$controller->getResponse()->addHeader('X-Status', rawurlencode(_t(
|
||||
LeftAndMain::class . '.CREATED_RECORD',
|
||||
'Created {name} "{title}"',
|
||||
[
|
||||
'name' => $record->i18n_singular_name(),
|
||||
'title' => $record->Title,
|
||||
]
|
||||
)));
|
||||
return $controller->redirect($editController->Link('show/' . $record->ID));
|
||||
}
|
||||
|
||||
public function doCancel(): HTTPResponse
|
||||
{
|
||||
return $this->getController()->redirect(CMSMain::singleton()->Link()); // @TODO when there's no CMSPageEditController anymore, change this to $this->getController()->Link()
|
||||
}
|
||||
}
|
@ -7,19 +7,19 @@ use SilverStripe\ORM\DataObject;
|
||||
/**
|
||||
* This interface lets us set up objects that will tell us what the current page is.
|
||||
*/
|
||||
interface CurrentPageIdentifier
|
||||
interface CurrentRecordIdentifier
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the current page ID.
|
||||
* @return int
|
||||
*/
|
||||
public function currentPageID();
|
||||
public function currentRecordID();
|
||||
|
||||
/**
|
||||
* Check if the given DataObject is the current page.
|
||||
* @param DataObject $page The page to check.
|
||||
* @return boolean
|
||||
*/
|
||||
public function isCurrentPage(DataObject $page);
|
||||
public function isCurrentRecord(DataObject $page);
|
||||
}
|
@ -783,12 +783,12 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
||||
*/
|
||||
public function isCurrent()
|
||||
{
|
||||
$currentPage = Director::get_current_page();
|
||||
if ($currentPage instanceof ContentController) {
|
||||
$currentPage = $currentPage->data();
|
||||
$currentRecord = Director::get_current_page();
|
||||
if ($currentRecord instanceof ContentController) {
|
||||
$currentRecord = $currentRecord->data();
|
||||
}
|
||||
if ($currentPage instanceof SiteTree) {
|
||||
return $currentPage === $this || $currentPage->ID === $this->ID;
|
||||
if ($currentRecord instanceof SiteTree) {
|
||||
return $currentRecord === $this || $currentRecord->ID === $this->ID;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -2778,9 +2778,9 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
||||
// Sort alphabetically, and put current on top
|
||||
asort($result);
|
||||
if (isset($result[$this->ClassName])) {
|
||||
$currentPageTypeName = $result[$this->ClassName];
|
||||
$currentRecordTypeName = $result[$this->ClassName];
|
||||
unset($result[$this->ClassName]);
|
||||
$result = [$this->ClassName => $currentPageTypeName] + $result;
|
||||
$result = [$this->ClassName => $currentRecordTypeName] + $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
@ -256,6 +256,8 @@ en:
|
||||
TABCONTENT: 'Main content'
|
||||
TABDEPENDENT: 'Dependent pages'
|
||||
TOPLEVEL: 'Site Content (Top Level)'
|
||||
TREETITLE: 'Page name'
|
||||
TREETYPE: 'Page type'
|
||||
UNTITLED: 'Untitled {pagetype}'
|
||||
URLSegment: 'URL segment'
|
||||
UntitledDependentObject: 'Untitled {instanceType}'
|
||||
|
@ -0,0 +1,47 @@
|
||||
<div class="flexbox-area-grow cms-content $Controller.BaseCSSClasses" data-layout-type="border" data-pjax-fragment="Content">
|
||||
<form $FormAttributes data-layout-type="border">
|
||||
<div class="toolbar toolbar--north">
|
||||
<div class="toolbar__navigation">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb__item">
|
||||
<% if $Controller.SectionTitle %>
|
||||
$Controller.SectionTitle
|
||||
<% else %>
|
||||
<%t SilverStripe\CMS\Controllers\CMSMain.Title 'Data Models'%>
|
||||
<% end_if %>
|
||||
</li>
|
||||
<li class="breadcrumb__item breadcrumb__item--last breadcrumb__item--no-crumb">
|
||||
<h2 class="breadcrumb__item-title breadcrumb__item-title--last">
|
||||
<%t SilverStripe\Admin\LeftAndMain.NewRecord 'New {name}' name=$Controller.getRecord('singleton').i18n_singular_name() %>
|
||||
</h2>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel--padded panel--scrollable flexbox-area-grow">
|
||||
<% if $Message %>
|
||||
<p id="{$FormName}_error" class="alert $AlertType">$Message</p>
|
||||
<% else %>
|
||||
<p id="{$FormName}_error" class="alert $AlertType" style="display: none"></p>
|
||||
<% end_if %>
|
||||
|
||||
<fieldset>
|
||||
<% if $Legend %><legend>$Legend</legend><% end_if %>
|
||||
<% loop $Fields %>
|
||||
$FieldHolder
|
||||
<% end_loop %>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="toolbar--south">
|
||||
<% if $Actions %>
|
||||
<div class="btn-toolbar">
|
||||
<% loop $Actions %>
|
||||
$Field
|
||||
<% end_loop %>
|
||||
</div>
|
||||
<% end_if %>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -4,24 +4,24 @@
|
||||
<div class="fill-height flexbox-area-grow">
|
||||
<div class="cms-content-header north">
|
||||
<div class="cms-content-header-info flexbox-area-grow vertical-align-items">
|
||||
<a href="$BreadcrumbsBackLink" class="btn btn-secondary btn--no-text font-icon-left-open-big hidden-lg-up toolbar__back-button"></a>
|
||||
<% include SilverStripe\\Admin\\BackLink_Button Backlink=$BreadcrumbsBacklink %>
|
||||
<% include SilverStripe\\Admin\\CMSBreadcrumbs %>
|
||||
</div>
|
||||
|
||||
<div class="cms-content-header-tabs cms-tabset">
|
||||
<ul class="cms-tabset-nav-primary nav nav-tabs">
|
||||
<li class="nav-item content-treeview<% if $TabIdentifier == 'edit' %> ui-tabs-active<% end_if %>">
|
||||
<a href="$LinkPageEdit" class="nav-link cms-panel-link" title="Form_EditForm" data-href="$LinkPageEdit">
|
||||
<a href="$LinkRecordEdit" class="nav-link cms-panel-link" title="Form_EditForm" data-href="$LinkRecordEdit">
|
||||
<%t SilverStripe\\CMS\\Controllers\\CMSMain.TabContent 'Content' %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item content-listview<% if $TabIdentifier == 'settings' %> ui-tabs-active<% end_if %>">
|
||||
<a href="$LinkPageSettings" class="nav-link cms-panel-link" title="Form_EditForm" data-href="$LinkPageSettings">
|
||||
<a href="$LinkRecordSettings" class="nav-link cms-panel-link" title="Form_EditForm" data-href="$LinkRecordSettings">
|
||||
<%t SilverStripe\\CMS\\Controllers\\CMSMain.TabSettings 'Settings' %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item content-listview<% if $TabIdentifier == 'history' %> ui-tabs-active<% end_if %>">
|
||||
<a href="$LinkPageHistory" class="nav-link cms-panel-link" title="Form_EditForm" data-href="$LinkPageHistory">
|
||||
<a href="$LinkRecordHistory" class="nav-link cms-panel-link" title="Form_EditForm" data-href="$LinkRecordHistory">
|
||||
<%t SilverStripe\\CMS\\Controllers\\CMSMain.TabHistory 'History' %>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSPagesController_ContentToolActions %>
|
||||
|
||||
<div class="ss-dialog cms-page-add-form-dialog cms-dialog-content" id="cms-page-add-form" title="<%t SilverStripe\\CMS\\Controllers\\CMSMain.AddNew 'Add new page' %>">
|
||||
<div class="ss-dialog cms-page-add-form-dialog cms-dialog-content" id="cms-page-add-form" title="<%t SilverStripe\Admin\\LeftAndMain.AddNew 'Add new {name}' name=$getRecord('singleton').i18n_singular_name().lowercase %>">
|
||||
$AddForm
|
||||
</div>
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSMain_PageList %>
|
@ -0,0 +1 @@
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSMain_RecordList %>
|
@ -4,7 +4,7 @@
|
||||
<% if $limited %>
|
||||
<ul><li class="readonly">
|
||||
<span class="item">
|
||||
<%t SilverStripe\\CMS\\Controllers\\CMSMain.TOO_MANY_PAGES 'Too many pages' %>
|
||||
<%t SilverStripe\\CMS\\Controllers\\CMSMain.TOO_MANY_RECORDS 'Too many records' %>
|
||||
(<a href="{$listViewLink.ATT}" class="subtree-list-link" data-id="$node.ID" data-pjax-target="Content"><%t SilverStripe\\CMS\\Controllers\\CMSMain.SHOW_AS_LIST 'show as list' %></a>)
|
||||
</span>
|
||||
</li></ul>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="cms-content-header north vertical-align-items">
|
||||
<div class="cms-content-header-info vertical-align-items fill-width">
|
||||
<div class="section-heading flexbox-area-grow">
|
||||
<span class="section-label"><a href="$LinkPages">{$MenuCurrentItem.Title}</a></span>
|
||||
<span class="section-label"><a href="$LinkRecords">{$MenuCurrentItem.Title}</a></span>
|
||||
</div>
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSMain_Filter %>
|
||||
</div>
|
||||
@ -14,7 +14,7 @@
|
||||
data-schema="$SearchFieldSchema"
|
||||
></div>
|
||||
</div>
|
||||
$PageListSidebar
|
||||
$RecordListSidebar
|
||||
</div>
|
||||
<div class="cms-panel-content-collapsed">
|
||||
<h3 class="cms-panel-header">$SiteConfig.Title</h3>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSPagesController_ContentToolActions View='Tree' %>
|
||||
|
||||
<div class="ss-dialog cms-page-add-form-dialog cms-dialog-content" id="cms-page-add-form" title="<%t SilverStripe\CMS\Controllers\CMSMain.AddNew 'Add new page' %>">
|
||||
<div class="ss-dialog cms-page-add-form-dialog cms-dialog-content" id="cms-page-add-form" title="<%t SilverStripe\Admin\LeftAndMain.AddNew 'Add new {name}' name=$getRecord('singleton').i18n_singular_name().lowercase %>">
|
||||
$AddForm
|
||||
</div>
|
||||
|
||||
@ -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
|
||||
</div>
|
||||
</div>
|
||||
<% 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
|
||||
</div>
|
||||
<% end_if %>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="view-controls view-controls--{$ViewState}">
|
||||
<% 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 --%>
|
||||
<a class="page-view-link btn btn-secondary btn--icon-sm btn--no-text font-icon-tree"
|
||||
href="$LinkTreeView.ATT"
|
||||
data-view="treeview"
|
||||
|
@ -1,45 +0,0 @@
|
||||
<div class="flexbox-area-grow cms-content $BaseCSSClasses" data-layout-type="border" data-pjax-fragment="Content">
|
||||
<% with $AddForm %>
|
||||
<form $FormAttributes data-layout-type="border">
|
||||
<div class="toolbar toolbar--north">
|
||||
<div class="toolbar__navigation">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb__item">
|
||||
<%t SilverStripe\CMS\Controllers\CMSPagesController.MENUTITLE 'Pages'%>
|
||||
</li>
|
||||
<li class="breadcrumb__item breadcrumb__item--last breadcrumb__item--no-crumb">
|
||||
<h2 class="breadcrumb__item-title breadcrumb__item-title--last">
|
||||
<%t SilverStripe\CMS\Controllers\CMSPageAddController.Title 'Add page' %>
|
||||
</h2>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel--padded panel--scrollable flexbox-area-grow">
|
||||
<% if $Message %>
|
||||
<p id="{$FormName}_error" class="alert $AlertType">$Message</p>
|
||||
<% else %>
|
||||
<p id="{$FormName}_error" class="alert $AlertType" style="display: none"></p>
|
||||
<% end_if %>
|
||||
|
||||
<fieldset>
|
||||
<% if $Legend %><legend>$Legend</legend><% end_if %>
|
||||
<% loop $Fields %>
|
||||
$FieldHolder
|
||||
<% end_loop %>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="toolbar--south">
|
||||
<% if $Actions %>
|
||||
<div class="btn-toolbar">
|
||||
<% loop $Actions %>
|
||||
$Field
|
||||
<% end_loop %>
|
||||
</div>
|
||||
<% end_if %>
|
||||
</div>
|
||||
</form>
|
||||
<% end_with %>
|
||||
</div>
|
@ -2,9 +2,7 @@
|
||||
<div class="cms-content-header north vertical-align-items">
|
||||
<div class="cms-content-header-info fill-width vertical-align-items">
|
||||
<% if $TreeIsFiltered %>
|
||||
<a class="btn btn-secondary font-icon-left-open-big toolbar__back-button btn--no-text" href="$BreadcrumbsBacklink">
|
||||
<span class="sr-only"><%t SilverStripe\Admin\LeftAndMain.NavigateUp "Return to Pages" %></span>
|
||||
</a>
|
||||
<% include SilverStripe\\Admin\\BackLink_Button Backlink=$BreadcrumbsBacklink %>
|
||||
<% end_if %>
|
||||
<% include SilverStripe\\Admin\\CMSBreadcrumbs %>
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSMain_Filter %>
|
||||
@ -13,6 +11,6 @@
|
||||
|
||||
<div class="flexbox-area-grow fill-height cms-content-fields ui-widget-content cms-panel-padded">
|
||||
$Tools
|
||||
$PageList
|
||||
$RecordList
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,9 @@
|
||||
<div class="toolbar toolbar--content cms-content-toolbar">
|
||||
<div class="btn-toolbar cms-actions-buttons-row">
|
||||
<% if not $TreeIsFiltered %>
|
||||
<a class="btn btn-primary cms-content-addpage-button tool-button font-icon-plus" href="$LinkPageAdd" data-url-addpage="{$LinkPageAdd('', 'ParentID=%s')}"><%t SilverStripe\CMS\Controllers\CMSMain.AddNewButton 'Add new' %></a>
|
||||
<a class="btn btn-primary cms-content-addpage-button tool-button font-icon-plus" href="$LinkRecordAdd" data-url-addpage="{$LinkRecordAdd('', 'ParentID=%s')}">
|
||||
<%t SilverStripe\Admin\\LeftAndMain.AddNew 'Add new {name}' name=$getRecord('singleton').i18n_singular_name().lowercase %>
|
||||
</a>
|
||||
|
||||
<% if $View == 'Tree' %>
|
||||
<button type="button" class="cms-content-batchactions-button btn btn-secondary tool-button font-icon-check-mark-2 btn--last" data-toolid="batch-actions">
|
||||
@ -10,7 +12,7 @@
|
||||
<% end_if %>
|
||||
<% end_if %>
|
||||
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSMain_ViewControls PJAXTarget='Content-PageList' %>
|
||||
<% include SilverStripe\\CMS\\Controllers\\CMSMain_ViewControls PJAXTarget='Content-RecordList' %>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -44,15 +44,15 @@ class CMSMainTest extends FunctionalTest
|
||||
}
|
||||
}
|
||||
|
||||
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) ?? '');
|
||||
@ -423,7 +423,7 @@ class CMSMainTest extends FunctionalTest
|
||||
);
|
||||
$jsonStr = $response->getBody();
|
||||
$data = json_decode($jsonStr, true);
|
||||
|
||||
|
||||
$parser = new CSSContentParser($data['Breadcrumbs']);
|
||||
$crumbs = $parser->getBySelector('.breadcrumbs-wrapper .crumb');
|
||||
|
||||
@ -611,7 +611,7 @@ class CMSMainTest extends FunctionalTest
|
||||
$this->assertEquals('Class A', $newPage->Title);
|
||||
}
|
||||
|
||||
public function testSiteTreeHintsCache()
|
||||
public function testTreeHintsCache()
|
||||
{
|
||||
$cms = CMSMain::create();
|
||||
/** @var Member $user */
|
||||
@ -635,31 +635,31 @@ class CMSMainTest extends FunctionalTest
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -703,21 +703,21 @@ class CMSMainTest extends FunctionalTest
|
||||
);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
$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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
$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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
$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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
$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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
$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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
);
|
||||
|
||||
$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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
|
||||
// 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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
'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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
|
||||
// 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 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user