mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
Merge 91cfcb8229
into bd48b04731
This commit is contained in:
commit
7221223567
@ -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