Commit 6053476c authored by Oliver Bartsch's avatar Oliver Bartsch
Browse files

[FEATURE] Introduce entry points for TCA type "group"

To improve the workflow for editors while selecting records
or folders in TCA type "group" fields, a new field configuration
"entryPoints" is introduced. It can be used to define
the default page / folder, which should be selected when
opening the element browser. This can be configured for
all or for each table individually.

This configuration might be quite useful since TYPO3
systems usually use dedicated storage pages for record
types nowadays.

To further support site administrators, the new configuration
also allows the usage of the known TCA markers and additionally
is also added to FormEngine's "allowOverrideMatrix".

Resolves: #91077
Releases: main
Change-Id: Ie6e4e8675ff07288480bce69467805409defdc57
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72911

Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Jochen's avatarJochen <rothjochen@gmail.com>
Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Jochen's avatarJochen <rothjochen@gmail.com>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
parent 45ae6943
......@@ -110,11 +110,12 @@ export = (function() {
*
* @param {string} mode can be "db" or "file"
* @param {string} params additional params for the browser window
* @param {string} entryPoint the entry point, which should be expanded by default
*/
FormEngine.openPopupWindow = function(mode: string, params: string): JQuery {
FormEngine.openPopupWindow = function(mode: string, params: string, entryPoint: string): JQuery {
return Modal.advanced({
type: Modal.types.iframe,
content: FormEngine.browserUrl + '&mode=' + mode + '&bparams=' + params,
content: FormEngine.browserUrl + '&mode=' + mode + '&bparams=' + params + (entryPoint ? ('&' + (mode === 'db' ? 'expandPage' : 'expandFolder') + '=' + entryPoint) : ''),
size: Modal.sizes.large
});
};
......@@ -405,8 +406,9 @@ export = (function() {
const $me = $(e.currentTarget);
const mode = $me.data('mode');
const params = $me.data('params');
const entryPoint = $me.data('entryPoint');
FormEngine.openPopupWindow(mode, params);
FormEngine.openPopupWindow(mode, params, entryPoint);
}).on('click', '[data-formengine-field-change-event="click"]', (evt: Event) => {
const items = JSON.parse((evt.currentTarget as HTMLElement).dataset.formengineFieldChangeItems);
FormEngine.processOnFieldChange(items, evt);
......
......@@ -19,6 +19,8 @@ namespace TYPO3\CMS\Backend\Form\FieldControl;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Backend\Form\InlineStackProcessor;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -76,14 +78,80 @@ class ElementBrowser extends AbstractNode
// Remove any white-spaces from the allowed extension lists
$elementBrowserAllowed = implode(',', GeneralUtility::trimExplode(',', $allowed, true));
// Initialize link attributes
$linkAttributes = [
'class' => 't3js-element-browser',
'data-mode' => htmlspecialchars($elementBrowserType),
'data-params' => htmlspecialchars($elementName . '|||' . $elementBrowserAllowed . '|' . $objectPrefix),
];
// Add the default entry point - if found
$linkAttributes = $this->addEntryPoint($table, $fieldName, $config, $linkAttributes);
return [
'iconIdentifier' => 'actions-insert-record',
'title' => $title,
'linkAttributes' => [
'class' => 't3js-element-browser',
'data-mode' => htmlspecialchars($elementBrowserType),
'data-params' => htmlspecialchars($elementName . '|||' . $elementBrowserAllowed . '|' . $objectPrefix),
],
'linkAttributes' => $linkAttributes,
];
}
/**
* Try to resolve a configured default entry point - page / folder
* to be expanded - and add it to the link attributes if found.
*/
protected function addEntryPoint(string $table, string $fieldName, array $fieldConfig, array $linkAttributes): array
{
if (!isset($fieldConfig['entryPoints']) || !is_array($fieldConfig['entryPoints'])) {
// Early return in case no entry points are defined
return $linkAttributes;
}
// Fetch the configured default entry point (which might be a marker)
$entryPoint = (string)($fieldConfig['entryPoints']['_default'] ?? '');
// In case no default entry point is given, check if we deal with type=db and only one allowed table
if ($entryPoint === '') {
if (($fieldConfig['internal_type'] ?? '') === 'folder') {
// Return for internal type folder as this requires the "_default" key to be set
return $linkAttributes;
}
// Check for the allowed tables, if only one table is allowed check if an entry point is defined for it
$allowed = GeneralUtility::trimExplode(',', $fieldConfig['allowed'] ?? '', true);
if (count($allowed) === 1 && isset($fieldConfig['entryPoints'][$allowed[0]])) {
// Use the entry point for the single table as default
$entryPoint = (string)$fieldConfig['entryPoints'][$allowed[0]];
}
if ($entryPoint === '') {
// Return if still empty
return $linkAttributes;
}
}
// Check and resolve possible marker
if (str_starts_with($entryPoint, '###') && str_ends_with($entryPoint, '###')) {
if ($entryPoint === '###CURRENT_PID###') {
// Use the current pid
$entryPoint = (string)$this->data['effectivePid'];
} elseif ($entryPoint === '###SITEROOT###' && ($this->data['site'] ?? null) instanceof Site) {
// Use the root page id from the current site
$entryPoint = (string)$this->data['site']->getRootPageId();
} else {
// Check for special TSconfig marker
$TSconfig = BackendUtility::getTCEFORM_TSconfig($table, ['pid' => $this->data['effectivePid']]);
$keyword = substr($entryPoint, 3, -3);
if (str_starts_with($keyword, 'PAGE_TSCONFIG_')) {
$entryPoint = (string)($TSconfig[$fieldName][$keyword] ?? '');
} else {
$entryPoint = (string)($TSconfig['_' . $keyword] ?? '');
}
}
}
// Add the entry point to the link attribute - if resolved
if ($entryPoint !== '') {
$linkAttributes['data-entry-point'] = htmlspecialchars($entryPoint);
}
return $linkAttributes;
}
}
......@@ -18,9 +18,11 @@ declare(strict_types=1);
namespace TYPO3\CMS\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -58,7 +60,7 @@ class TableList extends AbstractNode
$allowedTablesHtml[] = htmlspecialchars($label);
$allowedTablesHtml[] = '</span>';
} else {
$label = $languageService->sL($GLOBALS['TCA'][$tableName]['ctrl']['title']);
$label = $languageService->sL($GLOBALS['TCA'][$tableName]['ctrl']['title'] ?? '');
$icon = $iconFactory->getIconForRecord($tableName, [], Icon::SIZE_SMALL)->render();
if ((bool)($config['fieldControl']['elementBrowser']['disabled'] ?? false)) {
$allowedTablesHtml[] = '<span class="tablelist-item-nolink">';
......@@ -66,10 +68,20 @@ class TableList extends AbstractNode
$allowedTablesHtml[] = htmlspecialchars($label);
$allowedTablesHtml[] = '</span>';
} else {
$allowedTablesHtml[] = '<a href="#" class="btn btn-default t3js-element-browser" data-mode="db" data-params="' . htmlspecialchars($itemName . '|||' . $tableName) . '">';
// Initialize attributes
$attributes = [
'class' => 'btn btn-default t3js-element-browser',
'data-mode' => 'db',
'data-params' => $itemName . '|||' . $tableName,
];
// Add the entry point - if found
$attributes = $this->addEntryPoint($tableName, $config, $attributes);
$allowedTablesHtml[] = '<button ' . GeneralUtility::implodeAttributes($attributes, true) . '>';
$allowedTablesHtml[] = $icon;
$allowedTablesHtml[] = htmlspecialchars($label);
$allowedTablesHtml[] = '</a>';
$allowedTablesHtml[] = '</button>';
}
}
}
......@@ -83,6 +95,53 @@ class TableList extends AbstractNode
return $result;
}
/**
* Try to resolve a configured default entry point - page / folder
* to be expanded - and add it to the attributes if found.
*/
protected function addEntryPoint(string $tableName, array $fieldConfig, array $attributes): array
{
if (!isset($fieldConfig['entryPoints']) || !is_array($fieldConfig['entryPoints'])) {
// Early return in case no entry points are defined
return $attributes;
}
// Fetch the configured value (which might be a marker) - falls back to _default
$entryPoint = (string)($fieldConfig['entryPoints'][$tableName] ?? $fieldConfig['entryPoints']['_default'] ?? '');
if ($entryPoint === '') {
// In case no entry point exists for the given table and also no default is defined, return
return $attributes;
}
// Check and resolve possible marker
if (str_starts_with($entryPoint, '###') && str_ends_with($entryPoint, '###')) {
if ($entryPoint === '###CURRENT_PID###') {
// Use the current pid
$entryPoint = (string)$this->data['effectivePid'];
} elseif ($entryPoint === '###SITEROOT###' && ($this->data['site'] ?? null) instanceof Site) {
// Use the root page id from the current site
$entryPoint = (string)$this->data['site']->getRootPageId();
} else {
// Check for special TSconfig marker
$TSconfig = BackendUtility::getTCEFORM_TSconfig($this->data['tableName'], ['pid' => $this->data['effectivePid']]);
$keyword = substr($entryPoint, 3, -3);
if (str_starts_with($keyword, 'PAGE_TSCONFIG_')) {
$entryPoint = (string)($TSconfig[$this->data['fieldName']][$keyword] ?? '');
} else {
$entryPoint = (string)($TSconfig['_' . $keyword] ?? '');
}
}
}
// Add the entry point to the attribute - if resolved
if ($entryPoint !== '') {
$attributes['data-entry-point'] = $entryPoint;
}
return $attributes;
}
/**
* @return LanguageService
*/
......
......@@ -48,7 +48,7 @@ class FormEngineUtility
'check' => ['cols', 'readOnly'],
'select' => ['size', 'autoSizeMax', 'maxitems', 'minitems', 'readOnly', 'treeConfig', 'fileFolderConfig'],
'category' => ['size', 'maxitems', 'minitems', 'readOnly', 'treeConfig'],
'group' => ['size', 'autoSizeMax', 'max_size', 'maxitems', 'minitems', 'readOnly'],
'group' => ['size', 'autoSizeMax', 'max_size', 'maxitems', 'minitems', 'readOnly', 'entryPoints'],
'inline' => ['appearance', 'behaviour', 'foreign_label', 'foreign_selector', 'foreign_unique', 'maxitems', 'minitems', 'size', 'autoSizeMax', 'symmetric_label', 'readOnly'],
'imageManipulation' => ['ratios', 'cropVariants'],
];
......
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,n,a){void 0===a&&(a=n),Object.defineProperty(e,a,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,a){void 0===a&&(a=n),e[a]=t[n]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&__createBinding(t,e,n);return __setModuleDefault(t,e),t},__importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Backend/FormEngineValidation","TYPO3/CMS/Backend/DocumentSaveActions","TYPO3/CMS/Backend/Icons","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Utility/MessageUtility","TYPO3/CMS/Backend/Severity","TYPO3/CMS/Backend/BackendException","TYPO3/CMS/Backend/Event/InteractionRequestMap"],(function(e,t,n,a,o,i,r,l,c,d,s){"use strict";return n=__importDefault(n),l=__importStar(l),d=__importStar(d),function(){function t(e,t){t?f.interactionRequestMap.resolveFor(e):f.interactionRequestMap.rejectFor(e)}const u=new Map;u.set("typo3-backend-form-update-value",(e,t)=>{const n=document.querySelector('[name="'+CSS.escape(e.elementName)+'"]'),o=document.querySelector('[data-formengine-input-name="'+CSS.escape(e.elementName)+'"]');a.updateInputField(e.elementName),null!==n&&(a.markFieldAsChanged(n),a.validateField(n)),null!==o&&o!==n&&a.validateField(o)}),u.set("typo3-backend-form-reload",(e,t)=>{e.confirm?r.confirm(TYPO3.lang["FormEngine.refreshRequiredTitle"],TYPO3.lang["FormEngine.refreshRequiredContent"]).on("button.clicked",e=>{"ok"==e.target.name&&f.saveDocument(),r.dismiss()}):f.saveDocument()}),u.set("typo3-backend-form-update-bitmask",(e,t)=>{const n=t.target,a=document.editform[e.elementName],o=n.checked!==e.invert,i=Math.pow(2,e.position),r=Math.pow(2,e.total)-i-1;a.value=o?a.value|i:a.value&r,a.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))});const f={consumeTypes:["typo3.setUrl","typo3.beforeSetUrl","typo3.refresh"],Validation:a,interactionRequestMap:s,formName:TYPO3.settings.FormEngine.formName,openedPopupWindow:null,legacyFieldChangedCb:function(){!n.default.isFunction(TYPO3.settings.FormEngine.legacyFieldChangedCb)||TYPO3.settings.FormEngine.legacyFieldChangedCb()},browserUrl:"",openPopupWindow:function(e,t){return r.advanced({type:r.types.iframe,content:f.browserUrl+"&mode="+e+"&bparams="+t,size:r.sizes.large})},setSelectOptionFromExternalSource:function(e,t,o,i,r,l){r=String(r);let c,d,s=!1,u=!1;if(c=f.getFieldElement(e),d=c.get(0),null===d||"--div--"===t||d instanceof HTMLOptGroupElement)return;const m=f.getFieldElement(e,"_list",!0);if(m.length>0&&(c=m,s=c.prop("multiple")&&"1"!=c.prop("size"),u=!0),s||u){const u=f.getFieldElement(e,"_avail");if(s||(c.find("option").each((e,t)=>{const a=u.find('option[value="'+n.default.escapeSelector((0,n.default)(t).attr("value"))+'"]');a&&(a.removeClass("hidden").prop("disabled",!1),f.enableOptGroup(a.get(0)))}),c.empty()),r){let e=!1,n=new RegExp("(^|,)"+t+"($|,)");r.match(n)?(c.empty(),e=!0):1==c.find("option").length&&(n=new RegExp("(^|,)"+c.find("option").prop("value")+"($|,)"),r.match(n)&&(c.empty(),e=!0)),e&&void 0!==l&&l.closest("select").querySelectorAll("[disabled]").forEach((function(e){e.classList.remove("hidden"),e.disabled=!1,f.enableOptGroup(e)}))}let m=!0;const p=f.getFieldElement(e,"_mul",!0);if((0==p.length||0==p.val())&&(c.find("option").each((function(e,a){if((0,n.default)(a).prop("value")==t)return m=!1,!1})),m&&void 0!==l)){l.classList.add("hidden"),l.disabled=!0;const e=l.parentElement;e instanceof HTMLOptGroupElement&&0===e.querySelectorAll("option:not([disabled]):not([hidden]):not(.hidden)").length&&(e.disabled=!0,e.classList.add("hidden"))}if(m){const e=(0,n.default)("<option></option>");e.attr({value:t,title:i}).text(o),e.appendTo(c),f.updateHiddenFieldValueFromSelect(c,d),f.legacyFieldChangedCb(),a.markFieldAsChanged(d),f.Validation.validateField(c),f.Validation.validateField(u)}}else{const e=/_(\d+)$/,n=t.toString().match(e);null!=n&&(t=n[1]),c.val(t),f.Validation.validateField(c)}},updateHiddenFieldValueFromSelect:function(e,t){const a=[];(0,n.default)(e).find("option").each((e,t)=>{a.push((0,n.default)(t).prop("value"))}),t.value=a.join(","),t.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},getFormElement:function(e){const t=(0,n.default)('form[name="'+f.formName+'"]:first');if(!e)return t;{const n=f.getFieldElement(e),a=f.getFieldElement(e,"_list");if(n.length>0&&("select-one"===n.prop("type")||a.length>0&&a.prop("type").match(/select-(one|multiple)/)))return t;console.error("Form fields missing: form: "+f.formName+", field name: "+e),alert("Form field is invalid")}},getFieldElement:function(e,t,a){const o=(0,n.default)('form[name="'+f.formName+'"]:first');if(t){let i;switch(t){case"_list":i=(0,n.default)(':input[data-formengine-input-name="'+e+'"]:not([type=hidden])',o);break;case"_avail":i=(0,n.default)(':input[data-relatedfieldname="'+e+'"]',o);break;case"_mul":case"_hr":i=(0,n.default)(':input[type=hidden][data-formengine-input-name="'+e+'"]',o);break;default:i=null}if(i&&i.length>0||!0===a)return i}return(0,n.default)(':input[name="'+e+'"]',o)},initializeEvents:function(){top.TYPO3&&void 0!==top.TYPO3.Backend&&(top.TYPO3.Backend.consumerScope.attach(f),(0,n.default)(window).on("unload",(function(){top.TYPO3.Backend.consumerScope.detach(f)}))),(0,n.default)(document).on("click",".t3js-editform-close",e=>{e.preventDefault(),f.preventExitIfNotSaved(f.preventExitIfNotSavedCallback)}).on("click",".t3js-editform-view",e=>{e.preventDefault(),f.previewAction(e,f.previewActionCallback)}).on("click",".t3js-editform-new",e=>{e.preventDefault(),f.newAction(e,f.newActionCallback)}).on("click",".t3js-editform-duplicate",e=>{e.preventDefault(),f.duplicateAction(e,f.duplicateActionCallback)}).on("click",".t3js-editform-delete-record",e=>{e.preventDefault(),f.deleteAction(e,f.deleteActionCallback)}).on("click",".t3js-editform-submitButton",e=>{const t=(0,n.default)(e.currentTarget),a=t.data("name")||e.currentTarget.name,o=(0,n.default)("<input />").attr("type","hidden").attr("name",a).attr("value","1");t.parents("form").append(o)}).on("change",'.t3-form-field-eval-null-checkbox input[type="checkbox"]',e=>{(0,n.default)(e.currentTarget).closest(".t3js-formengine-field-item").toggleClass("disabled")}).on("change",'.t3js-form-field-eval-null-placeholder-checkbox input[type="checkbox"]',e=>{f.toggleCheckboxField((0,n.default)(e.currentTarget)),a.markFieldAsChanged((0,n.default)(e.currentTarget))}).on("change",(function(e){(0,n.default)(".module-docheader-bar .btn").removeClass("disabled").prop("disabled",!1)})).on("click",".t3js-element-browser",(function(e){e.preventDefault(),e.stopPropagation();const t=(0,n.default)(e.currentTarget),a=t.data("mode"),o=t.data("params");f.openPopupWindow(a,o)})).on("click",'[data-formengine-field-change-event="click"]',e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);f.processOnFieldChange(t,e)}).on("change",'[data-formengine-field-change-event="change"]',e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);f.processOnFieldChange(t,e)}),document.editform.addEventListener("submit",(function(){if(document.editform.closeDoc.value)return;const e=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(","),t=document.querySelector(e);null!==t&&(t.disabled=!0,i.getIcon("spinner-circle-dark",i.sizes.small).then((function(e){t.querySelector(".t3js-icon").outerHTML=e})))})),window.addEventListener("message",f.handlePostMessage)},consume:function(e){if(!e)throw new d.BackendException("No interaction request given",1496589980);const a=n.default.Deferred();if(e.concernsTypes(f.consumeTypes)){const n=e.outerMostRequest;f.interactionRequestMap.attachFor(n,a),n.isProcessed()?t(n,n.getProcessedData().response):f.hasChange()?f.preventExitIfNotSaved((function(e){n.setProcessedData({response:e}),t(n,e)})):f.interactionRequestMap.resolveFor(n)}return a},handlePostMessage:function(e){if(!l.MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";const t=e.data.label||e.data.value,n=e.data.title||t,a=e.data.exclusiveValues||"";f.setSelectOptionFromExternalSource(e.data.fieldName,e.data.value,t,n,a)}},initializeRemainingCharacterViews:function(){const e=(0,n.default)("[maxlength]").not(".t3js-datetimepicker").not(".t3js-charcounter-initialized");e.on("focus",e=>{const t=(0,n.default)(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=f.getCharacterCounterProperties(t);a.append((0,n.default)("<div />",{class:"t3js-charcounter"}).append((0,n.default)("<span />",{class:o.labelClass}).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))))}).on("blur",e=>{(0,n.default)(e.currentTarget).parents(".t3js-formengine-field-item:first").find(".t3js-charcounter").remove()}).on("keyup",e=>{const t=(0,n.default)(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=f.getCharacterCounterProperties(t);a.find(".t3js-charcounter span").removeClass().addClass(o.labelClass).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))}),e.addClass("t3js-charcounter-initialized"),(0,n.default)(":password").on("focus",(function(e){(0,n.default)(e.currentTarget).attr({type:"text","data-active-password":"true"}).trigger("select")})).on("blur",(function(e){(0,n.default)(e.currentTarget).attr("type","password").removeAttr("data-active-password")}))},getCharacterCounterProperties:function(e){const t=e.val(),n=e.attr("maxlength")-t.length-(t.match(/\n/g)||[]).length;let a="";return a=n<15?"label-danger":n<30?"label-warning":"label-info",{remainingCharacters:n,labelClass:"label "+a}},initializeNullNoPlaceholderCheckboxes:function(){(0,n.default)(".t3-form-field-eval-null-checkbox").each((function(e,t){const a=(0,n.default)(t),o=a.find('input[type="checkbox"]'),i=a.closest(".t3js-formengine-field-item");o.attr("checked")||i.addClass("disabled")}))},initializeNullWithPlaceholderCheckboxes:function(){(0,n.default)(".t3js-form-field-eval-null-placeholder-checkbox").each((e,t)=>{f.toggleCheckboxField((0,n.default)(t).find('input[type="checkbox"]'))})},toggleCheckboxField:function(e){const t=e.closest(".t3js-formengine-field-item");e.prop("checked")?(t.find(".t3js-formengine-placeholder-placeholder").hide(),t.find(".t3js-formengine-placeholder-formfield").show(),t.find(".t3js-formengine-placeholder-formfield").find(":input").trigger("focus")):(t.find(".t3js-formengine-placeholder-placeholder").show(),t.find(".t3js-formengine-placeholder-formfield").hide())},reinitialize:function(){const t=Array.from(document.querySelectorAll(".t3js-clearable")).filter(e=>!e.classList.contains("t3js-color-picker"));t.length>0&&e(["TYPO3/CMS/Backend/Input/Clearable"],(function(){t.forEach(e=>e.clearable())})),f.initializeNullNoPlaceholderCheckboxes(),f.initializeNullWithPlaceholderCheckboxes(),f.initializeLocalizationStateSelector(),f.initializeRemainingCharacterViews()},initializeLocalizationStateSelector:function(){(0,n.default)(".t3js-l10n-state-container").each((e,t)=>{const a=(0,n.default)(t),o=a.closest(".t3js-formengine-field-item").find("[data-formengine-input-name]"),i=a.find('input[type="radio"]:checked').val();"parent"!==i&&"source"!==i||o.attr("disabled","disabled")})},hasChange:function(){const e=(0,n.default)('form[name="'+f.formName+'"] .has-change').length>0,t=(0,n.default)('[name^="data["].has-change').length>0;return e||t},preventExitIfNotSavedCallback:function(e){f.closeDocument()},preventFollowLinkIfNotSaved:function(e){return f.preventExitIfNotSaved((function(){window.location.href=e})),!1},preventExitIfNotSaved:function(e){if(e=e||f.preventExitIfNotSavedCallback,f.hasChange()){const t=TYPO3.lang["label.confirm.close_without_save.title"]||"Do you want to close without saving?",a=TYPO3.lang["label.confirm.close_without_save.content"]||"You currently have unsaved changes. Are you sure you want to discard these changes?",o=(0,n.default)("<input />").attr("type","hidden").attr("name","_saveandclosedok").attr("value","1"),i=[{text:TYPO3.lang["buttons.confirm.close_without_save.no"]||"No, I will continue editing",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.close_without_save.yes"]||"Yes, discard my changes",btnClass:"btn-default",name:"yes"}];0===(0,n.default)(".has-error").length&&i.push({text:TYPO3.lang["buttons.confirm.save_and_close"]||"Save and close",btnClass:"btn-warning",name:"save",active:!0});r.confirm(t,a,c.warning,i).on("button.clicked",(function(t){"no"===t.target.name?r.dismiss():"yes"===t.target.name?(r.dismiss(),e.call(null,!0)):"save"===t.target.name&&((0,n.default)("form[name="+f.formName+"]").append(o),r.dismiss(),f.saveDocument())}))}else e.call(null,!0)},preventSaveIfHasErrors:function(){if((0,n.default)(".has-error").length>0){const e=TYPO3.lang["label.alert.save_with_error.title"]||"You have errors in your form!",t=TYPO3.lang["label.alert.save_with_error.content"]||"Please check the form, there is at least one error in your form.";return r.confirm(e,t,c.error,[{text:TYPO3.lang["buttons.alert.save_with_error.ok"]||"OK",btnClass:"btn-danger",name:"ok"}]).on("button.clicked",(function(e){"ok"===e.target.name&&r.dismiss()})),!1}return!0},requestFormEngineUpdate:function(e){if(e){r.confirm(TYPO3.lang["FormEngine.refreshRequiredTitle"],TYPO3.lang["FormEngine.refreshRequiredContent"]).on("button.clicked",(function(e){"ok"===e.target.name?(f.closeModalsRecursive(),f.saveDocument()):r.dismiss()}))}else f.saveDocument()},processOnFieldChange:function(e,t){e.forEach(e=>{const n=u.get(e.name);n instanceof Function&&n.call(null,e.data||null,t)})},registerOnFieldChangeHandler:function(e,t){u.has(e)&&console.warn("Handler for onFieldChange name `"+e+"` has been overridden."),u.set(e,t)},closeModalsRecursive:function(){void 0!==r.currentModal&&null!==r.currentModal&&(r.currentModal.on("hidden.bs.modal",(function(){f.closeModalsRecursive(r.currentModal)})),r.currentModal.trigger("modal-dismiss"))},previewAction:function(e,t){t=t||f.previewActionCallback;const a=e.target.href,o=e.target.dataset.hasOwnProperty("isNew"),i=(0,n.default)("<input />").attr("type","hidden").attr("name","_savedokview").attr("value","1");f.hasChange()?f.showPreviewModal(a,o,i,t):((0,n.default)("form[name="+f.formName+"]").append(i),window.open("","newTYPO3frontendWindow"),document.editform.submit())},previewActionCallback:function(e,t,a){switch(r.dismiss(),e){case"discard":const e=window.open(t,"newTYPO3frontendWindow");e.focus(),e.location.href===t&&e.location.reload();break;case"save":(0,n.default)("form[name="+f.formName+"]").append(a),window.open("","newTYPO3frontendWindow"),f.saveDocument()}},showPreviewModal:function(e,t,n,a){const o=TYPO3.lang["label.confirm.view_record_changed.title"]||"Do you want to save before viewing?",i={text:TYPO3.lang["buttons.confirm.view_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},l={text:TYPO3.lang["buttons.confirm.view_record_changed.no-save"]||"View without changes",btnClass:"btn-info",name:"discard"},d={text:TYPO3.lang["buttons.confirm.view_record_changed.save"]||"Save changes and view",btnClass:"btn-info",name:"save",active:!0};let s=[],u="";t?(s=[i,d],u=TYPO3.lang["label.confirm.view_record_changed.content.is-new-page"]||"You need to save your changes before viewing the page. Do you want to save and view them now?"):(s=[i,l,d],u=TYPO3.lang["label.confirm.view_record_changed.content"]||"You currently have unsaved changes. You can either discard these changes or save and view them.");const f=r.confirm(o,u,c.info,s);f.on("button.clicked",(function(t){a(t.target.name,e,n,f)}))},newAction:function(e,t){t=t||f.newActionCallback;const a=(0,n.default)("<input />").attr("type","hidden").attr("name","_savedoknew").attr("value","1"),o=e.target.dataset.hasOwnProperty("isNew");f.hasChange()?f.showNewModal(o,a,t):((0,n.default)("form[name="+f.formName+"]").append(a),document.editform.submit())},newActionCallback:function(e,t){const a=(0,n.default)("form[name="+f.formName+"]");switch(r.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),f.saveDocument()}},showNewModal:function(e,t,n){const a=TYPO3.lang["label.confirm.new_record_changed.title"]||"Do you want to save before adding?",o=TYPO3.lang["label.confirm.new_record_changed.content"]||"You need to save your changes before creating a new record. Do you want to save and create now?";let i=[];const l={text:TYPO3.lang["buttons.confirm.new_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},d={text:TYPO3.lang["buttons.confirm.new_record_changed.no"]||"No, just add",btnClass:"btn-default",name:"no"},s={text:TYPO3.lang["buttons.confirm.new_record_changed.yes"]||"Yes, save and create now",btnClass:"btn-info",name:"yes",active:!0};i=e?[l,s]:[l,d,s];r.confirm(a,o,c.info,i).on("button.clicked",(function(e){n(e.target.name,t)}))},duplicateAction:function(e,t){t=t||f.duplicateActionCallback;const a=(0,n.default)("<input />").attr("type","hidden").attr("name","_duplicatedoc").attr("value","1"),o=e.target.dataset.hasOwnProperty("isNew");f.hasChange()?f.showDuplicateModal(o,a,t):((0,n.default)("form[name="+f.formName+"]").append(a),document.editform.submit())},duplicateActionCallback:function(e,t){const a=(0,n.default)("form[name="+f.formName+"]");switch(r.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),f.saveDocument()}},showDuplicateModal:function(e,t,n){const a=TYPO3.lang["label.confirm.duplicate_record_changed.title"]||"Do you want to save before duplicating this record?",o=TYPO3.lang["label.confirm.duplicate_record_changed.content"]||"You currently have unsaved changes. Do you want to save your changes before duplicating this record?";let i=[];const l={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},d={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.no"]||"No, just duplicate the original",btnClass:"btn-default",name:"no"},s={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.yes"]||"Yes, save and duplicate this record",btnClass:"btn-info",name:"yes",active:!0};i=e?[l,s]:[l,d,s];r.confirm(a,o,c.info,i).on("button.clicked",(function(e){n(e.target.name,t)}))},deleteAction:function(e,t){t=t||f.deleteActionCallback;const a=(0,n.default)(e.target);f.showDeleteModal(a,t)},deleteActionCallback:function(e,t){r.dismiss(),"yes"===e&&f.invokeRecordDeletion(t)},showDeleteModal:function(e,t){const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?";let a=TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete this record?";e.data("reference-count-message")&&(a+=" "+e.data("reference-count-message")),e.data("translation-count-message")&&(a+=" "+e.data("translation-count-message"));r.confirm(n,a,c.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",active:!0}]).on("button.clicked",(function(n){t(n.target.name,e)}))},enableOptGroup:function(e){const t=e.parentElement;t instanceof HTMLOptGroupElement&&t.querySelectorAll("option:not([hidden]):not([disabled]):not(.hidden)").length&&(t.hidden=!1,t.disabled=!1,t.classList.remove("hidden"))},closeDocument:function(){document.editform.closeDoc.value=1,f.dispatchSubmitEvent(),document.editform.submit()},saveDocument:function(){document.editform.doSave.value=1,f.dispatchSubmitEvent(),document.editform.submit()},dispatchSubmitEvent:function(){const e=document.createEvent("Event");e.initEvent("submit",!1,!0),document.editform.dispatchEvent(e)},initialize:function(e,t){o.getInstance().addPreSubmitCallback((function(){(0,n.default)('[data-active-password]:not([type="password"])').each((function(e,t){t.setAttribute("type","password"),t.blur()}))})),f.browserUrl=e,f.Validation.setUsMode(t),(0,n.default)((function(){f.initializeEvents(),f.Validation.initialize(),f.reinitialize(),(0,n.default)("#t3js-ui-block").remove()}))},invokeRecordDeletion:function(e){window.location.href=e.attr("href")}};return void 0!==TYPO3.settings.RequireJS&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"]&&n.default.each(TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"],(function(t,n){e([n])})),TYPO3.FormEngine=f,f}()}));
\ No newline at end of file
var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,n,a){void 0===a&&(a=n),Object.defineProperty(e,a,{enumerable:!0,get:function(){return t[n]}})}:function(e,t,n,a){void 0===a&&(a=n),e[a]=t[n]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&__createBinding(t,e,n);return __setModuleDefault(t,e),t},__importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Backend/FormEngineValidation","TYPO3/CMS/Backend/DocumentSaveActions","TYPO3/CMS/Backend/Icons","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Utility/MessageUtility","TYPO3/CMS/Backend/Severity","TYPO3/CMS/Backend/BackendException","TYPO3/CMS/Backend/Event/InteractionRequestMap"],(function(e,t,n,a,o,i,r,l,c,d,s){"use strict";return n=__importDefault(n),l=__importStar(l),d=__importStar(d),function(){function t(e,t){t?f.interactionRequestMap.resolveFor(e):f.interactionRequestMap.rejectFor(e)}const u=new Map;u.set("typo3-backend-form-update-value",(e,t)=>{const n=document.querySelector('[name="'+CSS.escape(e.elementName)+'"]'),o=document.querySelector('[data-formengine-input-name="'+CSS.escape(e.elementName)+'"]');a.updateInputField(e.elementName),null!==n&&(a.markFieldAsChanged(n),a.validateField(n)),null!==o&&o!==n&&a.validateField(o)}),u.set("typo3-backend-form-reload",(e,t)=>{e.confirm?r.confirm(TYPO3.lang["FormEngine.refreshRequiredTitle"],TYPO3.lang["FormEngine.refreshRequiredContent"]).on("button.clicked",e=>{"ok"==e.target.name&&f.saveDocument(),r.dismiss()}):f.saveDocument()}),u.set("typo3-backend-form-update-bitmask",(e,t)=>{const n=t.target,a=document.editform[e.elementName],o=n.checked!==e.invert,i=Math.pow(2,e.position),r=Math.pow(2,e.total)-i-1;a.value=o?a.value|i:a.value&r,a.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))});const f={consumeTypes:["typo3.setUrl","typo3.beforeSetUrl","typo3.refresh"],Validation:a,interactionRequestMap:s,formName:TYPO3.settings.FormEngine.formName,openedPopupWindow:null,legacyFieldChangedCb:function(){!n.default.isFunction(TYPO3.settings.FormEngine.legacyFieldChangedCb)||TYPO3.settings.FormEngine.legacyFieldChangedCb()},browserUrl:"",openPopupWindow:function(e,t,n){return r.advanced({type:r.types.iframe,content:f.browserUrl+"&mode="+e+"&bparams="+t+(n?"&"+("db"===e?"expandPage":"expandFolder")+"="+n:""),size:r.sizes.large})},setSelectOptionFromExternalSource:function(e,t,o,i,r,l){r=String(r);let c,d,s=!1,u=!1;if(c=f.getFieldElement(e),d=c.get(0),null===d||"--div--"===t||d instanceof HTMLOptGroupElement)return;const m=f.getFieldElement(e,"_list",!0);if(m.length>0&&(c=m,s=c.prop("multiple")&&"1"!=c.prop("size"),u=!0),s||u){const u=f.getFieldElement(e,"_avail");if(s||(c.find("option").each((e,t)=>{const a=u.find('option[value="'+n.default.escapeSelector((0,n.default)(t).attr("value"))+'"]');a&&(a.removeClass("hidden").prop("disabled",!1),f.enableOptGroup(a.get(0)))}),c.empty()),r){let e=!1,n=new RegExp("(^|,)"+t+"($|,)");r.match(n)?(c.empty(),e=!0):1==c.find("option").length&&(n=new RegExp("(^|,)"+c.find("option").prop("value")+"($|,)"),r.match(n)&&(c.empty(),e=!0)),e&&void 0!==l&&l.closest("select").querySelectorAll("[disabled]").forEach((function(e){e.classList.remove("hidden"),e.disabled=!1,f.enableOptGroup(e)}))}let m=!0;const p=f.getFieldElement(e,"_mul",!0);if((0==p.length||0==p.val())&&(c.find("option").each((function(e,a){if((0,n.default)(a).prop("value")==t)return m=!1,!1})),m&&void 0!==l)){l.classList.add("hidden"),l.disabled=!0;const e=l.parentElement;e instanceof HTMLOptGroupElement&&0===e.querySelectorAll("option:not([disabled]):not([hidden]):not(.hidden)").length&&(e.disabled=!0,e.classList.add("hidden"))}if(m){const e=(0,n.default)("<option></option>");e.attr({value:t,title:i}).text(o),e.appendTo(c),f.updateHiddenFieldValueFromSelect(c,d),f.legacyFieldChangedCb(),a.markFieldAsChanged(d),f.Validation.validateField(c),f.Validation.validateField(u)}}else{const e=/_(\d+)$/,n=t.toString().match(e);null!=n&&(t=n[1]),c.val(t),f.Validation.validateField(c)}},updateHiddenFieldValueFromSelect:function(e,t){const a=[];(0,n.default)(e).find("option").each((e,t)=>{a.push((0,n.default)(t).prop("value"))}),t.value=a.join(","),t.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))},getFormElement:function(e){const t=(0,n.default)('form[name="'+f.formName+'"]:first');if(!e)return t;{const n=f.getFieldElement(e),a=f.getFieldElement(e,"_list");if(n.length>0&&("select-one"===n.prop("type")||a.length>0&&a.prop("type").match(/select-(one|multiple)/)))return t;console.error("Form fields missing: form: "+f.formName+", field name: "+e),alert("Form field is invalid")}},getFieldElement:function(e,t,a){const o=(0,n.default)('form[name="'+f.formName+'"]:first');if(t){let i;switch(t){case"_list":i=(0,n.default)(':input[data-formengine-input-name="'+e+'"]:not([type=hidden])',o);break;case"_avail":i=(0,n.default)(':input[data-relatedfieldname="'+e+'"]',o);break;case"_mul":case"_hr":i=(0,n.default)(':input[type=hidden][data-formengine-input-name="'+e+'"]',o);break;default:i=null}if(i&&i.length>0||!0===a)return i}return(0,n.default)(':input[name="'+e+'"]',o)},initializeEvents:function(){top.TYPO3&&void 0!==top.TYPO3.Backend&&(top.TYPO3.Backend.consumerScope.attach(f),(0,n.default)(window).on("unload",(function(){top.TYPO3.Backend.consumerScope.detach(f)}))),(0,n.default)(document).on("click",".t3js-editform-close",e=>{e.preventDefault(),f.preventExitIfNotSaved(f.preventExitIfNotSavedCallback)}).on("click",".t3js-editform-view",e=>{e.preventDefault(),f.previewAction(e,f.previewActionCallback)}).on("click",".t3js-editform-new",e=>{e.preventDefault(),f.newAction(e,f.newActionCallback)}).on("click",".t3js-editform-duplicate",e=>{e.preventDefault(),f.duplicateAction(e,f.duplicateActionCallback)}).on("click",".t3js-editform-delete-record",e=>{e.preventDefault(),f.deleteAction(e,f.deleteActionCallback)}).on("click",".t3js-editform-submitButton",e=>{const t=(0,n.default)(e.currentTarget),a=t.data("name")||e.currentTarget.name,o=(0,n.default)("<input />").attr("type","hidden").attr("name",a).attr("value","1");t.parents("form").append(o)}).on("change",'.t3-form-field-eval-null-checkbox input[type="checkbox"]',e=>{(0,n.default)(e.currentTarget).closest(".t3js-formengine-field-item").toggleClass("disabled")}).on("change",'.t3js-form-field-eval-null-placeholder-checkbox input[type="checkbox"]',e=>{f.toggleCheckboxField((0,n.default)(e.currentTarget)),a.markFieldAsChanged((0,n.default)(e.currentTarget))}).on("change",(function(e){(0,n.default)(".module-docheader-bar .btn").removeClass("disabled").prop("disabled",!1)})).on("click",".t3js-element-browser",(function(e){e.preventDefault(),e.stopPropagation();const t=(0,n.default)(e.currentTarget),a=t.data("mode"),o=t.data("params"),i=t.data("entryPoint");f.openPopupWindow(a,o,i)})).on("click",'[data-formengine-field-change-event="click"]',e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);f.processOnFieldChange(t,e)}).on("change",'[data-formengine-field-change-event="change"]',e=>{const t=JSON.parse(e.currentTarget.dataset.formengineFieldChangeItems);f.processOnFieldChange(t,e)}),document.editform.addEventListener("submit",(function(){if(document.editform.closeDoc.value)return;const e=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(","),t=document.querySelector(e);null!==t&&(t.disabled=!0,i.getIcon("spinner-circle-dark",i.sizes.small).then((function(e){t.querySelector(".t3js-icon").outerHTML=e})))})),window.addEventListener("message",f.handlePostMessage)},consume:function(e){if(!e)throw new d.BackendException("No interaction request given",1496589980);const a=n.default.Deferred();if(e.concernsTypes(f.consumeTypes)){const n=e.outerMostRequest;f.interactionRequestMap.attachFor(n,a),n.isProcessed()?t(n,n.getProcessedData().response):f.hasChange()?f.preventExitIfNotSaved((function(e){n.setProcessedData({response:e}),t(n,e)})):f.interactionRequestMap.resolveFor(n)}return a},handlePostMessage:function(e){if(!l.MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";const t=e.data.label||e.data.value,n=e.data.title||t,a=e.data.exclusiveValues||"";f.setSelectOptionFromExternalSource(e.data.fieldName,e.data.value,t,n,a)}},initializeRemainingCharacterViews:function(){const e=(0,n.default)("[maxlength]").not(".t3js-datetimepicker").not(".t3js-charcounter-initialized");e.on("focus",e=>{const t=(0,n.default)(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=f.getCharacterCounterProperties(t);a.append((0,n.default)("<div />",{class:"t3js-charcounter"}).append((0,n.default)("<span />",{class:o.labelClass}).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))))}).on("blur",e=>{(0,n.default)(e.currentTarget).parents(".t3js-formengine-field-item:first").find(".t3js-charcounter").remove()}).on("keyup",e=>{const t=(0,n.default)(e.currentTarget),a=t.parents(".t3js-formengine-field-item:first"),o=f.getCharacterCounterProperties(t);a.find(".t3js-charcounter span").removeClass().addClass(o.labelClass).text(TYPO3.lang["FormEngine.remainingCharacters"].replace("{0}",o.remainingCharacters))}),e.addClass("t3js-charcounter-initialized"),(0,n.default)(":password").on("focus",(function(e){(0,n.default)(e.currentTarget).attr({type:"text","data-active-password":"true"}).trigger("select")})).on("blur",(function(e){(0,n.default)(e.currentTarget).attr("type","password").removeAttr("data-active-password")}))},getCharacterCounterProperties:function(e){const t=e.val(),n=e.attr("maxlength")-t.length-(t.match(/\n/g)||[]).length;let a="";return a=n<15?"label-danger":n<30?"label-warning":"label-info",{remainingCharacters:n,labelClass:"label "+a}},initializeNullNoPlaceholderCheckboxes:function(){(0,n.default)(".t3-form-field-eval-null-checkbox").each((function(e,t){const a=(0,n.default)(t),o=a.find('input[type="checkbox"]'),i=a.closest(".t3js-formengine-field-item");o.attr("checked")||i.addClass("disabled")}))},initializeNullWithPlaceholderCheckboxes:function(){(0,n.default)(".t3js-form-field-eval-null-placeholder-checkbox").each((e,t)=>{f.toggleCheckboxField((0,n.default)(t).find('input[type="checkbox"]'))})},toggleCheckboxField:function(e){const t=e.closest(".t3js-formengine-field-item");e.prop("checked")?(t.find(".t3js-formengine-placeholder-placeholder").hide(),t.find(".t3js-formengine-placeholder-formfield").show(),t.find(".t3js-formengine-placeholder-formfield").find(":input").trigger("focus")):(t.find(".t3js-formengine-placeholder-placeholder").show(),t.find(".t3js-formengine-placeholder-formfield").hide())},reinitialize:function(){const t=Array.from(document.querySelectorAll(".t3js-clearable")).filter(e=>!e.classList.contains("t3js-color-picker"));t.length>0&&e(["TYPO3/CMS/Backend/Input/Clearable"],(function(){t.forEach(e=>e.clearable())})),f.initializeNullNoPlaceholderCheckboxes(),f.initializeNullWithPlaceholderCheckboxes(),f.initializeLocalizationStateSelector(),f.initializeRemainingCharacterViews()},initializeLocalizationStateSelector:function(){(0,n.default)(".t3js-l10n-state-container").each((e,t)=>{const a=(0,n.default)(t),o=a.closest(".t3js-formengine-field-item").find("[data-formengine-input-name]"),i=a.find('input[type="radio"]:checked').val();"parent"!==i&&"source"!==i||o.attr("disabled","disabled")})},hasChange:function(){const e=(0,n.default)('form[name="'+f.formName+'"] .has-change').length>0,t=(0,n.default)('[name^="data["].has-change').length>0;return e||t},preventExitIfNotSavedCallback:function(e){f.closeDocument()},preventFollowLinkIfNotSaved:function(e){return f.preventExitIfNotSaved((function(){window.location.href=e})),!1},preventExitIfNotSaved:function(e){if(e=e||f.preventExitIfNotSavedCallback,f.hasChange()){const t=TYPO3.lang["label.confirm.close_without_save.title"]||"Do you want to close without saving?",a=TYPO3.lang["label.confirm.close_without_save.content"]||"You currently have unsaved changes. Are you sure you want to discard these changes?",o=(0,n.default)("<input />").attr("type","hidden").attr("name","_saveandclosedok").attr("value","1"),i=[{text:TYPO3.lang["buttons.confirm.close_without_save.no"]||"No, I will continue editing",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.close_without_save.yes"]||"Yes, discard my changes",btnClass:"btn-default",name:"yes"}];0===(0,n.default)(".has-error").length&&i.push({text:TYPO3.lang["buttons.confirm.save_and_close"]||"Save and close",btnClass:"btn-warning",name:"save",active:!0});r.confirm(t,a,c.warning,i).on("button.clicked",(function(t){"no"===t.target.name?r.dismiss():"yes"===t.target.name?(r.dismiss(),e.call(null,!0)):"save"===t.target.name&&((0,n.default)("form[name="+f.formName+"]").append(o),r.dismiss(),f.saveDocument())}))}else e.call(null,!0)},preventSaveIfHasErrors:function(){if((0,n.default)(".has-error").length>0){const e=TYPO3.lang["label.alert.save_with_error.title"]||"You have errors in your form!",t=TYPO3.lang["label.alert.save_with_error.content"]||"Please check the form, there is at least one error in your form.";return r.confirm(e,t,c.error,[{text:TYPO3.lang["buttons.alert.save_with_error.ok"]||"OK",btnClass:"btn-danger",name:"ok"}]).on("button.clicked",(function(e){"ok"===e.target.name&&r.dismiss()})),!1}return!0},requestFormEngineUpdate:function(e){if(e){r.confirm(TYPO3.lang["FormEngine.refreshRequiredTitle"],TYPO3.lang["FormEngine.refreshRequiredContent"]).on("button.clicked",(function(e){"ok"===e.target.name?(f.closeModalsRecursive(),f.saveDocument()):r.dismiss()}))}else f.saveDocument()},processOnFieldChange:function(e,t){e.forEach(e=>{const n=u.get(e.name);n instanceof Function&&n.call(null,e.data||null,t)})},registerOnFieldChangeHandler:function(e,t){u.has(e)&&console.warn("Handler for onFieldChange name `"+e+"` has been overridden."),u.set(e,t)},closeModalsRecursive:function(){void 0!==r.currentModal&&null!==r.currentModal&&(r.currentModal.on("hidden.bs.modal",(function(){f.closeModalsRecursive(r.currentModal)})),r.currentModal.trigger("modal-dismiss"))},previewAction:function(e,t){t=t||f.previewActionCallback;const a=e.target.href,o=e.target.dataset.hasOwnProperty("isNew"),i=(0,n.default)("<input />").attr("type","hidden").attr("name","_savedokview").attr("value","1");f.hasChange()?f.showPreviewModal(a,o,i,t):((0,n.default)("form[name="+f.formName+"]").append(i),window.open("","newTYPO3frontendWindow"),document.editform.submit())},previewActionCallback:function(e,t,a){switch(r.dismiss(),e){case"discard":const e=window.open(t,"newTYPO3frontendWindow");e.focus(),e.location.href===t&&e.location.reload();break;case"save":(0,n.default)("form[name="+f.formName+"]").append(a),window.open("","newTYPO3frontendWindow"),f.saveDocument()}},showPreviewModal:function(e,t,n,a){const o=TYPO3.lang["label.confirm.view_record_changed.title"]||"Do you want to save before viewing?",i={text:TYPO3.lang["buttons.confirm.view_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},l={text:TYPO3.lang["buttons.confirm.view_record_changed.no-save"]||"View without changes",btnClass:"btn-info",name:"discard"},d={text:TYPO3.lang["buttons.confirm.view_record_changed.save"]||"Save changes and view",btnClass:"btn-info",name:"save",active:!0};let s=[],u="";t?(s=[i,d],u=TYPO3.lang["label.confirm.view_record_changed.content.is-new-page"]||"You need to save your changes before viewing the page. Do you want to save and view them now?"):(s=[i,l,d],u=TYPO3.lang["label.confirm.view_record_changed.content"]||"You currently have unsaved changes. You can either discard these changes or save and view them.");const f=r.confirm(o,u,c.info,s);f.on("button.clicked",(function(t){a(t.target.name,e,n,f)}))},newAction:function(e,t){t=t||f.newActionCallback;const a=(0,n.default)("<input />").attr("type","hidden").attr("name","_savedoknew").attr("value","1"),o=e.target.dataset.hasOwnProperty("isNew");f.hasChange()?f.showNewModal(o,a,t):((0,n.default)("form[name="+f.formName+"]").append(a),document.editform.submit())},newActionCallback:function(e,t){const a=(0,n.default)("form[name="+f.formName+"]");switch(r.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),f.saveDocument()}},showNewModal:function(e,t,n){const a=TYPO3.lang["label.confirm.new_record_changed.title"]||"Do you want to save before adding?",o=TYPO3.lang["label.confirm.new_record_changed.content"]||"You need to save your changes before creating a new record. Do you want to save and create now?";let i=[];const l={text:TYPO3.lang["buttons.confirm.new_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},d={text:TYPO3.lang["buttons.confirm.new_record_changed.no"]||"No, just add",btnClass:"btn-default",name:"no"},s={text:TYPO3.lang["buttons.confirm.new_record_changed.yes"]||"Yes, save and create now",btnClass:"btn-info",name:"yes",active:!0};i=e?[l,s]:[l,d,s];r.confirm(a,o,c.info,i).on("button.clicked",(function(e){n(e.target.name,t)}))},duplicateAction:function(e,t){t=t||f.duplicateActionCallback;const a=(0,n.default)("<input />").attr("type","hidden").attr("name","_duplicatedoc").attr("value","1"),o=e.target.dataset.hasOwnProperty("isNew");f.hasChange()?f.showDuplicateModal(o,a,t):((0,n.default)("form[name="+f.formName+"]").append(a),document.editform.submit())},duplicateActionCallback:function(e,t){const a=(0,n.default)("form[name="+f.formName+"]");switch(r.dismiss(),e){case"no":a.append(t),document.editform.submit();break;case"yes":a.append(t),f.saveDocument()}},showDuplicateModal:function(e,t,n){const a=TYPO3.lang["label.confirm.duplicate_record_changed.title"]||"Do you want to save before duplicating this record?",o=TYPO3.lang["label.confirm.duplicate_record_changed.content"]||"You currently have unsaved changes. Do you want to save your changes before duplicating this record?";let i=[];const l={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.cancel"]||"Cancel",btnClass:"btn-default",name:"cancel"},d={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.no"]||"No, just duplicate the original",btnClass:"btn-default",name:"no"},s={text:TYPO3.lang["buttons.confirm.duplicate_record_changed.yes"]||"Yes, save and duplicate this record",btnClass:"btn-info",name:"yes",active:!0};i=e?[l,s]:[l,d,s];r.confirm(a,o,c.info,i).on("button.clicked",(function(e){n(e.target.name,t)}))},deleteAction:function(e,t){t=t||f.deleteActionCallback;const a=(0,n.default)(e.target);f.showDeleteModal(a,t)},deleteActionCallback:function(e,t){r.dismiss(),"yes"===e&&f.invokeRecordDeletion(t)},showDeleteModal:function(e,t){const n=TYPO3.lang["label.confirm.delete_record.title"]||"Delete this record?";let a=TYPO3.lang["label.confirm.delete_record.content"]||"Are you sure you want to delete this record?";e.data("reference-count-message")&&(a+=" "+e.data("reference-count-message")),e.data("translation-count-message")&&(a+=" "+e.data("translation-count-message"));r.confirm(n,a,c.warning,[{text:TYPO3.lang["buttons.confirm.delete_record.no"]||"Cancel",btnClass:"btn-default",name:"no"},{text:TYPO3.lang["buttons.confirm.delete_record.yes"]||"Yes, delete this record",btnClass:"btn-warning",name:"yes",active:!0}]).on("button.clicked",(function(n){t(n.target.name,e)}))},enableOptGroup:function(e){const t=e.parentElement;t instanceof HTMLOptGroupElement&&t.querySelectorAll("option:not([hidden]):not([disabled]):not(.hidden)").length&&(t.hidden=!1,t.disabled=!1,t.classList.remove("hidden"))},closeDocument:function(){document.editform.closeDoc.value=1,f.dispatchSubmitEvent(),document.editform.submit()},saveDocument:function(){document.editform.doSave.value=1,f.dispatchSubmitEvent(),document.editform.submit()},dispatchSubmitEvent:function(){const e=document.createEvent("Event");e.initEvent("submit",!1,!0),document.editform.dispatchEvent(e)},initialize:function(e,t){o.getInstance().addPreSubmitCallback((function(){(0,n.default)('[data-active-password]:not([type="password"])').each((function(e,t){t.setAttribute("type","password"),t.blur()}))})),f.browserUrl=e,f.Validation.setUsMode(t),(0,n.default)((function(){f.initializeEvents(),f.Validation.initialize(),f.reinitialize(),(0,n.default)("#t3js-ui-block").remove()}))},invokeRecordDeletion:function(e){window.location.href=e.attr("href")}};return void 0!==TYPO3.settings.RequireJS&&void 0!==TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"]&&n.default.each(TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/FormEngine"],(function(t,n){e([n])})),TYPO3.FormEngine=f,f}()}));
\ No newline at end of file
......@@ -20,6 +20,7 @@ namespace TYPO3\CMS\Backend\Tests\Unit\Form\FieldControl;
use Prophecy\PhpUnit\ProphecyTrait;
use TYPO3\CMS\Backend\Form\FieldControl\ElementBrowser;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
class ElementBrowserTest extends UnitTestCase
......@@ -76,4 +77,145 @@ class ElementBrowserTest extends UnitTestCase
$result = $elementBrowser->render();
self::assertSame($result['linkAttributes']['data-params'], '|||be_users,be_groups|');
}
/**
* @test
* @dataProvider renderResolvesEntryPointDataProvider
*/
public function renderResolvesEntryPoint(array $config, string $expected): void
{
$nodeFactory = $this->prophesize(NodeFactory::class);
$elementBrowser = new ElementBrowser($nodeFactory->reveal(), [
'fieldName' => 'somefield',
'isInlineChild' => false,
'effectivePid' => 123,
'site' => new Site('some-site', 123, []),
'tableName' => 'tt_content',
'inlineStructure' => [],
'parameterArray' => [
'itemFormElName' => '',
'fieldConf' => [
'config' => $config,
],
],
]);
$result = $elementBrowser->render();
self::assertEquals($expected, $result['linkAttributes']['data-entry-point'] ?? '');
}
public function renderResolvesEntryPointDataProvider(): \Generator
{
yield 'Wildcard' => [
[
'allowed' => '*',
'entryPoints' => [
'_default' => 123,
],
],
'123',
];
yield 'One table' => [
[
'allowed' => 'pages',
'entryPoints' => [
'pages' => 123,
],
],
'123',
];
yield 'One table with default' => [
[
'allowed' => 'pages',
'entryPoints' => [
'_default' => 123,
],
],
'123',
];
yield 'One table with default and table definition' => [
[
'allowed' => 'pages',
'entryPoints' => [
'_default' => 123,
'pages' => 124,
],
],
'123',
];
yield 'One table with invalid configuration' => [
[
'allowed' => 'pages',
'entryPoints' => [
'some_table' => 123,
],
],
'',
];
yield 'Two tables without _defualt' => [
[
'allowed' => 'pages,some_table',
'entryPoints' => [
'pages' => 123,
'some_table' => 124,
],
],
'',
];
yield 'Two tables with _defualt' => [
[
'allowed' => 'pages,some_table',
'entryPoints' => [
'_default' => 123,
'pages' => 124,
'some_table' => 125,
],
],
'123',
];
yield 'Folder' => [
[
'internal_type' => 'folder',
'entryPoints' => [
'_default' => '1:/storage/',
],
],
'1:/storage/',
];
yield 'Folder without mandatory _default' => [
[
'internal_type' => 'folder',
'entryPoints' => [
'file' => 123,
],
],
'',
];
yield 'Entry point is escaped' => [
[
'internal_type' => 'folder',
'entryPoints' => [
'_default' => '1:/<script>alert(1)</script>/',
],
],
'1:/&lt;script&gt;alert(1)&lt;/script&gt;/',
];
yield 'Pid placeholder is resolved' => [
[
'allowed' => '*',
'entryPoints' => [
'_default' => '###CURRENT_PID###',
],
],
'123',
];
yield 'Site placeholder is resolved' => [
[
'allowed' => '*',
'entryPoints' => [
'_default' => '###SITEROOT###',
],