14 lines
20 KiB
JavaScript
14 lines
20 KiB
JavaScript
/*
|
|
* This file is part of the TYPO3 CMS project.
|
|
*
|
|
* It is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* For the full copyright and license information, please read the
|
|
* LICENSE.txt file that was distributed with this source code.
|
|
*
|
|
* The TYPO3 project - inspiring people to share!
|
|
*/
|
|
import{MessageUtility as R}from"@typo3/backend/utility/message-utility.js";import{Collapse as x}from"bootstrap";import{AjaxDispatcher as w}from"@typo3/backend/form-engine/inline-relation/ajax-dispatcher.js";import F from"@typo3/core/document-service.js";import"@typo3/backend/element/progress-bar-element.js";import E from"sortablejs";import j from"@typo3/backend/form-engine.js";import v from"@typo3/backend/form-engine-validation.js";import S from"@typo3/backend/icons.js";import B from"@typo3/backend/info-window.js";import O from"@typo3/backend/modal.js";import D from"@typo3/backend/notification.js";import h from"@typo3/core/event/regular-event.js";import U from"@typo3/backend/severity.js";import g from"@typo3/backend/utility.js";import{selector as p}from"@typo3/core/literals.js";import C from"~labels/backend.alt_doc";import G from"~labels/core.core";var f;(function(c){c.controlSectionSelector=".t3js-formengine-irre-control",c.createNewRecordButtonSelector=".t3js-create-new-button",c.createNewRecordBySelectorSelector=".t3js-create-new-selector",c.createNewRecordByPresetSelector=".t3js-create-new-preset",c.deleteRecordButtonSelector=".t3js-editform-delete-inline-record",c.enableDisableRecordButtonSelector=".t3js-toggle-visibility-button",c.infoWindowButton='[data-action="infowindow"]',c.synchronizeLocalizeRecordButtonSelector=".t3js-synchronizelocalize-button",c.uniqueValueSelectors="select.t3js-inline-unique",c.revertUniqueness=".t3js-revert-unique",c.controlContainer=".t3js-inline-controls"})(f||(f={}));var q;(function(c){c.new="inlineIsNewRecord",c.notLoaded="t3js-not-loaded"})(q||(q={}));var m;(function(c){c.structureSeparator="-"})(m||(m={}));var b;(function(c){c.DOWN="down",c.UP="up"})(b||(b={}));class d extends HTMLElement{constructor(){super(...arguments),this.ajaxDispatcher=null,this.recordsContainer=null,this.requestQueue={},this.progressQueue={},this.noTitleString=G.get("labels.no_title"),this.handlePostMessage=e=>{if(!R.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if(e.data.actionName==="typo3:foreignRelation:insert"){if(typeof e.data.objectGroup>"u")throw"No object group defined for message";if(e.data.objectGroup!==this.objectGroup)return;if(this.isUniqueElementUsed(parseInt(e.data.uid,10),e.data.table)){D.error("There is already a relation to the selected element");return}this.importRecord([e.data.objectGroup,e.data.uid]).then(()=>{if(e.source){const t={actionName:"typo3:foreignRelation:inserted",objectGroup:e.data.objectId,table:e.data.table,uid:e.data.uid};R.send(t,e.source)}})}if(e.data.actionName==="typo3:foreignRelation:delete"){if(e.data.objectGroup!==this.objectGroup)return;const t=e.data.directRemoval||!1,o=[e.data.objectGroup,e.data.uid].join("-");this.deleteRecord(o,t)}}}get objectGroup(){return this.dataset.objectGroup}get formField(){return this.dataset.formField}get expandSingle(){return this.dataset.expandSingle==="true"}get sortable(){return this.dataset.sortable==="true"}get min(){return parseInt(this.dataset.min,10)||0}get max(){return parseInt(this.dataset.max,10)||0}get type(){const e=this.dataset.type;return e==="file"||e==="language"?e:"record"}get endpoints(){switch(this.type){case"file":return{create:"file_reference_create",details:"file_reference_details",synchronizelocalize:"file_reference_synchronizelocalize",expandcollapse:"file_reference_expandcollapse"};case"language":return{create:"site_configuration_inline_create",details:"site_configuration_inline_details",synchronizelocalize:null,expandcollapse:null};default:return{create:"record_inline_create",details:"record_inline_details",synchronizelocalize:"record_inline_synchronizelocalize",expandcollapse:"record_inline_expandcollapse"}}}static getValuesFromHashMap(e){return Object.keys(e).map(t=>e[t])}static selectOptionValueExists(e,t){return e.querySelector(p`option[value="${t}"]`)!==null}static removeSelectOptionByValue(e,t){const o=e.querySelector(p`option[value="${t}"]`);o!==null&&o.remove()}static reAddSelectOption(e,t,o){if(d.selectOptionValueExists(e,t))return;const i=e.querySelectorAll("option");let n=-1;for(const a of Object.keys(o.possible)){if(a===t)break;for(let s=0;s<i.length;++s)if(i[s].value===a){n=s;break}}n===-1?n=0:n<i.length&&n++;const r=document.createElement("option");r.text=o.possible[t],r.value=t,e.insertBefore(r,e.options[n])}connectedCallback(){this.ajaxDispatcher=new w(this.objectGroup),this.recordsContainer=this.querySelector(p`[id="${this.id}_records"]`),this.registerEvents()}getRecordContainer(e){return this.querySelector(p`[data-object-id="${e}"]`)}getCollapseContent(e){return this.querySelector(p`[id="${e}_fields"]`)}collapseElement(e){x.getOrCreateInstance(this.getCollapseContent(e)).hide()}isNewRecord(e){return this.getRecordContainer(e).classList.contains(q.new)}updateExpandedCollapsedStateLocally(e,t){const o=this.getRecordContainer(e),i=this.querySelectorAll('[name="uc[inlineView]['+o.dataset.topmostParentTable+"]["+o.dataset.topmostParentUid+"]"+o.dataset.fieldName+'"]');i.length&&(i[0].value=t?"1":"0")}async registerEvents(){if(await F.ready(),this.registerInfoButton(),this.registerSort(),this.registerCreateRecordButton(),this.registerEnableDisableButton(),this.registerDeleteButton(),this.registerSynchronizeLocalize(),this.registerRevertUniquenessAction(),this.registerToggle(),this.registerCreateRecordBySelector(),this.registerCreateRecordByPresetSelector(),this.registerUniqueSelectFieldChanged(),new h("message",this.handlePostMessage).bindTo(window),this.sortable){const e=this.recordsContainer;new E(e,{group:e.getAttribute("id"),handle:".sortableHandle",onSort:()=>{this.updateSorting()}})}}registerToggle(){new h("show.bs.collapse",e=>{const o=e.target.parentElement;if(o.closest("typo3-formengine-container-inline")!==this)return;const i=o.dataset.objectId;if(o.classList.contains(q.notLoaded)){e.preventDefault(),this.loadRecordDetails(i);return}this.expandSingle&&this.collapseAllRecords(o.dataset.objectUid)}).bindTo(this),new h("shown.bs.collapse",e=>{const o=e.target.closest("[data-object-id]");o===null||o.closest("typo3-formengine-container-inline")!==this||this.persistExpandCollapseState(o.dataset.objectId,!0)}).bindTo(this),new h("hidden.bs.collapse",e=>{const o=e.target.closest("[data-object-id]");o===null||o.closest("typo3-formengine-container-inline")!==this||this.persistExpandCollapseState(o.dataset.objectId,!1)}).bindTo(this)}registerSort(){new h("click",(e,t)=>{e.preventDefault(),e.stopImmediatePropagation(),this.changeSortingByButton(t.closest("[data-object-id]").dataset.objectId,t.dataset.direction)}).delegateTo(this,f.controlSectionSelector+' [data-action="sort"]')}registerCreateRecordButton(){new h("click",(e,t)=>{if(e.preventDefault(),e.stopImmediatePropagation(),this.isBelowMax()){let o=this.objectGroup;typeof t.dataset.recordUid<"u"&&(o+=m.structureSeparator+t.dataset.recordUid),this.importRecord([o,this.querySelector(f.createNewRecordBySelectorSelector)?.value],t.dataset.recordUid??null)}}).delegateTo(this,f.createNewRecordButtonSelector)}registerCreateRecordBySelector(){new h("change",(e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const o=t,i=o.options[o.selectedIndex].getAttribute("value");this.importRecord([this.objectGroup,i])}).delegateTo(this,f.createNewRecordBySelectorSelector)}registerCreateRecordByPresetSelector(){new h("change",e=>{e.preventDefault(),e.stopImmediatePropagation();const t=this.querySelector(f.createNewRecordByPresetSelector),o=t?.value;o!==""&&(t.value="",this.importRecord([this.objectGroup,"",o]))}).delegateTo(this,f.createNewRecordByPresetSelector)}createRecord(e,t,o=null,i=null){let n=this.objectGroup;o!==null&&(n+=m.structureSeparator+o),o!==null?(this.getRecordContainer(n).insertAdjacentHTML("afterend",t),this.memorizeAddRecord(e,o,i)):(this.recordsContainer.insertAdjacentHTML("beforeend",t),this.memorizeAddRecord(e,null,i))}async importRecord(e,t){return this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint(this.endpoints.create)),e).then(async o=>{this.isBelowMax()&&this.createRecord(o.compilerInput.uid,o.data,typeof t<"u"?t:null,typeof o.compilerInput.childChildUid<"u"?o.compilerInput.childChildUid:null)})}registerEnableDisableButton(){new h("click",(e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const o=t.closest("[data-object-id]").dataset.objectId,i=this.getRecordContainer(o),n=p`data${i.dataset.fieldName}[${t.dataset.hiddenField}]`,r=this.querySelector('[data-formengine-input-name="'+n+'"'),a=this.querySelector('[name="'+n+'"');if(r!==null&&a!==null){r.checked=!r.checked;const y=r.checked!==(r.dataset.invertStateDisplay==="true");a.value=y?"1":"0",j.markFieldAsChanged(r)}const s="panel-hidden",l=i.classList.contains(s);let u;l?(u="actions-edit-hide",i.classList.remove(s)):(u="actions-edit-unhide",i.classList.add(s)),S.getIcon(u,S.sizes.small).then(y=>{t.replaceChild(document.createRange().createContextualFragment(y),t.querySelector(".t3js-icon"))})}).delegateTo(this,f.enableDisableRecordButtonSelector)}registerInfoButton(){new h("click",function(e){e.preventDefault(),e.stopImmediatePropagation(),B.showItem(this.dataset.infoTable,this.dataset.infoUid)}).delegateTo(this,f.infoWindowButton)}registerDeleteButton(){new h("click",(e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const o=C.get("label.confirm.delete_record.title"),i=C.get("label.confirm.delete_record.content",[t.dataset.recordInfo]),n=O.confirm(o,i,U.warning,[{text:C.get("buttons.confirm.delete_record.no"),active:!0,btnClass:"btn-default",name:"no"},{text:C.get("buttons.confirm.delete_record.yes"),btnClass:"btn-warning",name:"yes"}]);n.addEventListener("button.clicked",r=>{if(r.target.name==="yes"){const a=t.closest("[data-object-id]").dataset.objectId;this.deleteRecord(a)}n.hideModal()})}).delegateTo(this,f.deleteRecordButtonSelector)}registerSynchronizeLocalize(){new h("click",(e,t)=>{if(e.preventDefault(),e.stopImmediatePropagation(),this.endpoints.synchronizelocalize===null){console.error(`Synchronize/localize is not supported for type "${this.type}"`);return}this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint(this.endpoints.synchronizelocalize)),[this.objectGroup,t.dataset.type]).then(async o=>{this.recordsContainer.insertAdjacentHTML("beforeend",o.data);const i=this.objectGroup+m.structureSeparator;for(const n of o.compilerInput.delete)this.deleteRecord(i+n,!0);for(const n of Object.values(o.compilerInput.localize)){if(typeof n.remove<"u"){const r=this.getRecordContainer(i+n.remove);r.parentElement.removeChild(r)}this.memorizeAddRecord(n.uid,null,n.selectedValue)}})}).delegateTo(this,f.synchronizeLocalizeRecordButtonSelector)}registerUniqueSelectFieldChanged(){new h("change",(e,t)=>{e.preventDefault(),e.stopImmediatePropagation();const o=t.closest("[data-object-id]");if(o!==null){const i=o.dataset.objectId,n=o.dataset.objectUid;this.handleChangedField(t,i);const r=this.getFormFieldForElements();if(r===null)return;this.updateUnique(t,r,n)}}).delegateTo(this,f.uniqueValueSelectors)}registerRevertUniquenessAction(){new h("click",(e,t)=>{e.preventDefault(),e.stopImmediatePropagation(),this.revertUnique(t.dataset.uid)}).delegateTo(this,f.revertUniqueness)}loadRecordDetails(e){const t=this.getCollapseContent(e),o=this.getRecordContainer(e),i=typeof this.requestQueue[e]<"u",n=this.getProgress(e);if(i)this.requestQueue[e].abort(),delete this.requestQueue[e],delete this.progressQueue[e],n.done();else{const r=this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint(this.endpoints.details));this.ajaxDispatcher.send(r,[e]).then(async s=>{if(delete this.requestQueue[e],delete this.progressQueue[e],o.classList.remove(q.notLoaded),t.innerHTML=s.data,n.done(),this.expandSingle&&this.collapseAllRecords(o.dataset.objectUid),x.getOrCreateInstance(t).show(),j.reinitialize(),v.initializeInputFields(),v.validate(this),this.hasObjectGroupDefinedUniqueConstraints()){const l=this.getRecordContainer(e);this.removeUsed(l)}}).catch(s=>{if(!(s instanceof DOMException&&s.name==="AbortError"))throw s}),this.requestQueue[e]=r,n.start()}}persistExpandCollapseState(e,t){if(this.endpoints.expandcollapse===null)return;if(this.isNewRecord(e)){this.updateExpandedCollapsedStateLocally(e,t);return}const o=this.getRecordContainer(e),i=t?o.dataset.objectUid:"",n=t?"":o.dataset.objectUid;this.ajaxDispatcher.send(this.ajaxDispatcher.newRequest(this.ajaxDispatcher.getEndpoint(this.endpoints.expandcollapse)),[e,i,n])}memorizeAddRecord(e,t=null,o=null){const i=this.getFormFieldForElements();if(i===null)return;let n=g.trimExplode(",",i.value);if(t){const r=[];for(let a=0;a<n.length;a++)n[a].length&&r.push(n[a]),t===n[a]&&r.push(e);n=r}else n.push(e);i.value=n.join(","),j.markFieldAsChanged(i),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.objectGroup,n),this.setUnique(e,o),this.isBelowMax()||this.toggleContainerControls(!1),j.reinitialize(),v.initializeInputFields(),v.validate(this)}memorizeRemoveRecord(e){const t=this.getFormFieldForElements();if(t===null)return[];const o=g.trimExplode(",",t.value),i=o.indexOf(e);return i>-1&&(o.splice(i,1),t.value=o.join(","),j.markFieldAsChanged(t),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.objectGroup,o)),o}changeSortingByButton(e,t){const o=this.getRecordContainer(e),i=o.dataset.objectUid,n=this.recordsContainer,r=Array.from(n.children).map(l=>l.dataset.objectUid),a=r.indexOf(i);let s=!1;if(t===b.UP&&a>0?(r[a]=r[a-1],r[a-1]=i,s=!0):t===b.DOWN&&a<r.length-1&&(r[a]=r[a+1],r[a+1]=i,s=!0),s){const l=this.objectGroup+m.structureSeparator,u=t===b.UP?1:0;o.parentElement.insertBefore(this.getRecordContainer(l+r[a-u]),this.getRecordContainer(l+r[a+1-u])),this.updateSorting()}}updateSorting(){const e=this.getFormFieldForElements();if(e===null)return;const t=this.recordsContainer,o=Array.from(t.querySelectorAll(p`[data-object-parent-group="${this.objectGroup}"][data-placeholder-record="0"]`)).map(i=>i.dataset.objectUid);e.value=o.join(","),j.markFieldAsChanged(e),document.dispatchEvent(new Event("change")),this.redrawSortingButtons(this.objectGroup,o)}deleteRecord(e,t=!1){const o=this.getRecordContainer(e),i=o.dataset.objectUid;if(o.classList.add("t3js-inline-record-deleted"),!this.isNewRecord(e)&&!t){const n=this.querySelector(p`[name="cmd${o.dataset.fieldName}[delete]"]`);n.removeAttribute("disabled"),o.parentElement.insertAdjacentElement("afterbegin",n)}new h("transitionend",()=>{o.remove(),v.validate(this)}).bindTo(o),this.revertUnique(i),this.memorizeRemoveRecord(i),o.classList.add("form-irre-object--deleted"),this.isBelowMax()&&this.toggleContainerControls(!0)}toggleContainerControls(e){this.querySelectorAll(":scope > "+f.controlContainer).forEach(o=>{o.querySelectorAll("button, a").forEach(n=>{n.hidden=!e})})}getProgress(e){let t;if(typeof this.progressQueue[e]<"u")t=this.progressQueue[e];else{t=document.createElement("typo3-backend-progress-bar");const o=this.getRecordContainer(e);o.insertBefore(t,o.firstChild),this.progressQueue[e]=t}return t}collapseAllRecords(e){const t=this.getFormFieldForElements();if(t!==null){const o=g.trimExplode(",",t.value);for(const i of o){if(i===e)continue;const n=this.objectGroup+m.structureSeparator+i;this.getCollapseContent(n)?.classList.contains("show")&&this.collapseElement(n)}}}getFormFieldForElements(){return this.querySelector(p`[name="${this.formField}"]`)}redrawSortingButtons(e,t=[]){if(t.length===0){const o=this.getFormFieldForElements();o!==null&&(t=g.trimExplode(",",o.value))}t.length!==0&&t.forEach((o,i)=>{const n=this.getRecordContainer(e+m.structureSeparator+o);if(n===null)return;const r=n.querySelector(p`[data-action="sort"][data-direction="${b.UP}"]`);if(r!==null){let s="actions-move-up";i===0?(r.classList.add("disabled"),s="empty-empty"):r.classList.remove("disabled"),S.getIcon(s,S.sizes.small).then(l=>{r.replaceChild(document.createRange().createContextualFragment(l),r.querySelector(".t3js-icon"))})}const a=n.querySelector(p`[data-action="sort"][data-direction="${b.DOWN}"]`);if(a!==null){let s="actions-move-down";i===t.length-1?(a.classList.add("disabled"),s="empty-empty"):a.classList.remove("disabled"),S.getIcon(s,S.sizes.small).then(l=>{a.replaceChild(document.createRange().createContextualFragment(l),a.querySelector(".t3js-icon"))})}})}isBelowMax(){const e=this.getFormFieldForElements();if(e===null)return!0;const t=g.trimExplode(",",e.value);if(this.max>0&&t.length>=this.max)return!1;if(this.hasObjectGroupDefinedUniqueConstraints()){const o=TYPO3.settings.FormEngineInline.unique[this.objectGroup];if(o.used.length>=o.max&&o.max>=0)return!1}return!0}isUniqueElementUsed(e,t){if(!this.hasObjectGroupDefinedUniqueConstraints())return!1;const o=TYPO3.settings.FormEngineInline.unique[this.objectGroup],i=d.getValuesFromHashMap(o.used);if(o.type==="select"&&i.indexOf(e)!==-1)return!0;if(o.type==="groupdb"){for(let n=i.length-1;n>=0;n--)if(i[n].table===t&&i[n].uid===e)return!0}return!1}removeUsed(e){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const t=TYPO3.settings.FormEngineInline.unique[this.objectGroup];if(t.type!=="select")return;const o=e.querySelector('[name="data['+t.table+"]["+e.dataset.objectUid+"]["+t.field+']"]'),i=d.getValuesFromHashMap(t.used);if(o!==null){const n=o.options[o.selectedIndex].value;for(const r of i)r!==n&&d.removeSelectOptionByValue(o,r)}}setUnique(e,t){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const o=this.querySelector(p`[id="${this.objectGroup}_selector"]`),i=TYPO3.settings.FormEngineInline.unique[this.objectGroup];if(i.type==="select"){if(!(i.selector&&i.max===-1)){const n=this.getFormFieldForElements(),r=this.objectGroup+m.structureSeparator+e;let s=this.getRecordContainer(r).querySelector('[name="data['+i.table+"]["+e+"]["+i.field+']"]');const l=d.getValuesFromHashMap(i.used);if(o!==null){if(s!==null){for(const u of l)d.removeSelectOptionByValue(s,u);i.selector||(t=s.options[0].value,s.options[0].selected=!0,this.updateUnique(s,n,e),this.handleChangedField(s,this.objectGroup+"["+e+"]"))}for(const u of l)d.removeSelectOptionByValue(s,u);typeof i.used.length<"u"&&(i.used={}),i.used[e]={table:i.elTable,uid:t}}if(n!==null&&d.selectOptionValueExists(o,t)){const u=g.trimExplode(",",n.value);for(const y of u)s=this.querySelector('[name="data['+i.table+"]["+y+"]["+i.field+']"]'),s!==null&&y!==e&&d.removeSelectOptionByValue(s,t)}}}else i.type==="groupdb"&&(i.used[e]={table:i.elTable,uid:t});i.selector==="select"&&d.selectOptionValueExists(o,t)&&(d.removeSelectOptionByValue(o,t),i.used[e]={table:i.elTable,uid:t})}updateUnique(e,t,o){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const i=TYPO3.settings.FormEngineInline.unique[this.objectGroup],n=i.used[o];if(i.selector==="select"){const s=this.querySelector(p`[id="${this.objectGroup}_selector"]`);d.removeSelectOptionByValue(s,e.value),typeof n<"u"&&d.reAddSelectOption(s,n,i)}if(i.selector&&i.max===-1||!i||t===null)return;const r=g.trimExplode(",",t.value);let a;for(const s of r)a=this.querySelector('[name="data['+i.table+"]["+s+"]["+i.field+']"]'),a!==null&&a!==e&&(d.removeSelectOptionByValue(a,e.value),typeof n<"u"&&d.reAddSelectOption(a,n,i));i.used[o]=e.value}revertUnique(e){if(!this.hasObjectGroupDefinedUniqueConstraints())return;const t=TYPO3.settings.FormEngineInline.unique[this.objectGroup],o=this.objectGroup+m.structureSeparator+e,i=this.getRecordContainer(o),n=i.querySelector('[name="data['+t.table+"]["+i.dataset.objectUid+"]["+t.field+']"]');if(t.type==="select"){let r;if(n!==null)r=n.value;else if(i.dataset.tableUniqueOriginalValue!=="")r=i.dataset.tableUniqueOriginalValue;else return;if(t.selector==="select"&&!isNaN(parseInt(r,10))){const u=this.querySelector(p`[id="${this.objectGroup}_selector"]`);d.reAddSelectOption(u,r,t)}if(t.selector&&t.max===-1)return;const a=this.getFormFieldForElements();if(a===null)return;const s=g.trimExplode(",",a.value);let l;for(let u=0;u<s.length;u++)l=this.querySelector('[name="data['+t.table+"]["+s[u]+"]["+t.field+']"]'),l!==null&&d.reAddSelectOption(l,r,t);delete t.used[e]}else t.type==="groupdb"&&delete t.used[e]}hasObjectGroupDefinedUniqueConstraints(){return typeof TYPO3.settings.FormEngineInline.unique<"u"&&typeof TYPO3.settings.FormEngineInline.unique[this.objectGroup]<"u"}handleChangedField(e,t){let o;e instanceof HTMLSelectElement?o=e.options[e.selectedIndex].text:o=e.value,this.querySelector(p`[id="${t}_label"]`).textContent=o.length?o:this.noTitleString}}window.customElements.define("typo3-formengine-container-inline",d);
|