From 9ee97e4a8c993738c4e17d53a9191be4ec34ad84 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 4 Jan 2022 23:55:35 -0500 Subject: [PATCH 01/12] Quick Create/Edit Window Refinements --- .../default/sass/_breakpoint-medium.scss | 47 + .../default/sass/_breakpoint-small.scss | 20 + .../default/sass/_colors-and-vars.scss | 3 +- _build/templates/default/sass/_forms.scss | 4 +- _build/templates/default/sass/_windows.scss | 12 +- _build/templates/default/sass/index.scss | 3 + core/lexicon/en/chunk.inc.php | 13 +- core/lexicon/en/default.inc.php | 32 + core/lexicon/en/plugin.inc.php | 17 +- core/lexicon/en/snippet.inc.php | 13 +- core/lexicon/en/template.inc.php | 14 +- core/lexicon/en/tv.inc.php | 5 +- .../assets/modext/widgets/core/modx.window.js | 9 + .../modext/widgets/element/modx.panel.tv.js | 3 +- manager/assets/modext/widgets/windows.js | 974 ++++++++++++++---- 15 files changed, 957 insertions(+), 212 deletions(-) create mode 100644 _build/templates/default/sass/_breakpoint-medium.scss create mode 100644 _build/templates/default/sass/_breakpoint-small.scss diff --git a/_build/templates/default/sass/_breakpoint-medium.scss b/_build/templates/default/sass/_breakpoint-medium.scss new file mode 100644 index 00000000000..904e7d8c1b3 --- /dev/null +++ b/_build/templates/default/sass/_breakpoint-medium.scss @@ -0,0 +1,47 @@ +/* Medium screens, including small desktops and tablets */ + +// Breakpoint for up to 1024px +@include grid-media($tabletM) { + +} + +// Breakpoint for up to 960px +@include grid-media($desktop) { + .x-window { + form { + .x-column-inner { + width: 100% !important; + } + .x-panel-body { + width: 100% !important; + } + } + .x-window-bc { + .x-window-footer { + padding: 15px; + } + } + &.qce-create, + &.qce-update { + .x-toolbar-cell { + margin-bottom: .75rem; + display: inline-block; + margin-right: 2%; + &:last-child { + margin-right: 0; + } + } + } + &.qce-create { + .x-toolbar-cell { + width: 49%; + } + } + &.qce-update { + .x-toolbar-cell { + width: 32%; + } + } + } + +} diff --git a/_build/templates/default/sass/_breakpoint-small.scss b/_build/templates/default/sass/_breakpoint-small.scss new file mode 100644 index 00000000000..f1d6f73ec7a --- /dev/null +++ b/_build/templates/default/sass/_breakpoint-small.scss @@ -0,0 +1,20 @@ +// Breakpoint for up to tablet (portrait orientation) size 768px +@include grid-media($tabletP) { + +} + +// Breakpoint for mobile size +@include grid-media($mobile) { + + .x-window { + &.qce-create, + &.qce-update { + .x-toolbar-cell { + display: block; + margin-right: 0; + width: 100%; + } + } + } + +} diff --git a/_build/templates/default/sass/_colors-and-vars.scss b/_build/templates/default/sass/_colors-and-vars.scss index fa3d6ad513f..92212d45672 100644 --- a/_build/templates/default/sass/_colors-and-vars.scss +++ b/_build/templates/default/sass/_colors-and-vars.scss @@ -3,6 +3,7 @@ $colorSplash: #234368; $colorSplashLight: lighten($colorSplash, 50%); $colorSplashMedium: lighten($colorSplash, 75%); $colorSplashDark: darken($colorSplash, 20%); +$colorSplashShadow: scale-color($colorSplash, $lightness: -70%, $saturation: -25%); $colorSplashContrast: #FFFFFF; // needs much more adaption, should be used as text color for elements with $colorSplash background $silver: #CCCCCC; $gallery: #EEEEEE; @@ -64,7 +65,7 @@ $borderRadius: 3px; // Shadows $boxShadow: 0 4px 6px rgba(0, 0, 0, 0.15); -$boxShadowBig: 0 0 15px 0 rgba(0,0,0,0.2); +$boxShadowBig: 0 0 15px 0 rgba($black,0.25); $shadowBorder: 0 0 0 1px $borderColor; $shadowBorderField: 0 0 0 1px $borderColor; $shadowBorderDark: 0 0 0 1px $softGray; diff --git a/_build/templates/default/sass/_forms.scss b/_build/templates/default/sass/_forms.scss index 7497aaacbfb..77bd12650fe 100644 --- a/_build/templates/default/sass/_forms.scss +++ b/_build/templates/default/sass/_forms.scss @@ -916,7 +916,7 @@ input::-moz-focus-inner { .x-form-check-wrap, .x-fieldset-checkbox-toggle legend, - .x-fieldset legend { + .x-fieldset legend{ [type="checkbox"]{ position: absolute; @@ -1238,7 +1238,7 @@ input::-moz-focus-inner { } .x-form-grow-sizer { - font: $fontSmall; + font: $baseText; } .x-form-invalid-msg { diff --git a/_build/templates/default/sass/_windows.scss b/_build/templates/default/sass/_windows.scss index 2b8ff87a67e..581cfbd927c 100644 --- a/_build/templates/default/sass/_windows.scss +++ b/_build/templates/default/sass/_windows.scss @@ -268,6 +268,11 @@ } } } + &.qce-window { + .x-window-body { + padding-top: 0; + } + } } /* .x-window */ @@ -299,20 +304,19 @@ /* the window modal mask, but also the mask that covers a grid when reloading for example */ .ext-el-mask { - background-color: $white; + background-color: $colorSplashShadow; opacity: 0; - filter: alpha(opacity=0); /* for IE <= 8 */ transition: opacity .25s; /*z-index: 10;*/ /* this is handeled by extjs and set to 9000 on show */ &.fade-in { opacity: .5; - filter: alpha(opacity=50); /* for IE <= 8 */ } + // This affects the grid mask .x-masked & { + background-color: $white; opacity: .5; - filter: alpha(opacity=50); /* for IE <= 8 */ z-index: 9; /* extjs standard is 100, 10 prevents overlapping the topnav dropdowns */ } } diff --git a/_build/templates/default/sass/index.scss b/_build/templates/default/sass/index.scss index b8c635ba901..ab85c775db9 100644 --- a/_build/templates/default/sass/index.scss +++ b/_build/templates/default/sass/index.scss @@ -2562,3 +2562,6 @@ iframe[classname="x-hidden"] { margin-top: 0 !important; } } + +@import "breakpoint-medium"; +@import "breakpoint-small"; diff --git a/core/lexicon/en/chunk.inc.php b/core/lexicon/en/chunk.inc.php index 9ffddaf3127..e029e223351 100644 --- a/core/lexicon/en/chunk.inc.php +++ b/core/lexicon/en/chunk.inc.php @@ -12,8 +12,6 @@ $_lang['chunk'] = 'Chunk'; $_lang['chunk_category_desc'] = 'Use to group Chunks within the Elements tree.'; -$_lang['chunk_code'] = 'Chunk Code (HTML)'; -$_lang['chunk_description_desc'] = 'Usage information for this Chunk shown in search results and as a tooltip in the Elements tree.'; $_lang['chunk_delete_confirm'] = 'Are you sure you want to delete this chunk?'; $_lang['chunk_duplicate_confirm'] = 'Are you sure you want to duplicate this chunk?'; $_lang['chunk_err_create'] = 'An error occurred while trying to create the chunk.'; @@ -39,9 +37,18 @@ // Temporarily match old keys to new ones to ensure compatibility // --fields $_lang['chunk_desc_category'] = $_lang['chunk_category_desc']; -$_lang['chunk_desc_description'] = $_lang['chunk_description_desc']; $_lang['chunk_desc_name'] = $_lang['chunk_name_desc']; $_lang['chunk_lock_msg'] = $_lang['chunk_lock_desc']; // --tabs $_lang['chunk_msg'] = $_lang['chunk_tab_general_desc']; + +/* + Refer to default.inc.php for the keys below. + (Placement in this default file necessary to allow + quick create/edit panels access to them when opened + outside the context of their respective element types) + + chunk_code + chunk_description_desc +*/ diff --git a/core/lexicon/en/default.inc.php b/core/lexicon/en/default.inc.php index 58145332f7f..ccffe760e42 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -570,9 +570,41 @@ access when they are opened outside the context of their respective element types */ + // All +// Chunks +$_lang['chunk_code'] = 'Chunk Code (HTML)'; +$_lang['chunk_description_desc'] = 'Usage information for this Chunk shown in search results and as a tooltip in the Elements tree.'; + // Temporarily match old keys to new ones to ensure compatibility + $_lang['chunk_desc_description'] = $_lang['chunk_description_desc']; + +// Plugins +$_lang['plugin_code'] = 'Plugin Code (PHP)'; +$_lang['plugin_description_desc'] = 'Usage information for this Plugin shown in search results and as a tooltip in the Elements tree.'; +$_lang['plugin_disabled'] = 'Deactivate Plugin'; +$_lang['plugin_disabled_desc'] = 'When deactivated, this Plugin will not respond to events.'; + // Temporarily match old keys to new ones to ensure compatibility + $_lang['plugin_desc'] = $_lang['description']; + $_lang['plugin_desc_description'] = $_lang['plugin_description_desc']; + $_lang['plugin_disabled_msg'] = $_lang['plugin_disabled_desc']; + +// Snippets +$_lang['snippet_code'] = 'Snippet Code (PHP)'; +$_lang['snippet_description_desc'] = 'Usage information for this Snippet shown in search results and as a tooltip in the Elements tree.'; + // Temporarily match old keys to new ones to ensure compatibility + $_lang['snippet_desc'] = $_lang['description']; + $_lang['snippet_desc_description'] = $_lang['snippet_description_desc']; + +// Templates +$_lang['template_code'] = 'Template Code (HTML)'; +$_lang['template_description_desc'] = 'Usage information for this Template shown in search results and as a tooltip in the Elements tree.'; + // Temporarily match old keys to new ones to ensure compatibility + $_lang['template_desc'] = $_lang['description']; + $_lang['template_desc_description'] = $_lang['template_description_desc']; + // TVs +$_lang['tv_tab_input_options'] = 'Input Options'; $_lang['tv_type'] = 'Input Type'; $_lang['tv_default'] = 'Default Value'; $_lang['tv_default_desc'] = 'The content this TV will show if user-entered content is not provided.'; diff --git a/core/lexicon/en/plugin.inc.php b/core/lexicon/en/plugin.inc.php index a153e8841c9..e2eb313fd5e 100644 --- a/core/lexicon/en/plugin.inc.php +++ b/core/lexicon/en/plugin.inc.php @@ -11,12 +11,8 @@ $_lang['plugin'] = 'Plugin'; $_lang['plugin_add'] = 'Add Plugin'; $_lang['plugin_category_desc'] = 'Use to group Plugins within the Elements tree.'; -$_lang['plugin_code'] = 'Plugin Code (PHP)'; $_lang['plugin_config'] = 'Plugin configuration'; -$_lang['plugin_description_desc'] = 'Usage information for this Plugin shown in search results and as a tooltip in the Elements tree.'; $_lang['plugin_delete_confirm'] = 'Are you sure you want to delete this plugin?'; -$_lang['plugin_disabled'] = 'Deactivate Plugin'; -$_lang['plugin_disabled_msg'] = 'When deactivated, this Plugin will not respond to events.'; $_lang['plugin_duplicate_confirm'] = 'Are you sure you want to duplicate this plugin?'; $_lang['plugin_err_create'] = 'An error occurred while creating the plugin.'; $_lang['plugin_err_ae'] = 'A plugin already exists with the name "[[+name]]".'; @@ -48,9 +44,20 @@ // Temporarily match old keys to new ones to ensure compatibility // --fields $_lang['plugin_desc_category'] = $_lang['plugin_category_desc']; -$_lang['plugin_desc_description'] = $_lang['plugin_description_desc']; $_lang['plugin_desc_name'] = $_lang['plugin_name_desc']; $_lang['plugin_lock_msg'] = $_lang['plugin_lock_desc']; // --tabs $_lang['plugin_msg'] = $_lang['plugin_tab_general_desc']; + +/* + Refer to default.inc.php for the keys below. + (Placement in this default file necessary to allow + quick create/edit panels access to them when opened + outside the context of their respective element types) + + plugin_code + plugin_description_desc + plugin_disabled + plugin_disabled_desc +*/ diff --git a/core/lexicon/en/snippet.inc.php b/core/lexicon/en/snippet.inc.php index c06eb0cf1a1..b43c29009f2 100644 --- a/core/lexicon/en/snippet.inc.php +++ b/core/lexicon/en/snippet.inc.php @@ -10,9 +10,7 @@ $_lang['snippet'] = 'Snippet'; $_lang['snippets_available'] = 'Snippets available for you to include in your page'; $_lang['snippet_category_desc'] = 'Use to group Snippets within the Elements tree.'; -$_lang['snippet_code'] = 'Snippet Code (PHP)'; $_lang['snippet_delete_confirm'] = 'Are you sure you want to delete this snippet?'; -$_lang['snippet_description_desc'] = 'Usage information for this Snippet shown in search results and as a tooltip in the Elements tree.'; $_lang['snippet_duplicate_confirm'] = 'Are you sure you want to duplicate this snippet?'; $_lang['snippet_duplicate_error'] = 'An error occurred while duplicating the snippet.'; $_lang['snippet_err_create'] = 'An error occurred while creating the snippet.'; @@ -40,9 +38,18 @@ // Temporarily match old keys to new ones to ensure compatibility // --fields $_lang['snippet_desc_category'] = $_lang['snippet_category_desc']; -$_lang['snippet_desc_description'] = $_lang['snippet_description_desc']; $_lang['snippet_desc_name'] = $_lang['snippet_name_desc']; $_lang['snippet_lock_msg'] = $_lang['snippet_lock_desc']; // --tabs $_lang['snippet_msg'] = $_lang['snippet_tab_general_desc']; + +/* + Refer to default.inc.php for the keys below. + (Placement in this default file necessary to allow + quick create/edit panels access to them when opened + outside the context of their respective element types) + + snippet_code + snippet_description_desc +*/ diff --git a/core/lexicon/en/template.inc.php b/core/lexicon/en/template.inc.php index 738b49ea204..ce68978dc62 100644 --- a/core/lexicon/en/template.inc.php +++ b/core/lexicon/en/template.inc.php @@ -13,7 +13,6 @@ $_lang['template'] = 'Template'; $_lang['template_assignedtv_tab'] = 'Assigned TVs'; $_lang['template_category_desc'] = 'Use to group Templates within the Elements tree.'; -$_lang['template_code'] = 'Template Code (HTML)'; $_lang['template_delete_confirm'] = 'Are you sure you want to delete this template?'; $_lang['template_description_desc'] = 'Usage information for this Template shown in search results and as a tooltip in the Elements tree.'; $_lang['template_duplicate_confirm'] = 'Are you sure you want to duplicate this template?'; @@ -57,9 +56,20 @@ // Temporarily match old keys to new ones to ensure compatibility // --fields $_lang['template_desc_category'] = $_lang['template_category_desc']; -$_lang['template_desc_description'] = $_lang['template_description_desc']; $_lang['template_desc_name'] = $_lang['template_name_desc']; +$_lang['template_icon_description'] = $_lang['template_icon_desc']; $_lang['template_lock_msg'] = $_lang['template_lock_desc']; +$_lang['template_preview_description'] = $_lang['template_preview_desc']; // --tabs $_lang['template_msg'] = $_lang['template_tab_general_desc']; + +/* + Refer to default.inc.php for the keys below. + (Placement in this default file necessary to allow + quick create/edit panels access to them when opened + outside the context of their respective element types) + + template_code + template_description_desc + */ diff --git a/core/lexicon/en/tv.inc.php b/core/lexicon/en/tv.inc.php index f668a4d0a20..dca75b3e888 100644 --- a/core/lexicon/en/tv.inc.php +++ b/core/lexicon/en/tv.inc.php @@ -38,7 +38,7 @@ $_lang['tv_err_save'] = 'An error occurred while saving the TV.'; $_lang['tv_inuse'] = 'The following document(s) are currently using this TV. To continue with the delete operation click the Delete button otherwise click the Cancel button.'; $_lang['tv_inuse_template'] = 'The following template(s) are currently using this TV: [[+templates]].

Please detach the TV from the template(s) before deleting it.'; -$_lang['is_static_tv_desc'] = 'Use an external file to store the default value for this TV. This may be useful if the default value’s content is particularly lengthy.'; +$_lang['tv_isstatic_desc'] = 'Use an external file to store the default value for this TV. This may be useful if the default value’s content is particularly lengthy.'; $_lang['tv_lock'] = 'Restrict Editing'; $_lang['tv_lock_desc'] = 'Only users with “edit_locked” permissions can edit this TV.'; $_lang['tv_management_msg'] = 'Manage additional custom TVs for your documents.'; @@ -51,7 +51,6 @@ $_lang['tv_reset_params'] = 'Reset parameters'; $_lang['tv_tab_access_desc'] = 'Select the Resource Groups that this TV belongs to. Only users with access to the Groups selected will be able to modify this TV. If no Groups are selected, all users with access to the Manager will be able to modify the TV.'; $_lang['tv_tab_general_desc'] = 'Here you can enter the basic attributes for this Template Variable (TV). Note that TVs must be assigned to templates in order to access them from snippets and documents.'; -$_lang['tv_tab_input_options'] = 'Input Options'; $_lang['tv_tab_input_options_desc'] = '

Here you can edit the input options for the TV, specific to the type of input render that you select.

'; $_lang['tv_tab_output_options'] = 'Output Options'; $_lang['tv_tab_output_options_desc'] = '

Here you can edit the output options for the TV, specific to the type of output render that you select.

'; @@ -70,6 +69,7 @@ // Temporarily match old keys to new ones to ensure compatibility // -- fields +$_lang['is_static_tv_desc'] = $_lang['tv_isstatic_desc']; $_lang['tv_desc_caption'] = $_lang['tv_caption_desc']; $_lang['tv_desc_category'] = $_lang['tv_category_desc']; $_lang['tv_desc_description'] = $_lang['tv_description_desc']; @@ -94,6 +94,7 @@ quick create/edit panels access to them when opened outside the context of their respective element types) + tv_tab_input_options tv_caption_desc tv_category_desc tv_description_desc diff --git a/manager/assets/modext/widgets/core/modx.window.js b/manager/assets/modext/widgets/core/modx.window.js index db98205d901..b501cfb7b3d 100644 --- a/manager/assets/modext/widgets/core/modx.window.js +++ b/manager/assets/modext/widgets/core/modx.window.js @@ -114,6 +114,7 @@ Ext.override(Ext.Window, { */ MODx.Window = function(config) { config = config || {}; + this.isSmallScreen = Ext.getBody().getViewSize().height <= 768; Ext.applyIf(config,{ modal: false ,layout: 'auto' @@ -300,6 +301,14 @@ Ext.extend(MODx.Window,Ext.Window,{ return false; } + /* @smg6511: + Suggest moving away from using this bulk setValues method and + explicitly specifying each field’s value param in window configs, + as is done for standard form panel pages. This will already have been done + for the element quick create/edit windows. Also the above value-setting + procedure in the _loadForm method could be dropped too. All windows in + windows.js would need to be updated before dropping. + */ ,setValues: function(r) { if (r === null) { return false; } this.fp.getForm().setValues(r); diff --git a/manager/assets/modext/widgets/element/modx.panel.tv.js b/manager/assets/modext/widgets/element/modx.panel.tv.js index 52cd8ded14b..982bf010c00 100644 --- a/manager/assets/modext/widgets/element/modx.panel.tv.js +++ b/manager/assets/modext/widgets/element/modx.panel.tv.js @@ -1093,7 +1093,8 @@ Ext.extend(MODx.panel.TVInputProperties,MODx.Panel,{ ,id: 'modx-tv-elements' ,itemId: 'fld-elements' ,grow: true - ,maxHeight: 160 + ,growMin: 30 + ,growMax: 200 ,value: value ,plugins: new AddFieldUtilities.plugin.Class }; diff --git a/manager/assets/modext/widgets/windows.js b/manager/assets/modext/widgets/windows.js index f49a7511fbc..8308c8d9c9b 100644 --- a/manager/assets/modext/widgets/windows.js +++ b/manager/assets/modext/widgets/windows.js @@ -228,7 +228,6 @@ Ext.extend(MODx.window.DuplicateElement,MODx.Window, { } } }); - Ext.reg('modx-window-element-duplicate',MODx.window.DuplicateElement); MODx.window.CreateCategory = function(config) { @@ -383,46 +382,152 @@ Ext.reg('modx-window-namespace-update',MODx.window.UpdateNamespace); MODx.window.QuickCreateChunk = function(config) { config = config || {}; + this.ident = config.ident || `qcechunk${Ext.id()}`; Ext.applyIf(config,{ title: _('quick_create_chunk') - ,width: 600 - ,layout: 'anchor' + ,width: 700 + ,layout: 'form' ,url: MODx.config.connector_url ,action: 'Element/Chunk/Create' + ,cls: 'qce-window qce-create' + ,modal: true + ,monitorResize: true ,fields: [{ xtype: 'hidden' ,name: 'id' + ,value: config.record.id || 0 },{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,anchor: '100%' - },{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,anchor: '100%' - },{ - xtype: 'textarea' - ,name: 'description' - ,fieldLabel: _('description') - ,anchor: '100%' + // row 1 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textfield' + ,name: 'name' + ,fieldLabel: _('name') + ,allowBlank: false + ,maxLength: 50 + ,value: config.record.name || '' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'modx-combo-category' + ,name: 'category' + ,id: `modx-${this.ident}-category` + ,fieldLabel: _('category') + ,description: MODx.expandHelp ? '' : _('chunk_category_desc') + ,value: config.record.category || 0 + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-category` + ,html: _('chunk_category_desc') + ,cls: 'desc-under' + }] + }] + }] },{ - xtype: 'textarea' - ,name: 'snippet' - ,fieldLabel: _('code') - ,anchor: '100%' - ,grow: true - ,growMax: 216 + // row 2 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,name: 'description' + ,id: `modx-${this.ident}-description` + ,description: MODx.expandHelp ? '' : _('chunk_description_desc') + ,fieldLabel: _('description') + ,grow: true + ,growMin: 50 + ,growMax: this.isSmallScreen ? 90 : 120 + ,value: config.record.description || '' + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-description` + ,html: _('chunk_description_desc') + ,cls: 'desc-under' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'xcheckbox' + ,name: 'clearCache' + ,id: `modx-${this.ident}-clear-cache` + ,hideLabel: true + ,boxLabel: _('clear_cache_on_save') + ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') + ,ctCls: 'add-label-space' + ,inputValue: 1 + ,checked: true + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-clear-cache` + ,html: _('clear_cache_on_save_desc') + ,cls: 'desc-under toggle-slider-above' + }] + }] + }] },{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: _('clear_cache_on_save_msg') - ,inputValue: 1 - ,checked: true + // row 3 + cls:'form-row-wrapper', + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,fieldLabel: _('chunk_code') + ,name: 'snippet' + ,id: `modx-${this.ident}-code` + ,grow: true + ,growMin: 90 + ,growMax: this.isSmallScreen ? 160 : 300 + ,value: config.record.snippet || '' + }] }] ,keys: [{ key: Ext.EventObject.ENTER @@ -442,6 +547,7 @@ MODx.window.QuickUpdateChunk = function(config) { Ext.applyIf(config,{ title: _('quick_update_chunk') ,action: 'Element/Chunk/Update' + ,cls: 'qce-window qce-update' ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this @@ -462,48 +568,155 @@ MODx.window.QuickUpdateChunk = function(config) { Ext.extend(MODx.window.QuickUpdateChunk, MODx.window.QuickCreateChunk); Ext.reg('modx-window-quick-update-chunk',MODx.window.QuickUpdateChunk); + MODx.window.QuickCreateTemplate = function(config) { config = config || {}; + this.ident = config.ident || `qcetemplate${Ext.id()}`; Ext.applyIf(config,{ title: _('quick_create_template') - ,width: 600 + ,width: 700 ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Element/Template/Create' + ,cls: 'qce-window qce-create' + ,modal: true + ,monitorResize: true ,fields: [{ xtype: 'hidden' ,name: 'id' + ,value: config.record.id || 0 },{ - xtype: 'textfield' - ,name: 'templatename' - ,fieldLabel: _('name') - ,anchor: '100%' - },{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,anchor: '100%' - },{ - xtype: 'textarea' - ,name: 'description' - ,fieldLabel: _('description') - ,anchor: '100%' + // row 1 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textfield' + ,name: 'templatename' + ,fieldLabel: _('name') + ,allowBlank: false + ,maxLength: 50 + ,value: config.record.templatename || '' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'modx-combo-category' + ,name: 'category' + ,id: `modx-${this.ident}-category` + ,fieldLabel: _('category') + ,description: MODx.expandHelp ? '' : _('template_category_desc') + ,value: config.record.category || 0 + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-category` + ,html: _('template_category_desc') + ,cls: 'desc-under' + }] + }] + }] },{ - xtype: 'textarea' - ,name: 'content' - ,fieldLabel: _('code') - ,anchor: '100%' - ,grow: true - ,growMax: 216 + // row 2 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,name: 'description' + ,id: `modx-${this.ident}-description` + ,description: MODx.expandHelp ? '' : _('template_description_desc') + ,fieldLabel: _('description') + ,grow: true + ,growMin: 50 + ,growMax: this.isSmallScreen ? 90 : 120 + ,value: config.record.description || '' + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-description` + ,html: _('template_description_desc') + ,cls: 'desc-under' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'xcheckbox' + ,name: 'clearCache' + ,id: `modx-${this.ident}-clear-cache` + ,hideLabel: true + ,boxLabel: _('clear_cache_on_save') + ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') + ,ctCls: 'add-label-space' + ,inputValue: 1 + ,checked: true + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-clear-cache` + ,html: _('clear_cache_on_save_desc') + ,cls: 'desc-under toggle-slider-above' + }] + }] + }] },{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: _('clear_cache_on_save_msg') - ,inputValue: 1 - ,checked: true + // row 3 + cls:'form-row-wrapper', + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,fieldLabel: _('template_code') + ,name: 'content' + ,id: `modx-${this.ident}-code` + ,grow: true + ,growMin: 120 + ,growMax: this.isSmallScreen ? 160 : 300 + ,value: config.record.content || '' + }] }] ,keys: [{ key: Ext.EventObject.ENTER @@ -523,6 +736,7 @@ MODx.window.QuickUpdateTemplate = function(config) { Ext.applyIf(config,{ title: _('quick_update_template') ,action: 'Element/Template/Update' + ,cls: 'qce-window qce-update' ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this @@ -546,46 +760,152 @@ Ext.reg('modx-window-quick-update-template',MODx.window.QuickUpdateTemplate); MODx.window.QuickCreateSnippet = function(config) { config = config || {}; + this.ident = config.ident || `qcesnippet${Ext.id()}`; Ext.applyIf(config,{ title: _('quick_create_snippet') - ,width: 600 + ,width: 700 ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Element/Snippet/Create' + ,cls: 'qce-window qce-create' + ,modal: true + ,monitorResize: true ,fields: [{ xtype: 'hidden' ,name: 'id' + ,value: config.record.id || 0 },{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,anchor: '100%' - },{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,anchor: '100%' - },{ - xtype: 'textarea' - ,name: 'description' - ,fieldLabel: _('description') - ,anchor: '100%' + // row 1 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textfield' + ,name: 'name' + ,fieldLabel: _('name') + ,allowBlank: false + ,maxLength: 50 + ,value: config.record.name || '' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'modx-combo-category' + ,name: 'category' + ,id: `modx-${this.ident}-category` + ,fieldLabel: _('category') + ,description: MODx.expandHelp ? '' : _('snippet_category_desc') + ,value: config.record.category || 0 + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-category` + ,html: _('snippet_category_desc') + ,cls: 'desc-under' + }] + }] + }] },{ - xtype: 'textarea' - ,name: 'snippet' - ,fieldLabel: _('code') - ,anchor: '100%' - ,grow: true - ,growMax: 216 + // row 2 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,name: 'description' + ,id: `modx-${this.ident}-description` + ,description: MODx.expandHelp ? '' : _('snippet_description_desc') + ,fieldLabel: _('description') + ,grow: true + ,growMin: 50 + ,growMax: this.isSmallScreen ? 90 : 120 + ,value: config.record.description || '' + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-description` + ,html: _('snippet_description_desc') + ,cls: 'desc-under' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'xcheckbox' + ,name: 'clearCache' + ,id: `modx-${this.ident}-clear-cache` + ,hideLabel: true + ,boxLabel: _('clear_cache_on_save') + ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') + ,ctCls: 'add-label-space' + ,inputValue: 1 + ,checked: true + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-clear-cache` + ,html: _('clear_cache_on_save_desc') + ,cls: 'desc-under toggle-slider-above' + }] + }] + }] },{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: _('clear_cache_on_save_msg') - ,inputValue: 1 - ,checked: true + // row 3 + cls:'form-row-wrapper', + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,fieldLabel: _('snippet_code') + ,name: 'snippet' + ,id: `modx-${this.ident}-code` + ,grow: true + ,growMin: 90 + ,growMax: this.isSmallScreen ? 160 : 300 + ,value: config.record.snippet || '' + }] }] ,keys: [{ key: Ext.EventObject.ENTER @@ -605,6 +925,7 @@ MODx.window.QuickUpdateSnippet = function(config) { Ext.applyIf(config,{ title: _('quick_update_snippet') ,action: 'Element/Snippet/Update' + ,cls: 'qce-window qce-update' ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this @@ -626,57 +947,165 @@ Ext.extend(MODx.window.QuickUpdateSnippet,MODx.window.QuickCreateSnippet); Ext.reg('modx-window-quick-update-snippet',MODx.window.QuickUpdateSnippet); - MODx.window.QuickCreatePlugin = function(config) { config = config || {}; + this.ident = config.ident || `qceplugin${Ext.id()}`; Ext.applyIf(config,{ title: _('quick_create_plugin') - ,width: 600 + ,width: 700 ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Element/Plugin/Create' ,fields: [{ xtype: 'hidden' ,name: 'id' + ,value: config.record.id || 0 },{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,anchor: '100%' - },{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,anchor: '100%' - },{ - xtype: 'textarea' - ,name: 'description' - ,fieldLabel: _('description') - ,anchor: '100%' - ,rows: 2 - },{ - xtype: 'textarea' - ,name: 'plugincode' - ,fieldLabel: _('code') - ,anchor: '100%' - ,grow: true - ,growMax: 216 + // row 1 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textfield' + ,name: 'name' + ,fieldLabel: _('name') + ,allowBlank: false + ,maxLength: 50 + ,value: config.record.name || '' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'modx-combo-category' + ,name: 'category' + ,id: `modx-${this.ident}-category` + ,fieldLabel: _('category') + ,description: MODx.expandHelp ? '' : _('plugin_category_desc') + ,value: config.record.category || 0 + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-category` + ,html: _('plugin_category_desc') + ,cls: 'desc-under' + }] + }] + }] },{ - xtype: 'xcheckbox' - ,name: 'disabled' - ,boxLabel: _('disabled') - ,hideLabel: true - ,inputValue: 1 - ,checked: false + // row 2 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,name: 'description' + ,id: `modx-${this.ident}-description` + ,description: MODx.expandHelp ? '' : _('plugin_description_desc') + ,fieldLabel: _('description') + ,grow: true + ,growMin: 50 + ,growMax: this.isSmallScreen ? 90 : 120 + ,value: config.record.description || '' + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-description` + ,html: _('plugin_description_desc') + ,cls: 'desc-under' + }] + },{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'xcheckbox' + ,name: 'disabled' + ,id: `modx-${this.ident}-disabled` + ,hideLabel: true + ,boxLabel: _('plugin_disabled') + ,description: MODx.expandHelp ? '' : _('plugin_disabled_desc') + ,ctCls: 'add-label-space' + ,inputValue: 1 + ,checked: config.record.disabled || 0 + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-disabled` + ,html: _('plugin_disabled_desc') + ,cls: 'desc-under toggle-slider-above' + },{ + xtype: 'xcheckbox' + ,name: 'clearCache' + ,id: `modx-${this.ident}-clear-cache` + ,hideLabel: true + ,boxLabel: _('clear_cache_on_save') + ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') + ,inputValue: 1 + ,checked: true + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-clear-cache` + ,html: _('clear_cache_on_save_desc') + ,cls: 'desc-under toggle-slider-above' + }] + }] + }] },{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,boxLabel: _('clear_cache_on_save') - ,hideLabel: true - ,description: _('clear_cache_on_save_msg') - ,inputValue: 1 - ,checked: true + // row 3 + cls:'form-row-wrapper', + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,fieldLabel: _('plugin_code') + ,name: 'plugincode' + ,id: `modx-${this.ident}-code` + ,grow: true + ,growMin: 90 + ,growMax: this.isSmallScreen ? 160 : 300 + ,value: config.record.plugincode || '' + }] }] ,keys: [{ key: Ext.EventObject.ENTER @@ -696,6 +1125,7 @@ MODx.window.QuickUpdatePlugin = function(config) { Ext.applyIf(config,{ title: _('quick_update_plugin') ,action: 'Element/Plugin/Update' + ,cls: 'qce-window qce-update' ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this @@ -719,92 +1149,257 @@ Ext.reg('modx-window-quick-update-plugin',MODx.window.QuickUpdatePlugin); MODx.window.QuickCreateTV = function(config) { config = config || {}; - this.ident = config.ident || 'qtv'+Ext.id(); + this.ident = config.ident || `qcetv${Ext.id()}`; Ext.applyIf(config,{ title: _('quick_create_tv') ,width: 700 ,url: MODx.config.connector_url ,action: 'Element/TemplateVar/Create' + ,cls: 'qce-window qce-create' + ,modal: true + ,monitorResize: true ,fields: [{ xtype: 'hidden' ,name: 'id' + ,value: config.record.id || 0 },{ - layout: 'column' - ,border: false + // row 1 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } ,items: [{ - columnWidth: .6 - ,layout: 'form' + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } ,items: [{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,anchor: '100%' - },{ - xtype: 'textfield' - ,name: 'caption' - ,id: 'modx-'+this.ident+'-caption' - ,fieldLabel: _('caption') - ,anchor: '100%' - },{ - xtype: 'label' - ,forId: 'modx-'+this.ident+'-caption' - ,html: _('caption_desc') - ,cls: 'desc-under' - },{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,anchor: '100%' + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textfield' + ,name: 'name' + ,fieldLabel: _('name') + ,allowBlank: false + ,maxLength: 50 + ,value: config.record.name || '' + }] },{ - xtype: 'textarea' - ,name: 'description' - ,fieldLabel: _('description') - ,anchor: '100%' + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'modx-combo-tv-input-type' + ,fieldLabel: _('tv_type') + ,name: 'type' + ,value: config.record.type || 'text' + }] }] - },{ - columnWidth: .4 - ,border: false - ,layout: 'form' + }] + },{ + // row 2 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } ,items: [{ - xtype: 'modx-combo-tv-input-type' - ,fieldLabel: _('tv_type') - ,name: 'type' - ,anchor: '100%' - },{ - xtype: 'textfield' - ,fieldLabel: _('tv_elements') - ,name: 'els' - ,id: 'modx-'+this.ident+'-elements' - ,anchor: '100%' - },{ - xtype: 'label' - ,forId: 'modx-'+this.ident+'-elements' - ,html: _('tv_elements_short_desc') - ,cls: 'desc-under' + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textfield' + ,name: 'caption' + ,id: `modx-${this.ident}-caption` + ,fieldLabel: _('caption') + ,description: MODx.expandHelp ? '' : _('tv_caption_desc') + ,maxLength: 50 + ,value: config.record.caption || '' + },{ + xtype: 'label' + ,forId: `modx-${this.ident}-caption` + ,html: _('tv_caption_desc') + ,cls: 'desc-under' + }] },{ - xtype: 'textarea' - ,fieldLabel: _('tv_default') - ,name: 'default_text' - ,id: 'modx-'+this.ident+'-default-text' - ,anchor: '100%' - ,grow: true - ,growMax: Ext.getBody().getViewSize().height <= 768 ? 300 : 380 + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'modx-combo-category' + ,name: 'category' + ,id: `modx-${this.ident}-category` + ,fieldLabel: _('category') + ,description: MODx.expandHelp ? '' : _('tv_category_desc') + ,value: config.record.category || 0 + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-category` + ,html: _('tv_category_desc') + ,cls: 'desc-under' + }] + }] + }] + },{ + // row 3 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,name: 'description' + ,id: `modx-${this.ident}-description` + ,fieldLabel: _('description') + ,description: MODx.expandHelp ? '' : _('tv_description_desc') + ,grow: true + ,growMin: 50 + ,growMax: this.isSmallScreen ? 90 : 120 + ,value: config.record.description || '' + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-description` + ,html: _('tv_description_desc') + ,cls: 'desc-under' + }] },{ - xtype: 'label' - ,forId: 'modx-'+this.ident+'-default-text' - ,html: _('tv_default_desc') - ,cls: 'desc-under' + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + } + ,items: [{ + xtype: 'xcheckbox' + ,name: 'clearCache' + ,id: `modx-${this.ident}-clear-cache` + ,hideLabel: true + ,boxLabel: _('clear_cache_on_save') + ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') + ,ctCls: 'add-label-space' + ,inputValue: 1 + ,checked: true + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-clear-cache` + ,html: _('clear_cache_on_save_desc') + ,cls: 'desc-under toggle-slider-above' + }] }] }] },{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: _('clear_cache_on_save_msg') - ,inputValue: 1 - ,checked: true + // row 4 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 1 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,name: 'els' + ,id: `modx-${this.ident}-elements` + ,fieldLabel: _('tv_elements') + ,description: MODx.expandHelp ? '' : _('tv_elements_short_desc') + ,grow: true + ,growMin: 30 + ,growMax: this.isSmallScreen ? 90 : 120 + ,value: config.record.els || '' + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-elements` + ,html: _('tv_elements_short_desc') + ,cls: 'desc-under' + }] + }] + }] + },{ + // row 5 + cls:'form-row-wrapper', + defaults: { + layout: 'column' + } + ,items: [{ + defaults: { + layout: 'form' + ,labelSeparator: '' + ,labelAlign: 'top' + } + ,items: [{ + columnWidth: 0.5 + ,defaults: { + anchor: '100%' + ,msgTarget: 'under' + ,validationEvent: 'change' + ,validateOnBlur: false + } + ,items: [{ + xtype: 'textarea' + ,name: 'default_text' + ,id: `modx-${this.ident}-default-text` + ,fieldLabel: _('tv_default') + ,description: MODx.expandHelp ? '' : _('tv_default_desc') + ,grow: true + ,growMin: 30 + ,growMax: 60 + ,value: config.record.default_text || '' + },{ + xtype: MODx.expandHelp ? 'label' : 'hidden' + ,forId: `modx-${this.ident}-default-text` + ,html: _('tv_default_desc') + ,cls: 'desc-under' + }] + },{ + // using empty column here to allow full-width of previous column in mobile contexts + columnWidth: 0.5 + ,items: [] + }] + }] }] ,keys: [{ key: Ext.EventObject.ENTER @@ -824,6 +1419,7 @@ MODx.window.QuickUpdateTV = function(config) { Ext.applyIf(config,{ title: _('quick_update_tv') ,action: 'Element/TemplateVar/Update' + ,cls: 'qce-window qce-update' ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this From 562d21a7c9c307841b92abea9ff29552a6dcebd6 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 31 Jan 2022 00:56:29 -0500 Subject: [PATCH 02/12] Update _forms.scss --- _build/templates/default/sass/_forms.scss | 187 ++++++++++-------- .../modext/widgets/element/modx.panel.tv.js | 4 +- 2 files changed, 110 insertions(+), 81 deletions(-) diff --git a/_build/templates/default/sass/_forms.scss b/_build/templates/default/sass/_forms.scss index 77bd12650fe..a6fc3712ab5 100644 --- a/_build/templates/default/sass/_forms.scss +++ b/_build/templates/default/sass/_forms.scss @@ -76,7 +76,7 @@ textarea.x-form-field, border-radius: $borderRadius; border: 1px solid $borderColor; position: relative; - transition: border-color .25s; + transition: border-color 0.25s; } .x-viewport .x-trigger-wrap-focus, @@ -142,7 +142,7 @@ input::-moz-focus-inner { padding: 0 0 0 3px; top: 0; right: 0; - transition: all .25s; + transition: all 0.25s; width: 16px; height: 16px; @@ -150,7 +150,9 @@ input::-moz-focus-inner { @extend %pseudo-font; box-sizing: border-box; color: scale-color($coreFieldLabelColor, $lightness: 50%); - content: fa-content($fa-var-undo-alt); /* better match IMO for the action being taken */ + content: fa-content( + $fa-var-undo-alt + ); /* better match IMO for the action being taken */ font-size: 14px; position: relative; bottom: 2px; @@ -161,12 +163,12 @@ input::-moz-focus-inner { height: 16px; } &.modx-field-reset { - &::before { - content: fa-content($fa-var-undo-alt); - } - &:hover::before { - color: $green; - } + &::before { + content: fa-content($fa-var-undo-alt); + } + &:hover::before { + color: $green; + } } &.modx-field-clear { &::before { @@ -242,19 +244,21 @@ input::-moz-focus-inner { border-style: solid; border-width: 10px 10px 10px 0; border-color: transparent $lightGray transparent transparent; - content: ''; + content: ""; position: absolute; top: 0; left: -10px; - transform: rotate(360deg); /* for better anti-aliasing in webkit browsers */ + transform: rotate( + 360deg + ); /* for better anti-aliasing in webkit browsers */ width: 0; height: 0; } &:after { background-color: $white; - border-radius: 50%; /* make a circle */ - content: ''; + border-radius: 50%; /* make a circle */ + content: ""; position: absolute; top: 8px; left: -4px; @@ -276,7 +280,8 @@ input::-moz-focus-inner { background-color: darken($colorSplash, 6%); &:before { - border-color: transparent darken($colorSplash, 6%) transparent transparent; + border-color: transparent darken($colorSplash, 6%) transparent + transparent; } } } @@ -288,7 +293,7 @@ input::-moz-focus-inner { border: 1px solid $borderColor; border-radius: $borderRadius; padding: 5px; - transition: all .25s; + transition: all 0.25s; &:focus { border: 1px solid $borderColorFocus; @@ -332,7 +337,6 @@ input::-moz-focus-inner { } .x-window & { - .x-form-item-label { padding: 10px 0 4px 0; /* move the form fields a bit tighter together inside windows */ } @@ -352,21 +356,25 @@ input::-moz-focus-inner { &.disabled { label { - color: scale-color($coreFieldLabelColor, $lightness: 50%); + color: scale-color($coreFieldLabelColor, $lightness: 50%); } } .x-form-element { padding: 0; font: $baseText; + /* add margin to element without label, primarily for checkboxes/radios appearing after a regular field or help element */ + &.add-label-space { + margin-top: 28px; + } .x-form-invalid-icon { - color: $red; - &::before { - @extend %pseudo-font; - content: fa-content($fa-var-exclamation-triangle); /* : "\f071" */ - position: absolute; - left: 3px; - } + color: $red; + &::before { + @extend %pseudo-font; + content: fa-content($fa-var-exclamation-triangle); /* : "\f071" */ + position: absolute; + left: 3px; + } } } @@ -374,8 +382,7 @@ input::-moz-focus-inner { /* prevent columns used inside form elements to have too much spacing, some custom TV types need this */ .x-column-inner > .x-column { - - ~.x-column { + ~ .x-column { margin-left: 5px; } @@ -409,7 +416,7 @@ input::-moz-focus-inner { } &.toggle-slider-above { - margin: .3em 0; + margin: 0.3em 0; padding-left: 3.9em; } @@ -421,15 +428,15 @@ input::-moz-focus-inner { .example-list { ul { - margin: .4em 0; + margin: 0.4em 0; li { position: relative; - margin-bottom: .25em; + margin-bottom: 0.25em; padding-left: 1.25em; &::before { @extend %pseudo-font; position: absolute; - left: .2em; + left: 0.2em; top: 0; content: fa-content($fa-var-angle-double-right); color: scale-color($mediumGray, $lightness: 20%); @@ -439,7 +446,7 @@ input::-moz-focus-inner { } .example-input, .copy-this { - padding: 0 .3em; + padding: 0 0.3em; border-radius: 2px; transition: width 1s; } @@ -477,6 +484,13 @@ input::-moz-focus-inner { } } } + &:active { + color: $darkGray; + &::after { + color: $darkGray; + } + } + } } .feedback { margin-left: 1.4rem; @@ -500,13 +514,12 @@ input::-moz-focus-inner { .deemphasize { font-style: normal; } - } .fs-toggle { padding-top: 1em; margin-top: 2em; - margin-bottom: .5em; + margin-bottom: 0.5em; border-top: 1px dashed $borderColor; } @@ -565,7 +578,6 @@ input::-moz-focus-inner { } } } - } .x-form-field { @@ -635,7 +647,7 @@ input::-moz-focus-inner { transform: translate(-50%, -50%); text-align: center; width: 30px; - transition: opacity .25s; + transition: opacity 0.25s; } &.x-form-trigger-over, @@ -681,7 +693,6 @@ input::-moz-focus-inner { content: fa-content($fa-var-file-code); font-weight: 400; } - } &.x-datetime-wrap { @@ -815,7 +826,7 @@ input::-moz-focus-inner { padding-left: 3px; &:before { - content: ''; + content: ""; } } @@ -823,7 +834,7 @@ input::-moz-focus-inner { @extend %pseudo-font; box-sizing: border-box; - content: ''; + content: ""; font-size: 18px; padding-right: 3px; position: absolute; @@ -916,9 +927,8 @@ input::-moz-focus-inner { .x-form-check-wrap, .x-fieldset-checkbox-toggle legend, - .x-fieldset legend{ - [type="checkbox"]{ - + .x-fieldset legend { + [type="checkbox"] { position: absolute; left: -9999px; html[dir="rtl"] & { @@ -926,11 +936,11 @@ input::-moz-focus-inner { left: unset; } - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { position: relative; padding-left: 3.6em; - padding-top: .2em; + padding-top: 0.2em; margin-left: 0; cursor: pointer; box-sizing: border-box; @@ -938,9 +948,9 @@ input::-moz-focus-inner { &:before, &:after { - content: ''; + content: ""; position: absolute; - transition: all .2s ease; + transition: all 0.2s ease; font-size: inherit; } @@ -957,7 +967,7 @@ input::-moz-focus-inner { &:after { left: 0.1em; top: 0.8em; - margin-top: -.65em; + margin-top: -0.65em; height: 1.3em; width: 1.3em; border-radius: 50%; @@ -967,9 +977,8 @@ input::-moz-focus-inner { } &:checked { - - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { &:after { left: 1.6em; top: 0.8em; @@ -983,9 +992,8 @@ input::-moz-focus-inner { } &.danger:checked { - - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { &:before { background-color: $red; border-color: $red; @@ -994,9 +1002,8 @@ input::-moz-focus-inner { } &.warning:checked { - - &+.x-form-cb-label, - &+.x-fieldset-header-text { + & + .x-form-cb-label, + & + .x-fieldset-header-text { &:before { background-color: $orange; border-color: $orange; @@ -1011,6 +1018,17 @@ input::-moz-focus-inner { padding-top: 1.6rem; } +.x-window-footer { + .cb-clear-cache { + .x-form-check-wrap { + label { + font-size: $bodyFontSize * .92; + font-weight: 500; + } + } + } +} + .x-form-check-group, .x-form-radio-group { /*overflow: visible; /* do not cut off the bottom of the input elements */ @@ -1028,12 +1046,11 @@ input::-moz-focus-inner { } /* applies to new xcheckboxgroup custom checkbox group */ &.aggregated-group { - padding-left: 1em; - padding-right: 1em; + padding-left: 1em; + padding-right: 1em; } } - /* superboxselect / multi-select field */ .x-superboxselect { height: auto !important; /* override the extjs default theme style of 18px */ @@ -1083,13 +1100,13 @@ input::-moz-focus-inner { cursor: pointer; display: inline-block; /*font-size: 1px;*/ outline: 0; /* fix firefox dotted outlines */ - opacity: .6; + opacity: 0.6; filter: alpha(opacity=60); /* for IE <= 8 */ padding: 0; position: absolute; top: 0; right: 0; - transition: opacity .25s; + transition: opacity 0.25s; width: 16px; height: 100%; @@ -1172,7 +1189,7 @@ input::-moz-focus-inner { margin-bottom: 2px; } - input[type=text], + input[type="text"], textarea { background-color: $coreFieldBg; background-image: none; @@ -1182,7 +1199,7 @@ input::-moz-focus-inner { width: 97%; } - input[type=text] { + input[type="text"] { font-size: 13px; height: 20px !important; padding: 5px; @@ -1220,12 +1237,13 @@ input::-moz-focus-inner { } .x-editor .x-form-check-wrap { - background-color: $white + background-color: $white; } /* fix combo on grid editor bug */ .x-grid-editor .x-form-field-wrap { - background: #f6f2f7 url($imgPath + 'modx-theme/form/combo-bck.png') repeat-x scroll 0 100%; + background: #f6f2f7 url($imgPath+"modx-theme/form/combo-bck.png") repeat-x + scroll 0 100%; } .x-grid-editor .x-form-field-wrap input { @@ -1234,7 +1252,7 @@ input::-moz-focus-inner { .x-grid-editor .x-form-field-wrap img { background-color: $white; - background-image: url($imgPath + 'modx-theme/form/trigger.png'); + background-image: url($imgPath+"modx-theme/form/trigger.png"); } .x-form-grow-sizer { @@ -1264,7 +1282,6 @@ input::-moz-focus-inner { .x-grid3 { .x-small-editor { - .x-form-text, .x-form-field-wrap { font: $fontSmall; @@ -1368,7 +1385,7 @@ input::-moz-focus-inner { .x-btn { padding: 1px; - transition: color .25s; + transition: color 0.25s; &.x-btn-over, &:hover, @@ -1383,7 +1400,7 @@ input::-moz-focus-inner { &.x-item-disabled { color: $buttonColor; - opacity: .4; + opacity: 0.4; } button:before { @@ -1427,7 +1444,11 @@ input::-moz-focus-inner { } /* the second text cell, "of X" */ - .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell { + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell { .xtb-text { display: inline-block; position: absolute; @@ -1439,7 +1460,15 @@ input::-moz-focus-inner { } /* the last regular button >>, yes, I know it's ugly but tell that Microsoft and say thanks for IE8 =) */ - .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell + .x-toolbar-cell { + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell + + .x-toolbar-cell { .x-btn { margin-right: 0; } @@ -1448,13 +1477,13 @@ input::-moz-focus-inner { /* the refresh button */ .x-toolbar-cell:last-child { opacity: 0; - transition: opacity .25s; + transition: opacity 0.25s; .x-btn { font-size: 12px; line-height: 1; margin: 0; - opacity: .4; + opacity: 0.4; padding: 0; position: absolute; bottom: 2px; @@ -1496,7 +1525,7 @@ input::-moz-focus-inner { } .x-combo-list-hd { - background-image: url($imgPath + 'modx-theme/layout/panel-title-light-bg.gif'); + background-image: url($imgPath+"modx-theme/layout/panel-title-light-bg.gif"); border-bottom-color: #bcbcbc; color: #464646; } @@ -1543,18 +1572,18 @@ input::-moz-focus-inner { .x-date-mp-ybtn a.x-date-mp-prev, .x-date-mp-ybtn a.x-date-mp-next { display: inline-block; - opacity: .6; + opacity: 0.6; filter: alpha(opacity=60); /* for IE <= 8 */ margin: 0 auto; position: relative; - transition: opacity .25s; + transition: opacity 0.25s; &:before { @extend %pseudo-font; box-sizing: border-box; color: $colorSplash; - content: ''; + content: ""; font-size: 18px; position: absolute; top: 0; @@ -1783,7 +1812,7 @@ td.x-date-mp-sep { border-radius: $borderRadius; background-color: $coreFieldBg; border: 1px solid $borderColor; - background: url('../images/tp-no-preview.png') no-repeat center center; + background: url("../images/tp-no-preview.png") no-repeat center center; overflow: hidden; .x-panel-bwrap, @@ -1804,6 +1833,6 @@ td.x-date-mp-sep { bottom: 0; padding: 10px 20px; color: #fff; - background-color: rgba(0, 0, 0, .8); + background-color: rgba(0, 0, 0, 0.8); } } diff --git a/manager/assets/modext/widgets/element/modx.panel.tv.js b/manager/assets/modext/widgets/element/modx.panel.tv.js index 982bf010c00..df1ca2c80f9 100644 --- a/manager/assets/modext/widgets/element/modx.panel.tv.js +++ b/manager/assets/modext/widgets/element/modx.panel.tv.js @@ -309,7 +309,7 @@ MODx.panel.TV = function(config) { xtype: 'xcheckbox' ,hideLabel: true ,boxLabel: _('is_static') - ,description: MODx.expandHelp ? '' : _('is_static_tv_desc') + ,description: MODx.expandHelp ? '' : _('tv_isstatic_desc') ,name: 'static' ,id: 'modx-tv-static' ,inputValue: 1 @@ -318,7 +318,7 @@ MODx.panel.TV = function(config) { xtype: MODx.expandHelp ? 'label' : 'hidden' ,forId: 'modx-tv-static' ,id: 'modx-tv-static-help' - ,html: _('is_static_tv_desc') + ,html: _('tv_isstatic_desc') ,cls: 'desc-under toggle-slider-above' }] }] From f81d187c6684c8e09d5cc2945a42d2c709bf4a55 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 6 May 2022 13:20:06 -0400 Subject: [PATCH 03/12] Progress changes Post-rebase fixes and and updated modal behavior in response to reviewer feedback. --- .../data/transport.core.system_settings.php | 54 ++ _build/templates/default/sass/_forms.scss | 2 +- _build/templates/default/sass/_windows.scss | 6 +- core/lexicon/en/chunk.inc.php | 10 +- core/lexicon/en/default.inc.php | 30 +- core/lexicon/en/element.inc.php | 13 +- core/lexicon/en/setting.inc.php | 17 + core/lexicon/en/snippet.inc.php | 10 +- core/lexicon/en/tv.inc.php | 13 +- manager/assets/lib/values.min.js | 19 + manager/assets/modext/core/modx.js | 64 +- manager/assets/modext/util/utilities.js | 73 ++ .../assets/modext/widgets/core/modx.panel.js | 53 -- .../assets/modext/widgets/core/modx.window.js | 345 ++++++++- .../modext/widgets/element/modx.panel.tv.js | 2 +- .../widgets/resource/modx.tree.resource.js | 3 + .../widgets/resource/modx.window.resource.js | 4 +- .../widgets/system/modx.tree.directory.js | 106 ++- manager/assets/modext/widgets/windows.js | 670 ++++++++---------- manager/templates/default/header.tpl | 44 ++ 20 files changed, 983 insertions(+), 555 deletions(-) create mode 100644 manager/assets/lib/values.min.js diff --git a/_build/data/transport.core.system_settings.php b/_build/data/transport.core.system_settings.php index a34f938f995..aa4fe87d77b 100644 --- a/_build/data/transport.core.system_settings.php +++ b/_build/data/transport.core.system_settings.php @@ -2157,5 +2157,59 @@ 'area' => 'static_elements', 'editedon' => null, ], '', true, true); +$settings['enable_overlays'] = $xpdo->newObject(modSystemSetting::class); +$settings['enable_overlays']->fromArray([ + 'key' => 'enable_overlays', + 'value' => true, + 'xtype' => 'combo-boolean', + 'namespace' => 'core', + 'area' => 'manager', + 'editedon' => null, +], '', true, true); +$settings['overlay_color'] = $xpdo->newObject(modSystemSetting::class); +$settings['overlay_color']->fromArray([ + 'key' => 'overlay_color', + 'value' => '#0d141d', + 'xtype' => 'textfield', + 'namespace' => 'core', + 'area' => 'manager', + 'editedon' => null, +], '', true, true); +$settings['overlay_opacity_blocking'] = $xpdo->newObject(modSystemSetting::class); +$settings['overlay_opacity_blocking']->fromArray([ + 'key' => 'overlay_opacity_blocking', + 'value' => 50, + 'xtype' => 'numberfield', + 'namespace' => 'core', + 'area' => 'manager', + 'editedon' => null, +], '', true, true); +$settings['overlay_opacity_nonblocking'] = $xpdo->newObject(modSystemSetting::class); +$settings['overlay_opacity_nonblocking']->fromArray([ + 'key' => 'overlay_opacity_nonblocking', + 'value' => 50, + 'xtype' => 'numberfield', + 'namespace' => 'core', + 'area' => 'manager', + 'editedon' => null, +], '', true, true); +$settings['overlay_tint_blocking'] = $xpdo->newObject(modSystemSetting::class); +$settings['overlay_tint_blocking']->fromArray([ + 'key' => 'overlay_tint_blocking', + 'value' => 0, + 'xtype' => 'numberfield', + 'namespace' => 'core', + 'area' => 'manager', + 'editedon' => null, +], '', true, true); +$settings['overlay_tint_nonblocking'] = $xpdo->newObject(modSystemSetting::class); +$settings['overlay_tint_nonblocking']->fromArray([ + 'key' => 'overlay_tint_nonblocking', + 'value' => 70, + 'xtype' => 'numberfield', + 'namespace' => 'core', + 'area' => 'manager', + 'editedon' => null, +], '', true, true); return $settings; diff --git a/_build/templates/default/sass/_forms.scss b/_build/templates/default/sass/_forms.scss index a6fc3712ab5..97a6ef5281e 100644 --- a/_build/templates/default/sass/_forms.scss +++ b/_build/templates/default/sass/_forms.scss @@ -490,8 +490,8 @@ input::-moz-focus-inner { color: $darkGray; } } - } } + .feedback { margin-left: 1.4rem; color: scale-color($blue, $lightness: -35%); diff --git a/_build/templates/default/sass/_windows.scss b/_build/templates/default/sass/_windows.scss index 581cfbd927c..f9cc49939dd 100644 --- a/_build/templates/default/sass/_windows.scss +++ b/_build/templates/default/sass/_windows.scss @@ -306,9 +306,13 @@ .ext-el-mask { background-color: $colorSplashShadow; opacity: 0; - transition: opacity .25s; + transition: opacity .5s; /*z-index: 10;*/ /* this is handeled by extjs and set to 9000 on show */ + &.clickthrough { + pointer-events: none; + } + &.fade-in { opacity: .5; } diff --git a/core/lexicon/en/chunk.inc.php b/core/lexicon/en/chunk.inc.php index e029e223351..5c5dd6b37f0 100644 --- a/core/lexicon/en/chunk.inc.php +++ b/core/lexicon/en/chunk.inc.php @@ -7,9 +7,6 @@ * @subpackage lexicon */ -// Entry out of alpha order because it must come before the entry it's used in below -$_lang['example_tag_chunk_name'] = 'NameOfChunk'; - $_lang['chunk'] = 'Chunk'; $_lang['chunk_category_desc'] = 'Use to group Chunks within the Elements tree.'; $_lang['chunk_delete_confirm'] = 'Are you sure you want to delete this chunk?'; @@ -27,11 +24,11 @@ $_lang['chunk_err_ns_name'] = 'Please specify a name.'; $_lang['chunk_lock'] = 'Lock chunk for editing'; $_lang['chunk_lock_desc'] = 'Only users with “edit_locked” permissions can edit this Chunk.'; -$_lang['chunk_name_desc'] = 'Place the content generated by this Chunk in a Resource, Template, or other Chunk using the following MODX tag: [[+tag]]'; $_lang['chunk_new'] = 'Create Chunk'; $_lang['chunk_properties'] = 'Default Properties'; $_lang['chunk_tab_general_desc'] = 'Here you can enter the basic attributes for this Chunk as well as its content. The content must be HTML, either placed in the Chunk Code field below or in a static external file, and may include MODX tags. Note, however, that PHP code will not run in this element.'; -$_lang['chunk_tag_copied'] = 'Chunk tag copied!'; +$_lang['chunk_title'] = 'Create/edit chunk'; +$_lang['chunk_untitled'] = 'Untitled Chunk'; $_lang['chunks'] = 'Chunks'; // Temporarily match old keys to new ones to ensure compatibility @@ -49,6 +46,9 @@ quick create/edit panels access to them when opened outside the context of their respective element types) + example_tag_chunk_name chunk_code chunk_description_desc + chunk_name_desc + chunk_tag_copied */ diff --git a/core/lexicon/en/default.inc.php b/core/lexicon/en/default.inc.php index ccffe760e42..cfef37adb0c 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -572,10 +572,16 @@ */ // All +$_lang['static_file'] = 'Static File'; +$_lang['static_file_desc'] = 'The external file location where the source code for this element is stored.'; // Chunks +$_lang['example_tag_chunk_name'] = 'NameOfChunk'; $_lang['chunk_code'] = 'Chunk Code (HTML)'; $_lang['chunk_description_desc'] = 'Usage information for this Chunk shown in search results and as a tooltip in the Elements tree.'; +$_lang['chunk_name_desc'] = 'Place the content generated by this Chunk in a Resource, Template, or other Chunk using the following MODX tag: [[+tag]]'; +$_lang['chunk_new_name'] = 'New Chunk Name'; +$_lang['chunk_tag_copied'] = 'Chunk tag copied!'; // Temporarily match old keys to new ones to ensure compatibility $_lang['chunk_desc_description'] = $_lang['chunk_description_desc']; @@ -584,14 +590,19 @@ $_lang['plugin_description_desc'] = 'Usage information for this Plugin shown in search results and as a tooltip in the Elements tree.'; $_lang['plugin_disabled'] = 'Deactivate Plugin'; $_lang['plugin_disabled_desc'] = 'When deactivated, this Plugin will not respond to events.'; +$_lang['plugin_new_name'] = 'New Plugin Name'; // Temporarily match old keys to new ones to ensure compatibility $_lang['plugin_desc'] = $_lang['description']; $_lang['plugin_desc_description'] = $_lang['plugin_description_desc']; $_lang['plugin_disabled_msg'] = $_lang['plugin_disabled_desc']; // Snippets +$_lang['example_tag_snippet_name'] = 'NameOfSnippet'; $_lang['snippet_code'] = 'Snippet Code (PHP)'; $_lang['snippet_description_desc'] = 'Usage information for this Snippet shown in search results and as a tooltip in the Elements tree.'; +$_lang['snippet_name_desc'] = 'Place the content generated by this Snippet in a Resource, Template, or Chunk using the following MODX tag: [[+tag]]'; +$_lang['snippet_new_name'] = 'New Snippet Name'; +$_lang['snippet_tag_copied'] = 'Snippet tag copied!'; // Temporarily match old keys to new ones to ensure compatibility $_lang['snippet_desc'] = $_lang['description']; $_lang['snippet_desc_description'] = $_lang['snippet_description_desc']; @@ -599,17 +610,26 @@ // Templates $_lang['template_code'] = 'Template Code (HTML)'; $_lang['template_description_desc'] = 'Usage information for this Template shown in search results and as a tooltip in the Elements tree.'; +$_lang['template_new_name'] = 'New Template Name'; // Temporarily match old keys to new ones to ensure compatibility $_lang['template_desc'] = $_lang['description']; $_lang['template_desc_description'] = $_lang['template_description_desc']; // TVs -$_lang['tv_tab_input_options'] = 'Input Options'; -$_lang['tv_type'] = 'Input Type'; -$_lang['tv_default'] = 'Default Value'; -$_lang['tv_default_desc'] = 'The content this TV will show if user-entered content is not provided.'; +$_lang['example_tag_tv_name'] = 'NameOfTV'; $_lang['tv_caption_desc'] = 'The label shown for this TV in Resource editing pages (can be overridden per template or other criteria using Form Customization).'; $_lang['tv_category_desc'] = 'Use to group TVs in Resource editing pages and within the Elements tree.'; -$_lang['tv_description_desc'] = 'Usage information for this TV shown next to its caption in Resource editing pages and as a tooltip in the Elements tree.'; +$_lang['tv_default'] = 'Default Value'; +$_lang['tv_default_desc'] = 'The content this TV will show if user-entered content is not provided.'; +// $_lang['tv_description_desc'] = 'Usage information for this TV shown next to its caption in Resource editing pages and as a tooltip in the Elements tree.'; +$_lang['tv_description_desc'] = 'Usage information for this TV shown next to its caption in Resource editing pages, as a tooltip in the Elements tree, and within search results.'; $_lang['tv_elements'] = 'Input Option Values'; $_lang['tv_elements_short_desc'] = 'Defines the selectable options for this TV, which may be manually entered or built with a one-line database query.'; +$_lang['tv_name_desc'] = 'Place the content generated by this TV in a Resource, Template, or Chunk using the following MODX tag: [[+tag]]'; +$_lang['tv_new_caption'] = 'New TV Caption'; +$_lang['tv_new_name'] = 'New TV Name'; +$_lang['tv_tag_copied'] = 'TV tag copied!'; +$_lang['tv_type'] = 'Input Type'; +$_lang['tv_type_desc'] = 'The html input or content component type generated by this TV.'; + // Temporarily match old keys to new ones to ensure compatibility + $_lang['tv_description'] = $_lang['description']; diff --git a/core/lexicon/en/element.inc.php b/core/lexicon/en/element.inc.php index d7ea047a1bc..8bbd93499d1 100644 --- a/core/lexicon/en/element.inc.php +++ b/core/lexicon/en/element.inc.php @@ -27,8 +27,6 @@ $_lang['quick_update_tv'] = 'Quick Edit TV'; $_lang['property_preprocess'] = 'Pre-process tags in Property Values'; $_lang['property_preprocess_msg'] = 'If enabled, tags in Default Property/Property Set values will be processed before they are used for Element processing.'; -$_lang['static_file'] = 'Static File'; -$_lang['static_file_desc'] = 'The external file location where the source code for this element is stored.'; $_lang['static_source'] = 'Media Source'; $_lang['static_source_desc'] = 'Sets the basePath for the Static File to the one specified in the chosen Media Source. Choose “None” when specifying an absolute or other custom path to the file.'; $_lang['tv_elements'] = 'Input Option Values'; @@ -42,3 +40,14 @@ $_lang['is_static_msg'] = $_lang['is_static_desc']; $_lang['static_file_msg'] = $_lang['static_file_desc']; $_lang['static_source_msg'] = $_lang['static_source_desc']; + +/* + Refer to default.inc.php for the keys below. + (Placement in this default file necessary to allow + quick create/edit/duplicate panels access to them when opened + outside the context of their respective element types) + + static_file + static_file_desc + +*/ diff --git a/core/lexicon/en/setting.inc.php b/core/lexicon/en/setting.inc.php index 03cf9c14fa8..e7684bde7d4 100644 --- a/core/lexicon/en/setting.inc.php +++ b/core/lexicon/en/setting.inc.php @@ -494,6 +494,23 @@ $_lang['setting_package_installer_at_top'] = 'Pin Package-Installer at top'; $_lang['setting_package_installer_at_top_desc'] = 'If enabled, the Installer entry will be pinned to the top of the Extras menu. Otherwise it will be positioned according to its menuindex.'; +$_lang['setting_enable_overlays'] = 'Enable Overlays'; +$_lang['setting_enable_overlays_desc'] = 'If enabled, a semi-transparent overlay will mask the manager’s main interface below editing windows and dialog boxes, enhancing user focus on the front window(s).'; + +$_lang['setting_overlay_color'] = 'Overlay Color'; +$_lang['setting_overlay_color_desc'] = 'Any valid css color specified in Hexadecimal, RGB/A, HSL/A, W3C named, or transparent format.'; + +$_lang['setting_overlay_opacity_blocking'] = 'Overlay Opacity (Blocking)'; +$_lang['setting_overlay_opacity_blocking_desc'] = 'Controls how opaque a dialog window’s underlying overlay (mask) will be. This type of overlay is also used as a mask for disabled grids and blocks interaction with the elements below. Valid values range from 0 (completely transparent) to 100 (completely opaque).'; + +$_lang['setting_overlay_opacity_nonblocking'] = 'Overlay Opacity (Non-Blocking)'; +$_lang['setting_overlay_opacity_nonblocking_desc'] = 'Controls how opaque an editing window’s non-blocking underlying overlay (mask) will be. This type of overlay allows interaction with the elements below. Valid values range from 0 (completely transparent) to 100 (completely opaque).'; + +$_lang['setting_overlay_tint_blocking'] = 'Overlay Tint (Blocking)'; +$_lang['setting_overlay_tint_blocking_desc'] = 'The percentage of white added to lighten a dialog window’s underlying overlay color. This type of overlay is also used as a mask for disabled grids and blocks interaction with the elements below. Valid values range from 0 (no lightening) to 100 (completely white) in increments of 5.'; + +$_lang['setting_overlay_tint_nonblocking'] = 'Overlay Tint (Non-Blocking)'; +$_lang['setting_overlay_tint_nonblocking_desc'] = 'The percentage of white added to lighten an editing window’s non-blocking underlying overlay color. This type of overlay allows interaction with the elements below. Valid values range from 0 (no lightening) to 100 (completely white) in increments of 5.'; $_lang['setting_parser_recurse_uncacheable'] = 'Delay Uncacheable Parsing'; $_lang['setting_parser_recurse_uncacheable_desc'] = 'If disabled, uncacheable elements may have their output cached inside cacheable element content. Disable this ONLY if you are having problems with complex nested parsing which stopped working as expected.'; diff --git a/core/lexicon/en/snippet.inc.php b/core/lexicon/en/snippet.inc.php index b43c29009f2..ca6cfe8ddf3 100644 --- a/core/lexicon/en/snippet.inc.php +++ b/core/lexicon/en/snippet.inc.php @@ -6,7 +6,6 @@ * @package modx * @subpackage lexicon */ -$_lang['example_tag_snippet_name'] = 'NameOfSnippet'; $_lang['snippet'] = 'Snippet'; $_lang['snippets_available'] = 'Snippets available for you to include in your page'; $_lang['snippet_category_desc'] = 'Use to group Snippets within the Elements tree.'; @@ -28,11 +27,11 @@ $_lang['snippet_lock'] = 'Lock snippet for editing'; $_lang['snippet_lock_desc'] = 'Only users with “edit_locked” permissions can edit this Snippet.'; $_lang['snippet_management_msg'] = 'Here you can choose which snippet you wish to edit.'; -$_lang['snippet_name_desc'] = 'Place the content generated by this Snippet in a Resource, Template, or Chunk using the following MODX tag: [[+tag]]'; $_lang['snippet_new'] = 'Create Snippet'; $_lang['snippet_properties'] = 'Default Properties'; -$_lang['snippet_tab_general_desc'] = 'Here you can enter the basic attributes for this Snippet as well as its content. The content must be PHP, either placed in the Snippet Code field below or in a static external file. To receive output from your Snippet at the point where it is called (within a Template or Chunk), a value must be returned from within the code.'; -$_lang['snippet_tag_copied'] = 'Snippet tag copied!'; +$_lang['snippet_tab_general_desc'] = 'Here you can enter the basic attributes for this Snippet as well as its content. The content must be PHP, either placed in the Snippet Code field below or in a static external file. To receive output from your Snippet at the point where it is called (within a Template or Chunk), a value must be returned from within the code.'; +$_lang['snippet_title'] = 'Create/edit snippet'; +$_lang['snippet_untitled'] = 'Untitled snippet'; $_lang['snippets'] = 'Snippets'; // Temporarily match old keys to new ones to ensure compatibility @@ -50,6 +49,9 @@ quick create/edit panels access to them when opened outside the context of their respective element types) + example_tag_snippet_name snippet_code snippet_description_desc + snippet_name_desc + snippet_tag_copied */ diff --git a/core/lexicon/en/tv.inc.php b/core/lexicon/en/tv.inc.php index dca75b3e888..3f14b786ead 100644 --- a/core/lexicon/en/tv.inc.php +++ b/core/lexicon/en/tv.inc.php @@ -6,7 +6,6 @@ * @package modx * @subpackage lexicon */ -$_lang['example_tag_tv_name'] = 'NameOfTV'; $_lang['has_access'] = 'Has Access?'; $_lang['filter_by_category'] = 'Filter by Category...'; $_lang['rank'] = 'Rank'; @@ -15,12 +14,8 @@ $_lang['tvs'] = 'Template Variables'; $_lang['tv_binding_msg'] = 'This field supports data source bindings using the @ commands'; $_lang['tv_caption'] = 'Caption'; -$_lang['tv_caption_desc'] = 'The label shown for this TV in Resource editing pages (can be overridden per template or other criteria using Form Customization).'; -$_lang['tv_category_desc'] = 'Use to group TVs in Resource editing pages and within the Elements tree.'; $_lang['tv_change_template_msg'] = 'Changing this template will cause the page to reload the TVs, losing any unsaved changes.

Are you sure you want to change this template?'; $_lang['tv_delete_confirm'] = 'Are you sure you want to delete this TV?'; -$_lang['tv_description'] = 'Description'; -$_lang['tv_description_desc'] = 'Usage information for this TV shown next to its caption in Resource editing pages, as a tooltip in the Elements tree, and within search results.'; $_lang['tv_err_delete'] = 'An error occurred while trying to delete the TV.'; $_lang['tv_err_duplicate'] = 'An error occurred while trying to duplicate the TV.'; $_lang['tv_err_duplicate_templates'] = 'An error occurred while duplicating the TV templates.'; @@ -42,7 +37,7 @@ $_lang['tv_lock'] = 'Restrict Editing'; $_lang['tv_lock_desc'] = 'Only users with “edit_locked” permissions can edit this TV.'; $_lang['tv_management_msg'] = 'Manage additional custom TVs for your documents.'; -$_lang['tv_name_desc'] = 'Place the content generated by this TV in a Resource, Template, or Chunk using the following MODX tag: [[+tag]]'; +$_lang['tv_name'] = 'TV Name'; $_lang['tv_new'] = 'Create TV'; $_lang['tv_novars'] = 'No TVs found'; $_lang['tv_properties'] = 'Default Properties'; @@ -51,13 +46,13 @@ $_lang['tv_reset_params'] = 'Reset parameters'; $_lang['tv_tab_access_desc'] = 'Select the Resource Groups that this TV belongs to. Only users with access to the Groups selected will be able to modify this TV. If no Groups are selected, all users with access to the Manager will be able to modify the TV.'; $_lang['tv_tab_general_desc'] = 'Here you can enter the basic attributes for this Template Variable (TV). Note that TVs must be assigned to templates in order to access them from snippets and documents.'; +$_lang['tv_tab_input_options'] = 'Input Options'; $_lang['tv_tab_input_options_desc'] = '

Here you can edit the input options for the TV, specific to the type of input render that you select.

'; $_lang['tv_tab_output_options'] = 'Output Options'; $_lang['tv_tab_output_options_desc'] = '

Here you can edit the output options for the TV, specific to the type of output render that you select.

'; $_lang['tv_tab_sources_desc'] = 'Here you can assign the Media Sources that are to be used for this TV in each specified Context. Double-click on the Source name in the grid to change it.'; $_lang['tv_tab_tmpl_access'] = 'Template Access'; $_lang['tv_tab_tmpl_access_desc'] = 'Select the templates that are allowed to access this TV.'; -$_lang['tv_tag_copied'] = 'TV tag copied!'; $_lang['tv_widget'] = 'Widget'; $_lang['tv_widget_prop'] = 'Widget Properties'; $_lang['tvd_err_remove'] = 'An error occurred while trying to delete the TV from the document.'; @@ -94,9 +89,11 @@ quick create/edit panels access to them when opened outside the context of their respective element types) - tv_tab_input_options tv_caption_desc tv_category_desc tv_description_desc + tv_name_desc + tv_tag_copied + tv_type_desc */ diff --git a/manager/assets/lib/values.min.js b/manager/assets/lib/values.min.js new file mode 100644 index 00000000000..39a9e3f59f3 --- /dev/null +++ b/manager/assets/lib/values.min.js @@ -0,0 +1,19 @@ +!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e=e||self).Values=r()}(this,(function(){"use strict";var e={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},r=new RegExp("[^#a-f\\d]","gi"),n=new RegExp("^#?[a-f\\d]{3}[a-f\\d]?$|^#?[a-f\\d]{6}([a-f\\d]{2})?$","i"),t=new RegExp(/^#([a-f0-9]{3,4}|[a-f0-9]{4}(?:[a-f0-9]{2}){1,2})\b$/,"i"),a="-?\\d*(?:\\.\\d+)",i="("+a+"?)",o="("+a+"?%)",s=("^\n hsla?\\(\n \\s*(-?\\d*(?:\\.\\d+)?(?:deg|rad|turn)?)\\s*,\n \\s*"+o+"\\s*,\n \\s*"+o+"\\s*\n (?:,\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),l=new RegExp(s),u=("^\n hsla?\\(\n \\s*(-?\\d*(?:\\.\\d+)?(?:deg|rad|turn)?)\\s*\n \\s+"+o+"\n \\s+"+o+"\n \\s*(?:\\s*\\/\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),h=new RegExp(u),d=("^\n rgba?\\(\n \\s*"+i+"\\s*,\n \\s*"+i+"\\s*,\n \\s*"+i+"\\s*\n (?:,\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),g=new RegExp(d),p=("^\n rgba?\\(\n \\s*"+o+"\\s*,\n \\s*"+o+"\\s*,\n \\s*"+o+"\\s*\n (?:,\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),c=new RegExp(p),f=("^\n rgba?\\(\n \\s*"+i+"\n \\s+"+i+"\n \\s+"+i+"\n \\s*(?:\\s*\\/\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n$\n").replace(/\n|\s/g,""),v=new RegExp(f),b=("^\n rgba?\\(\n \\s*"+o+"\n \\s+"+o+"\n \\s+"+o+"\n \\s*(?:\\s*\\/\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n$\n").replace(/\n|\s/g,""),m=new RegExp(b),y=new RegExp(/^transparent$/,"i"),w=function(e,r,n){return Math.min(Math.max(r,e),n)},k=function(e){var r=e;return"number"!=typeof r&&(r=r.endsWith("%")?255*parseFloat(r)/100:parseFloat(r)),w(Math.round(r),0,255)},x=function(e){return w(parseFloat(e),0,100)};function M(e){var r=e;return"number"!=typeof r&&(r=r.endsWith("%")?parseFloat(r)/100:parseFloat(r)),w(r,0,1)}function E(e){var t=function(e,t){if(void 0===t&&(t={}),"string"!=typeof e||r.test(e)||!n.test(e))throw new TypeError("Expected a valid hex string");var a=1;8===(e=e.replace(/^#/,"")).length&&(a=parseInt(e.slice(6,8),16)/255,e=e.slice(0,6)),4===e.length&&(a=parseInt(e.slice(3,4).repeat(2),16)/255,e=e.slice(0,3)),3===e.length&&(e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);var i=parseInt(e,16),o=i>>16,s=i>>8&255,l=255&i;return"array"===t.format?[o,s,l,a]:{red:o,green:s,blue:l,alpha:a}}(e,{format:"array"});return F([null,t[0],t[1],t[2],t[3]])}function F(e){var r=e[1],n=e[2],t=e[3],a=e[4];return void 0===a&&(a=1),{type:"rgb",values:[r,n,t].map(k),alpha:M(null===a?1:a)}} +/** + * parse-css-color + * @version v0.1.2 + * @link http://github.com/noeldelgado/parse-css-color/ + * @license MIT + */var R=function(r){if("string"!=typeof r)return null;var n=t.exec(r);if(n)return E(n[0]);var a=h.exec(r)||l.exec(r);if(a)return function(e){var r=e[1],n=e[2],t=e[3],a=e[4];void 0===a&&(a=1);var i=r;return{type:"hsl",values:[i=i.endsWith("turn")?360*parseFloat(i)/1:i.endsWith("rad")?Math.round(180*parseFloat(i)/Math.PI):parseFloat(i),x(n),x(t)],alpha:M(null===a?1:a)}}(a);var i=v.exec(r)||m.exec(r)||g.exec(r)||c.exec(r);if(i)return F(i);if(y.exec(r))return F([null,0,0,0,0]);var o=e[r.toLowerCase()];return o?F([null,o[0],o[1],o[2],1]):null};var $=function(e){var r,n,t,a,i,o=e[0]/360,s=e[1]/100,l=e[2]/100;if(0==s)return[i=255*l,i,i];r=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(t=o+1/3*-(u-1))<0&&t++,t>1&&t--,i=6*t<1?r+6*(n-r)*t:2*t<1?n:3*t<2?r+(n-r)*(2/3-t)*6:r,a[u]=255*i;return a};var q=function(e,r,n){return Math.min(Math.max(e,r),n)};function S(e){var r=Math.round(q(e,0,255)).toString(16);return 1==r.length?"0"+r:r}var I=function(e){var r=4===e.length?S(255*e[3]):"";return"#"+S(e[0])+S(e[1])+S(e[2])+r};var j=function(e){var r,n,t=e[0]/255,a=e[1]/255,i=e[2]/255,o=Math.min(t,a,i),s=Math.max(t,a,i),l=s-o;return s==o?r=0:t==s?r=(a-i)/l:a==s?r=2+(i-t)/l:i==s&&(r=4+(t-a)/l),(r=Math.min(60*r,360))<0&&(r+=360),n=(o+s)/2,[r,100*(s==o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]}; +/** + * mix-css-color + * @version v0.1.1 + * @link http://github.com/noeldelgado/mix-css-color/ + * @license MIT + */function C(e){var r=R(e);return null===r?null:("hsl"===r.type&&(r.values=$(r.values)),r)}function W(e,r,n){void 0===n&&(n=50);var t=C(e),a=C(r);if(!t||!a)return null;var i=Math.min(Math.max(0,n),100)/100,o=2*i-1,s=t.alpha-a.alpha,l=((o*s==-1?o:(o+s)/(1+o*s))+1)/2,u=1-l,h=t.values.map((function(e,r){return Math.round(t.values[r]*l+a.values[r]*u)})),d=h[0],g=h[1],p=h[2],c=parseFloat((t.alpha*i+a.alpha*(1-i)).toFixed(8));return{hex:I([d,g,p]),hexa:I([d,g,p,c]),rgba:[d,g,p,c],hsla:j([d,g,p]).map(Math.round).concat([c])}} +/** + * values.js - Get the tints and shades of a color + * @version v2.0.0 + * @link http://noeldelgado.github.io/values.js/ + * @license MIT + */var _=function(e,r){return null===e||isNaN(e)||"string"==typeof e?r:e},N=function(e,r,n){var t;void 0===e&&(e="#000"),void 0===r&&(r="base"),void 0===n&&(n=0),t=[[0,0,0],1,r,n],this.rgb=t[0],this.alpha=t[1],this.type=t[2],this.weight=t[3];var a=null===e?"#000":e;if("string"!=typeof a)throw new TypeError("Input should be a string: "+a);var i=R(a);if(!i)throw new Error("Unable to parse color from string: "+a);return this["_setFrom"+i.type.toUpperCase()](i.values.concat([i.alpha]))},O={hex:{configurable:!0}};return O.hex.get=function(){return this.hexString().replace(/^#/,"")},N.prototype.setColor=function(e){var r=R(e);return r?this["_setFrom"+r.type.toUpperCase()](r.values.concat([r.alpha])):null},N.prototype.tint=function(e,r){return void 0===r&&(r=_(e,50)),new N("rgb("+W("#fff",this.rgbString(),r).rgba+")","tint",r)},N.prototype.shade=function(e,r){return void 0===r&&(r=_(e,50)),new N("rgb("+W("#000",this.rgbString(),r).rgba+")","shade",r)},N.prototype.tints=function(e,r){var n=this;return void 0===r&&(r=_(e,10)),Array.from({length:100/r},(function(e,t){return n.tint((t+1)*r)}))},N.prototype.shades=function(e,r){var n=this;return void 0===r&&(r=_(e,10)),Array.from({length:100/r},(function(e,t){return n.shade((t+1)*r)}))},N.prototype.all=function(e){return void 0===e&&(e=10),this.tints(e).reverse().concat([Object.assign(this)],this.shades(e))},N.prototype.hexString=function(){return I(this.alpha>=1?this.rgb:this.rgb.concat([this.alpha]))},N.prototype.rgbString=function(){var e=(this.alpha>=1?this.rgb:this.rgb.concat([this.alpha])).join(", ");return(this.alpha>=1?"rgb":"rgba")+"("+e+")"},N.prototype.getBrightness=function(){return Math.round(this.rgb.reduce((function(e,r){return e+r}))/765*100)},N.prototype._setFromRGB=function(e){var r;return r=[[e[0],e[1],e[2]],e[3]],this.rgb=r[0],this.alpha=r[1],this},N.prototype._setFromHSL=function(e){var r,n=e[0],t=e[1],a=e[2],i=e[3];return r=[$([n,t,a]).map(Math.round),i],this.rgb=r[0],this.alpha=r[1],this},Object.defineProperties(N.prototype,O),N.VERSION="v2.0.0",N})); diff --git a/manager/assets/modext/core/modx.js b/manager/assets/modext/core/modx.js index bbdf81902b5..7c498e7ee69 100644 --- a/manager/assets/modext/core/modx.js +++ b/manager/assets/modext/core/modx.js @@ -46,6 +46,18 @@ Ext.extend(MODx,Ext.Component,{ ,expandHelp: true ,defaultState: [] + /** + * Tracks our custom non click event blocking 'pseudo' modals; should contain + * an object for each currently open modal containing at minimum a reference to + * the modal window’s id (itemId). + */ + ,openPseudoModals: [] + + /** + * An Ext.Element object containing the page mask created by pseudo modals. + */ + ,mask: {} + ,startup: function() { this.initQuickTips(); this.initMarkRequiredFields(); @@ -437,49 +449,37 @@ Ext.extend(MODx,Ext.Component,{ } ,getStaticElementsPath: function(name, category, type) { - var path = MODx.config.static_elements_basepath, - ext = ''; - - if (category.length > 0) { - category = category.replace(/[^\w\s-]/gi, ""); - category = category.replace(/\s/g, '-').toLowerCase(); - // Convert nested elements to nested directory structure. - category = category.replace(/--/gi, '/'); - category = "/" + category + "/"; - } else { - category = "/"; - } + let path = MODx.config.static_elements_basepath, + ext = ''; + const htmlExtension = MODx.config.static_elements_html_extension || '.tpl'; + // console.log('cat before: ',category); + category = category.length > 0 ? MODx.util.Format.staticElementPathFragment(category, true) : '/' ; + // console.log('cat after: ',category); // Remove trailing slash. - path = path.replace(/\/$/, ""); + path = path.replace(/\/$/, ''); switch(type) { - case "templates": - ext = ".template" + (MODx.config.static_elements_html_extension || ".tpl"); + case 'templates': + ext = `.template${htmlExtension}`; break; - case "tvs": - ext = ".tv" + (MODx.config.static_elements_html_extension || ".tpl"); + case 'tvs': + ext = `.tv${htmlExtension}`; break; - case "chunks": - ext = ".chunk" + (MODx.config.static_elements_html_extension || ".tpl"); + case 'chunks': + ext = `.chunk${htmlExtension}`; break; - case "snippets": - ext = ".snippet.php"; + case 'snippets': + ext = '.snippet.php'; break; - case "plugins": - ext = ".plugin.php"; + case 'plugins': + ext = '.plugin.php'; break; } - // Remove special characters and spaces. - name = name.replace(/[^\w\s-]/gi, ''); - name = name.replace(/\s/g, '-').toLowerCase(); - - if (name.length > 0) { - path += "/" + type + category + name + ext; - } else { - path += "/" + type + category; - } + name = MODx.util.Format.staticElementPathFragment(name); + path += '/' + type + category; + path += name.length > 0 ? name + ext : '' ; return path; } diff --git a/manager/assets/modext/util/utilities.js b/manager/assets/modext/util/utilities.js index d1ee1e9bf12..679bcc23102 100644 --- a/manager/assets/modext/util/utilities.js +++ b/manager/assets/modext/util/utilities.js @@ -200,6 +200,57 @@ MODx.util.safeHtml = function (input, allowedTags, allowedAttributes) { return input.replace(eventAttributes, 'on​$1'); }; +/** + * @property {Function} insertTagCopyUtility - Updates placeholder tag in element name's help + * field to the current element name and attaches a listener to copy the tag when clicked on + * + * @param {Object} cmp - The help field's Ext.Component object + * @param {String} elType - The MODX element type (i.e., tv, chunk, or snippet) + */ +MODx.util.insertTagCopyUtility = function(cmp, elType) { + const helpTag = cmp.getEl().child('.example-replace-name'), + elTag = cmp.getEl().child('.copy-this'); + let nameVal = cmp.previousSibling().getValue(), + tagText; + console.log('helpTag: ',helpTag); + // If the helptag isn't available, skip here. This may happen when a lexicon is missing or outdated + // and doesn't contain the `example-replace-name` class. + if (!helpTag) { + return; + } + + if (nameVal.length > 0) { + helpTag.update(nameVal); + tagText = elTag.dom.innerText; + } + + helpTag.on({ + click: function() { + nameVal = cmp.previousSibling().getValue(); + if (nameVal.length > 0) { + tagText = elTag.dom.innerText; + const tmp = document.createElement('textarea'); + tmp.value = tagText; + document.body.appendChild(tmp); + tmp.select(); + if (document.execCommand('copy')) { + const feedback = document.createElement('span'); + feedback.className = 'element-panel feedback item-copied'; + feedback.textContent = _(elType+'_tag_copied'); + elTag.insertSibling(feedback, 'after'); + setTimeout(function(){ + feedback.style.opacity = 0; + setTimeout(function(){ + feedback.remove(); + }, 1200); + }, 10); + } + tmp.remove(); + } + } + }); +} + /**************************************************************************** * Ext-specific overrides/extensions * ****************************************************************************/ @@ -490,6 +541,28 @@ MODx.util.Format = { format = format.join(' '); return (new Date(timestamp).format(format)); + }, + staticElementPathFragment: function(fragment, isDirectoryFragment = false) { + fragment = fragment + .replace(/[^\w\s-]/gi, '') + // .replace(/[\s]+/g, '-') + // .toLowerCase() + ; + fragment = isDirectoryFragment ? fragment.replace(/\s/g, '-') : fragment.replace(/[\s]+/g, '-') ; + // Convert nested element categories to nested directory structure + if (isDirectoryFragment) { + fragment = fragment.replace(/--/gi, '/'); + fragment = `/${fragment}/`; + } + return fragment.toLowerCase(); + }, + fileFullPath: function(path, lowerCaseAll = false) { + path = path + .replace(/[^\w\s-/]/gi, '') + .replace(/[/]{2,}/g, '/') + .replace(/[\s]+/g, '-') + ; + return lowerCaseAll ? path.toLowerCase() : path ; } }; diff --git a/manager/assets/modext/widgets/core/modx.panel.js b/manager/assets/modext/widgets/core/modx.panel.js index 3d6681c7d61..288549249a7 100644 --- a/manager/assets/modext/widgets/core/modx.panel.js +++ b/manager/assets/modext/widgets/core/modx.panel.js @@ -477,59 +477,6 @@ Ext.extend(MODx.FormPanel,Ext.FormPanel,{ } } - /** - * @property {Function} insertTagCopyUtility - Updates placeholder tag in element name's help - * field to the current element name and attaches a listener to copy the tag when clicked on - * - * @param {Object} cmp - The help field's Ext.Component object - * @param {String} elType - The MODX element type (i.e., tv, chunk, or snippet) - */ - ,insertTagCopyUtility: function(cmp, elType) { - const helpTag = cmp.getEl().child('.example-replace-name'), - elTag = cmp.getEl().child('.copy-this') - ; - let nameVal = cmp.previousSibling().getValue(), - tagText - ; - - // If the helptag isn't available, skip here. This may happen when a lexicon is missing or outdated - // and doesn't contain the `example-replace-name` class. - if (!helpTag) { - return; - } - - if (nameVal.length > 0) { - helpTag.update(nameVal); - tagText = elTag.dom.innerText; - } - - helpTag.on({ - click: function() { - nameVal = cmp.previousSibling().getValue(); - if (nameVal.length > 0) { - tagText = elTag.dom.innerText; - const tmp = document.createElement('textarea'); - tmp.value = tagText; - document.body.appendChild(tmp); - tmp.select(); - if (document.execCommand('copy')) { - const feedback = document.createElement('span'); - feedback.className = 'element-panel feedback item-copied'; - feedback.textContent = _(elType+'_tag_copied'); - elTag.insertSibling(feedback, 'after'); - setTimeout(function(){ - feedback.style.opacity = 0; - setTimeout(function(){ - feedback.remove(); - }, 1200); - }, 10); - } - tmp.remove(); - } - } - }); - } - /** * @property {Function} onChangeStaticSource - Updates the static file field based * on the chosen source. diff --git a/manager/assets/modext/widgets/core/modx.window.js b/manager/assets/modext/widgets/core/modx.window.js index b501cfb7b3d..c4e751289d3 100644 --- a/manager/assets/modext/widgets/core/modx.window.js +++ b/manager/assets/modext/widgets/core/modx.window.js @@ -115,8 +115,40 @@ Ext.override(Ext.Window, { MODx.Window = function(config) { config = config || {}; this.isSmallScreen = Ext.getBody().getViewSize().height <= 768; + /* + Update boolean modxFbarHas[___]SaveSwitch properties for later use + */ + if (config.hasOwnProperty('modxFbarSaveSwitches') && config.modxFbarSaveSwitches.length > 0) { + config.modxFbarSaveSwitches.forEach(saveSwitch => { + saveSwitch = saveSwitch[0].toUpperCase() + saveSwitch.slice(1); + const configKey = `modxFbarHas${saveSwitch}Switch`; + config[configKey] = true; + }); + + } + /* + Setup the standard system footer bar if fbar and buttons properties are empty. + Note that buttons overrides fbar and can be used to specify a customized + set of window buttons. + */ + if (!config.hasOwnProperty('fbar') && (!config.hasOwnProperty('buttons') || config.buttons.length == 0)) { + const footerBar = this.getWindowFbar(config); + if (footerBar) { + config.buttonAlign = 'left'; + config.fbar = footerBar; + } + } Ext.applyIf(config,{ modal: false + + ,modxFbarHasClearCacheSwitch: false + ,modxFbarHasDuplicateValuesSwitch: false + ,modxFbarHasRedirectSwitch: false + + ,modxFbarButtons: config.modxFbarButtons || 'c-s' + ,modxFbarSaveSwitches: [] + ,modxPseudoModal: false + ,layout: 'auto' ,closeAction: 'hide' ,shadow: true @@ -130,6 +162,7 @@ MODx.Window = function(config) { ,constrain: true ,constrainHeader: true ,cls: 'modx-window' + /* ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this @@ -140,6 +173,7 @@ MODx.Window = function(config) { ,scope: this ,handler: this.submit }] + */ ,record: {} ,keys: [{ key: Ext.EventObject.ENTER @@ -151,15 +185,34 @@ MODx.Window = function(config) { } else { this.submit(); } - } ,scope: this }] + ,tools: [{ + id: 'gear', + title: 'Window Settings', + // href: '#' + menu: { + xtype: 'menu', + anchor: true, + items: [ + { + xtype: 'menucheckitem', + text: 'Remove Masks', + checked: true + // bind: '{indented}' + }, { + text: 'Disabled Item', + disabled: true, + separator: true + } + ] + } + }] }); MODx.Window.superclass.constructor.call(this,config); this.options = config; this.config = config; - this.addEvents({ success: true ,failure: true @@ -167,16 +220,87 @@ MODx.Window = function(config) { ,updateWindow: false }); this._loadForm(); - this.on('show',function() { - if (this.config.blankValues) { this.fp.getForm().reset(); } - if (this.config.allowDrop) { this.loadDropZones(); } - this.syncSize(); - this.focusFirstField(); - },this); - this.on('afterrender', function() { - this.originalHeight = this.el.getHeight(); - this.toolsHeight = this.originalHeight - this.body.getHeight() + 50; - this.resizeWindow(); + this.on({ + render: function() { + console.log('window render, this:', this); + if (MODx.config.enable_overlays) { + if (this.modxPseudoModal) { + if (MODx.openPseudoModals.length === 0) { + MODx.mask = this.container.createChild({cls:'ext-el-mask clickthrough'}, this.el.dom); + MODx.mask.setStyle('backgroundColor', overlayCssColorNonblocking); + // console.log('render, dynamic mask color: ', overlayCssColorNonblocking); + MODx.mask.hide(); + MODx.mask.resizeMask = function() { + // console.log('window resized!'); + MODx.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true)); + }; + // console.log('custom mask el: ', MODx.mask); + window.addEventListener('resize', MODx.mask.resizeMask); + } + MODx.openPseudoModals.push({ + modalId: this.itemId + }); + // console.log('open modxPseudoModals: ',MODx.openPseudoModals); + } + if (this.modal) { + console.log('rendering real modal...'); + } + } + }, + afterrender: function() { + this.originalHeight = this.el.getHeight(); + this.toolsHeight = this.originalHeight - this.body.getHeight() + 50; + this.resizeWindow(); + }, + beforeShow: function() { + if (this.modxPseudoModal && !MODx.mask.isVisible()) { + Ext.getBody().addClass('x-body-masked'); + MODx.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true)); + MODx.mask.show(); + } + }, + show: function() { + // console.log('showing a modxPseudoModal...'); + // console.log(`modxPseudoModal opacity: ${overlayOpacityNonblocking}`); + if (this.modxPseudoModal && MODx.mask.isVisible()) { + setTimeout(function() { + MODx.mask.setStyle('opacity', overlayOpacityNonblocking); + // MODx.mask.addClass('fade-in'); + }, 250); + } + // console.log('show, mask color: ', MODx.mask.getColor('backgroundColor')); + if (this.config.blankValues) { + this.fp.getForm().reset(); + } + if (this.config.allowDrop) { + this.loadDropZones(); + } + this.syncSize(); + this.focusFirstField(); + }, + beforehide: function() { + if (this.modxPseudoModal && MODx.mask && MODx.openPseudoModals.length === 1) { + MODx.mask.removeClass('fade-in'); + } + }, + hide: function() { + if (this.modxPseudoModal) { + if (MODx.openPseudoModals.length > 1) { + MODx.openPseudoModals.forEach((modxPseudoModal, i) => { + if (modxPseudoModal.modalId == this.itemId) { + MODx.openPseudoModals.splice(i, 1); + } + }); + } else { + MODx.openPseudoModals = []; + MODx.mask.hide(); + MODx.mask.remove(); + Ext.getBody().removeClass('x-body-masked'); + window.removeEventListener('resize', MODx.mask.resizeMask); + } + // console.log('hide, openPseudoModals: ', MODx.openPseudoModals); + } + } }); Ext.EventManager.onWindowResize(this.resizeWindow, this); }; @@ -199,6 +323,60 @@ Ext.extend(MODx.Window,Ext.Window,{ } } } + + /* + When a switch is rendered in the footer bar, we need to + insert a hidden field in the form to to be able to relay its value to + the processor + */ + if (this.config.hasOwnProperty('modxFbarSaveSwitches') && this.config.modxFbarSaveSwitches.length > 0) { + this.config.modxFbarSaveSwitches.forEach(saveSwitch => { + switch (saveSwitch) { + case 'redirect': + defaultValue = this.config.redirect ; + break; + case 'duplicateValues': + defaultValue = 0; + break; + default: + defaultValue = 1; + + } + this.setFbarSwitchHiddenField(saveSwitch, defaultValue); + }); + + } + console.log('final fields: ', this.config.fields); + /* + if (this.modxFbarHasClearCacheSwitch) { + // console.log('adding hidden cache switch...'); + const switchId = `${this.id}-clearcache`, + switchCmp = Ext.getCmp(switchId) + ; + if (switchCmp) { + this.config.fields.push({ + xtype: 'hidden' + ,name: 'clearCache' + ,id: `${switchId}-hidden` + ,value: 1 + }); + } + } + if (this.modxFbarHasRedirectSwitch) { + // console.log('adding hidden redirect switch..., default val: ',this.config.redirect); + const switchId = `${this.id}-redirect`, + switchCmp = Ext.getCmp(switchId) + ; + if (switchCmp) { + this.config.fields.push({ + xtype: 'hidden' + ,name: 'redirect' + ,id: `${switchId}-hidden` + ,value: this.config.redirect ? 1 : 0 + }); + } + } + */ this.fp = this.createForm({ url: this.config.url ,baseParams: this.config.baseParams || { action: this.config.action || '' } @@ -219,6 +397,7 @@ Ext.extend(MODx.Window,Ext.Window,{ if (fld) { fld.focus(false,200); } } } + ,findFirstTextField: function(i) { i = i || 0; var fld = this.fp.getForm().items.itemAt(i); @@ -234,6 +413,10 @@ Ext.extend(MODx.Window,Ext.Window,{ close = close === false ? false : true; var f = this.fp.getForm(); if (f.isValid() && this.fireEvent('beforeSubmit',f.getValues())) { + // console.log('window form submit, this:', this); + // console.log('window form submit, form:', f); + console.log('window form submit, form vals:', f.getValues()); + // return false; f.submit({ waitMsg: this.config.waitMsg || _('saving') ,submitEmptyText: this.config.submitEmptyText !== false @@ -276,6 +459,7 @@ Ext.extend(MODx.Window,Ext.Window,{ ,errorReader: MODx.util.JSONReader ,defaults: this.config.formDefaults || { msgTarget: this.config.msgTarget || 'under' + ,anchor: '100%' } ,url: this.config.url ,baseParams: this.config.baseParams || {} @@ -313,6 +497,7 @@ Ext.extend(MODx.Window,Ext.Window,{ if (r === null) { return false; } this.fp.getForm().setValues(r); } + ,reset: function() { this.fp.getForm().reset(); } @@ -359,5 +544,141 @@ Ext.extend(MODx.Window,Ext.Window,{ el.setHeight('auto'); } } + + /** + * + */ + ,setFbarSwitchHiddenField: function(fbarSwitchFieldName, defaultValue = 1) { + + const switchId = `${this.id}-${fbarSwitchFieldName}`, + switchCmp = Ext.getCmp(switchId) + ; + if (switchCmp) { + this.config.fields.push({ + xtype: 'hidden', + name: fbarSwitchFieldName, + id: `${switchId}-hidden`, + value: defaultValue + }); + } + } + + /** + * + */ + ,getFbarSwitch: function(windowId, fbarSwitchFieldName, switchLabel, switchIsChecked = true) { + + const switchCmp = { + xtype: 'xcheckbox', + id: `${windowId}-${fbarSwitchFieldName}`, + hideLabel: true, + boxLabel: switchLabel, + inputValue: 1, + checked: switchIsChecked, + listeners: { + check: { + fn: function(cmp) { + const hiddenCmp = Ext.getCmp(`${windowId}-${fbarSwitchFieldName}-hidden`); + if (hiddenCmp) { + const value = cmp.getValue() === false ? 0 : 1; + hiddenCmp.setValue(value); + } + }, + scope: this + } + } + }; + // console.log(`getting switch (${fbarSwitchFieldName}): `, switchCmp); + return switchCmp; + } + + /** + * + */ + ,getSaveButton: function(config, isPrimaryButton = true, isSaveAndClose = false) { + // console.log('getSaveButton, this', this); + const defaultBtnText = isSaveAndClose ? _('save_and_close') : _('save') ; + let btn; + if (isPrimaryButton) { + // console.log('modxFbarButtons: ',config.modxFbarButtons); + // console.log('isPrimaryButton, config.saveBtnText: ',config.saveBtnText); + // console.log('isPrimaryButton, isSaveAndClose: ',isSaveAndClose); + btn = { + text: config.saveBtnText || defaultBtnText, + cls: 'primary-button', + handler: this.submit, + scope: this + }; + } else { + btn = { + text: config.saveBtnText || defaultBtnText, + handler: function() { + this.submit(false); + }, + scope: this + }; + } + // console.log('getSaveButton, btn:', btn); + return btn; + } + + /** + * + */ + ,getWindowButtons: function(config) { + const btns = [{ + text: config.cancelBtnText || _('cancel'), + handler: function() { + this.config.closeAction !== 'close' ? this.hide() : this.close(); + }, + scope: this + }], + specification = config.modxFbarButtons || 'c-s' + ; + switch(specification) { + case 'c-s': + btns.push(this.getSaveButton(config)); + break; + case 'c-s-sc': + btns.push(this.getSaveButton(config, false)); + btns.push(this.getSaveButton(config, true, true)); + break; + case 'custom': + break; + } + return btns; + } + + /** + * + */ + ,getWindowFbar: function(config) { + // console.log('getting window fbar...'); + const windowId = config.id, + windowButtons = this.getWindowButtons(config), + footerBar = [] + ; + if (config.modxFbarHasClearCacheSwitch) { + const cacheSwitch = this.getFbarSwitch(windowId, 'clearCache', _('clear_cache_on_save')); + footerBar.push(cacheSwitch); + } + if (config.modxFbarHasDuplicateValuesSwitch) { + const dupValuesSwitch = this.getFbarSwitch(windowId, 'duplicateValues', _('element_duplicate_values'), false); + footerBar.push(dupValuesSwitch); + } + if (config.modxFbarHasRedirectSwitch) { + const redirectSwitch = this.getFbarSwitch(windowId, 'redirect', _('duplicate_redirect'), config.redirect); + footerBar.push(redirectSwitch); + } + footerBar.push('->'); + if (windowButtons && windowButtons.length > 0) { + windowButtons.forEach(button => { + footerBar.push(button); + }); + } + + return footerBar; + } + }); Ext.reg('modx-window',MODx.Window); diff --git a/manager/assets/modext/widgets/element/modx.panel.tv.js b/manager/assets/modext/widgets/element/modx.panel.tv.js index df1ca2c80f9..24336f92101 100644 --- a/manager/assets/modext/widgets/element/modx.panel.tv.js +++ b/manager/assets/modext/widgets/element/modx.panel.tv.js @@ -110,7 +110,7 @@ MODx.panel.TV = function(config) { ,listeners: { afterrender: { fn: function(cmp) { - this.insertTagCopyUtility(cmp, 'tv'); + MODx.util.insertTagCopyUtility(cmp, 'tv'); } ,scope: this } diff --git a/manager/assets/modext/widgets/resource/modx.tree.resource.js b/manager/assets/modext/widgets/resource/modx.tree.resource.js index c656574c156..bdf2fad8369 100644 --- a/manager/assets/modext/widgets/resource/modx.tree.resource.js +++ b/manager/assets/modext/widgets/resource/modx.tree.resource.js @@ -853,6 +853,8 @@ MODx.window.QuickCreateResource = function(config) { ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Resource/Create' + ,cls: 'qce-window qce-create' + ,modxPseudoModal: true ,fields: [{ xtype: 'modx-tabs' ,bodyStyle: { background: 'transparent' } @@ -1014,6 +1016,7 @@ MODx.window.QuickUpdateResource = function(config) { title: _('quick_update_resource') ,id: this.ident ,action: 'Resource/Update' + ,cls: 'qce-window qce-update' ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this diff --git a/manager/assets/modext/widgets/resource/modx.window.resource.js b/manager/assets/modext/widgets/resource/modx.window.resource.js index 66f91b4a4de..31484211f5d 100644 --- a/manager/assets/modext/widgets/resource/modx.window.resource.js +++ b/manager/assets/modext/widgets/resource/modx.window.resource.js @@ -72,6 +72,8 @@ MODx.window.CreateResource = function(config = {}) { action: 'Resource/Create' }, width: 600, + cls: 'qce-window qce-create', + modxPseudoModal: true, fields: [{ xtype: 'textfield', fieldLabel: _('resource_pagetitle'), @@ -321,7 +323,7 @@ Ext.extend(MODx.panel.TemplatePreview, Ext.Panel, { } else { this.removeClass('x-form-template-preview-empty'); - var html = '' + record.data.templatename + ''; + var html = '' + record.data.templatename + ''; } this.add({ diff --git a/manager/assets/modext/widgets/system/modx.tree.directory.js b/manager/assets/modext/widgets/system/modx.tree.directory.js index 77a154bfc24..39b3221af83 100644 --- a/manager/assets/modext/widgets/system/modx.tree.directory.js +++ b/manager/assets/modext/widgets/system/modx.tree.directory.js @@ -781,6 +781,8 @@ MODx.window.CreateDirectory = function(config) { title: _('file_folder_create') ,url: MODx.config.connector_url ,action: 'Browser/Directory/Create' + ,cls: 'qce-window qce-create' + ,modxPseudoModal: true ,fields: [{ xtype: 'hidden' ,name: 'wctx' @@ -792,14 +794,12 @@ MODx.window.CreateDirectory = function(config) { fieldLabel: _('name') ,name: 'name' ,xtype: 'textfield' - ,anchor: '100%' ,allowBlank: false },{ fieldLabel: _('file_folder_parent') ,id: 'folder-parent' ,name: 'parent' ,xtype: 'textfield' - ,anchor: '100%' },{ xtype: 'label' ,forId: 'folder-parent' @@ -826,6 +826,8 @@ MODx.window.SetVisibility = function(config) { title: _('file_folder_visibility') ,url: MODx.config.connector_url ,action: 'Browser/Visibility' + ,cls: 'qce-window qce-rename' + ,modxPseudoModal: true ,fields: [{ xtype: 'hidden' ,name: 'wctx' @@ -834,23 +836,19 @@ MODx.window.SetVisibility = function(config) { xtype: 'hidden' ,name: 'source' },{ - name: 'path' + xtype: 'statictextfield' + ,name: 'path' ,fieldLabel: _('file_folder_path') - ,xtype: 'statictextfield' - ,anchor: '100%' ,submitValue: true },{ - fieldLabel: _('file_folder_visibility_label') + xtype: 'modx-combo-visibility' ,name: 'visibility' - ,xtype: 'modx-combo-visibility' - ,anchor: '100%' + ,fieldLabel: _('file_folder_visibility_label') ,allowBlank: false },{ - hideLabel: true - ,xtype: 'displayfield' - ,value: _('file_folder_visibility_desc') - ,anchor: '100%' - ,allowBlank: false + xtype: 'box' + ,html: _('file_folder_visibility_desc') + ,cls: 'desc-under' }] }); MODx.window.SetVisibility.superclass.constructor.call(this,config); @@ -872,6 +870,8 @@ MODx.window.RenameDirectory = function(config) { title: _('rename') ,url: MODx.config.connector_url ,action: 'Browser/Directory/Rename' + ,cls: 'qce-window qce-rename' + ,modxPseudoModal: true ,fields: [{ xtype: 'hidden' ,name: 'wctx' @@ -880,21 +880,18 @@ MODx.window.RenameDirectory = function(config) { xtype: 'hidden' ,name: 'source' },{ - fieldLabel: _('path') + xtype: 'statictextfield' ,name: 'path' - ,xtype: 'statictextfield' + ,fieldLabel: _('path') ,submitValue: true - ,anchor: '100%' },{ - fieldLabel: _('old_name') + xtype: 'statictextfield' ,name: 'old_name' - ,xtype: 'statictextfield' - ,anchor: '100%' + ,fieldLabel: _('old_name') },{ - fieldLabel: _('new_name') + xtype: 'textfield' ,name: 'name' - ,xtype: 'textfield' - ,anchor: '100%' + ,fieldLabel: _('new_name') ,allowBlank: false }] }); @@ -917,6 +914,8 @@ MODx.window.RenameFile = function(config) { title: _('rename') ,url: MODx.config.connector_url ,action: 'Browser/File/Rename' + ,cls: 'qce-window qce-rename' + ,modxPseudoModal: true ,fields: [{ xtype: 'hidden' ,name: 'wctx' @@ -925,25 +924,22 @@ MODx.window.RenameFile = function(config) { xtype: 'hidden' ,name: 'source' },{ - fieldLabel: _('path') + xtype: 'hidden' + ,name: 'dir' + },{ + xtype: 'statictextfield' ,name: 'path' - ,xtype: 'statictextfield' + ,fieldLabel: _('path') ,submitValue: true - ,anchor: '100%' },{ - fieldLabel: _('old_name') + xtype: 'statictextfield' ,name: 'old_name' - ,xtype: 'statictextfield' - ,anchor: '100%' + ,fieldLabel: _('old_name') },{ - fieldLabel: _('new_name') + xtype: 'textfield' ,name: 'name' - ,xtype: 'textfield' - ,anchor: '100%' + ,fieldLabel: _('new_name') ,allowBlank: false - },{ - name: 'dir' - ,xtype: 'hidden' }] }); MODx.window.RenameFile.superclass.constructor.call(this,config); @@ -964,9 +960,11 @@ MODx.window.QuickUpdateFile = function(config) { Ext.applyIf(config,{ title: _('file_quick_update') ,width: 600 - ,layout: 'anchor' + // ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Browser/File/Update' + ,cls: 'qce-window qce-update' + ,modxPseudoModal: true ,fields: [{ xtype: 'hidden' ,name: 'wctx' @@ -978,21 +976,18 @@ MODx.window.QuickUpdateFile = function(config) { xtype: 'hidden' ,name: 'file' },{ - fieldLabel: _('name') + xtype: 'statictextfield' ,name: 'name' - ,xtype: 'statictextfield' - ,anchor: '100%' + ,fieldLabel: _('name') },{ - fieldLabel: _('path') + xtype: 'statictextfield' ,name: 'path' - ,xtype: 'statictextfield' - ,anchor: '100%' + ,fieldLabel: _('path') },{ - fieldLabel: _('content') - ,xtype: 'textarea' + xtype: 'textarea' ,name: 'content' - ,anchor: '100%' - ,height: 200 + ,fieldLabel: _('content') + ,minGrow: 200 }] ,keys: [{ key: Ext.EventObject.ENTER @@ -1033,9 +1028,11 @@ MODx.window.QuickCreateFile = function(config) { Ext.applyIf(config,{ title: _('file_quick_create') ,width: 600 - ,layout: 'anchor' + // ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Browser/File/Create' + ,cls: 'qce-window qce-create' + ,modxPseudoModal: true ,fields: [{ xtype: 'hidden' ,name: 'wctx' @@ -1044,27 +1041,24 @@ MODx.window.QuickCreateFile = function(config) { xtype: 'hidden' ,name: 'source' },{ - fieldLabel: _('directory') + xtype: 'statictextfield' ,name: 'directory' + ,fieldLabel: _('directory') ,submitValue: true - ,xtype: 'statictextfield' - ,anchor: '100%' },{ xtype: 'label' ,html: _('file_folder_parent_desc') ,cls: 'desc-under' },{ - fieldLabel: _('name') + xtype: 'textfield' ,name: 'name' - ,xtype: 'textfield' - ,anchor: '100%' + ,fieldLabel: _('name') ,allowBlank: false },{ - fieldLabel: _('content') - ,xtype: 'textarea' + xtype: 'textarea' ,name: 'content' - ,anchor: '100%' - ,height: 200 + ,fieldLabel: _('content') + ,minGrow: 200 }] ,keys: [{ key: Ext.EventObject.ENTER diff --git a/manager/assets/modext/widgets/windows.js b/manager/assets/modext/widgets/windows.js index 8308c8d9c9b..125bc31c80d 100644 --- a/manager/assets/modext/widgets/windows.js +++ b/manager/assets/modext/widgets/windows.js @@ -7,11 +7,14 @@ * @xtype modx-window-resource-duplicate */ MODx.window.DuplicateResource = function(config) { + config = config || {}; - this.ident = config.ident || 'dupres'+Ext.id(); + const windowId = `window-dup-resource-${Ext.id()}`; + Ext.applyIf(config,{ - title: config.pagetitle ? _('duplicate') + ' ' + config.pagetitle : _('duplication_options') - ,id: this.ident + id: windowId + ,title: config.pagetitle ? `${_('duplicate')} ${config.pagetitle}` : _('duplication_options') + ,modxPseudoModal: true }); MODx.window.DuplicateResource.superclass.constructor.call(this,config); }; @@ -28,30 +31,26 @@ Ext.extend(MODx.window.DuplicateResource,MODx.Window,{ var items = []; items.push({ xtype: 'textfield' - ,id: 'modx-'+this.ident+'-name' - ,fieldLabel: _('resource_name_new') ,name: 'name' - ,anchor: '100%' + ,fieldLabel: _('resource_name_new') ,value: '' }); if (this.config.hasChildren) { items.push({ xtype: 'xcheckbox' + ,name: 'duplicate_children' ,boxLabel: _('duplicate_children') + ' ('+this.config.childCount+')' ,hideLabel: true - ,name: 'duplicate_children' - ,id: 'modx-'+this.ident+'-duplicate-children' ,checked: true }); } items.push({ xtype: 'xcheckbox' + ,name: 'redirect' ,boxLabel: _('duplicate_redirect') ,hideLabel: true - ,name: 'redirect' - ,id: 'modx-'+this.ident+'-duplicate-redirect' ,checked: this.config.redirect }); @@ -65,19 +64,19 @@ Ext.extend(MODx.window.DuplicateResource,MODx.Window,{ ,columns: 1 ,value: pov ,items: [{ - boxLabel: _('po_make_all_unpub') + name: 'published_mode' + ,boxLabel: _('po_make_all_unpub') ,hideLabel: true - ,name: 'published_mode' ,inputValue: 'unpublish' },{ - boxLabel: _('po_make_all_pub') + name: 'published_mode' + ,boxLabel: _('po_make_all_pub') ,hideLabel: true - ,name: 'published_mode' ,inputValue: 'publish' },{ - boxLabel: _('po_preserve') + name: 'published_mode' + ,boxLabel: _('po_preserve') ,hideLabel: true - ,name: 'published_mode' ,inputValue: 'preserve' }] }] @@ -90,9 +89,6 @@ Ext.extend(MODx.window.DuplicateResource,MODx.Window,{ ,id: this.config.resource ,prefixDuplicate: true } - ,labelWidth: 125 - ,defaultType: 'textfield' - ,autoHeight: true ,items: items }); @@ -110,121 +106,190 @@ Ext.reg('modx-window-resource-duplicate',MODx.window.DuplicateResource); * @xtype modx-window-element-duplicate */ MODx.window.DuplicateElement = function(config) { + config = config || {}; - this.ident = config.ident || 'dupeel-'+Ext.id(); - var flds = [{ + const windowId = `window-dup-element-${Ext.id()}`, + staticFileCmpId = `${windowId}-modx-static_file`, + nameFieldName = config.record.type == 'template' ? 'templatename' : 'name' , + createExampleTag = ['tv', 'chunk', 'snippet'].includes(config.record.type), + defaultExampleTag = createExampleTag ? _(`example_tag_${config.record.type}_name`) : '' , + elementNameCmpId = `${windowId}-modx-name`, + nameFieldListeners = { + change: function(cmp) { + cmp.setValue(cmp.getValue().trim()); + } + }, + nameHelpListeners = {} + ; + if (createExampleTag) { + Object.assign(nameHelpListeners, { + afterrender: function(cmp) { + MODx.util.insertTagCopyUtility(cmp, config.record.type); + } + }); + } + // console.log('record:', config.record); + // console.log('name field listeners: ',nameFieldListeners); + const flds = [{ xtype: 'hidden' ,name: 'id' - ,id: 'modx-'+this.ident+'-id' },{ xtype: 'hidden' ,name: 'source' - ,id: 'modx-'+this.ident+'-source' },{ xtype: 'textfield' - ,fieldLabel: _('element_name_new') - ,name: config.record.type == 'template' ? 'templatename' : 'name' - ,id: 'modx-'+this.ident+'-name' - ,anchor: '100%' + ,name: nameFieldName + ,id: elementNameCmpId + ,fieldLabel: _(`${config.record.type}_new_name`) || _('element_name_new') ,enableKeyEvents: true - ,listeners: { - 'afterRender' : {scope:this,fn:function(f,e) { - this.setStaticElementsPath(f); - }}, - 'keyup': {scope:this,fn:function(f,e) { - this.setStaticElementsPath(f); - }} - } + ,allowBlank: false + ,listeners: nameFieldListeners + ,value: config.record.name + },{ + xtype: 'box' + ,hidden: MODx.expandHelp ? false : true + ,html: createExampleTag + ? _(`${config.record.type}_name_desc`, { + tag: `[[*${defaultExampleTag}]]` + }) + : _(`${config.record.type}_name_desc`) || '' + ,cls: 'desc-under' + ,listeners: nameHelpListeners }]; if (config.record.type == 'tv') { flds.push({ xtype: 'textfield' - ,fieldLabel: _('element_caption_new') ,name: 'caption' - ,id: 'modx-'+this.ident+'-caption' - ,anchor: '100%' - }); - flds.push({ - xtype: 'xcheckbox' - ,hideLabel: true - ,boxLabel: _('element_duplicate_values') - ,labelSeparator: '' - ,name: 'duplicateValues' - ,id: 'modx-'+this.ident+'-duplicate-values' - ,anchor: '100%' - ,inputValue: 1 - ,checked: false + ,fieldLabel: _(`tv_new_caption`) || _('element_caption_new') + ,value: config.record.caption + },{ + xtype: 'box' + ,hidden: MODx.expandHelp ? false : true + ,html: _('tv_caption_desc') + ,cls: 'desc-under' }); } if (config.record.static === true) { flds.push({ xtype: 'textfield' - ,fieldLabel: _('static_file') ,name: 'static_file' - ,id: 'modx-'+this.ident+'-static_file' - ,anchor: '100%' + ,id: staticFileCmpId + ,fieldLabel: _('static_file') + ,listeners: { + change: { + fn: function(cmp) { + const file = cmp.getValue().trim(); + if (!Ext.isEmpty(file)) { + const fileName = + cmp.setValue(MODx.util.Format.fileFullPath(file)); + } + }, + scope: this + } + } + },{ + xtype: 'box' + ,hidden: MODx.expandHelp ? false : true + ,html: _('static_file_desc') + ,cls: 'desc-under' }); } - flds.push({ - xtype: 'xcheckbox' - ,boxLabel: _('duplicate_redirect') - ,hideLabel: true - ,name: 'redirect' - ,id: 'modx-'+this.ident+'-duplicate-redirect' - ,checked: config.redirect - }); - Ext.applyIf(config,{ - title: _('duplicate_'+config.record.type) + id: windowId + ,title: _('duplicate_'+config.record.type) ,url: MODx.config.connector_url - ,action: 'element/'+config.record.type+'/duplicate' + ,action: `element/${config.record.type}/duplicate` ,width: 600 ,fields: flds ,labelWidth: 150 + ,modxPseudoModal: true + ,modxFbarSaveSwitches: config.record.type == 'tv' ? ['duplicateValues', 'redirect'] : ['redirect'] }); MODx.window.DuplicateElement.superclass.constructor.call(this,config); -}; + if (this.config.record.static) { + + const elementAutomationType = `${this.config.record.type}s`, + staticsAutomationConfigKey = `static_elements_automate_${elementAutomationType}` + ; + this.staticsAutomated = MODx.config[staticsAutomationConfigKey] ? true : false ; + + if (this.staticsAutomated) { + const elementCategory = this.config.record.category || 0; + this.staticElementType = elementAutomationType; + this.getElementCategoryName(elementCategory); + } else { + const currentPath = this.config.record.static_file, + fileName = currentPath.indexOf('/') !== -1 ? currentPath.split('/').pop() : currentPath, + fileExt = fileName.indexOf('.') !== -1 ? fileName.slice(fileName.lastIndexOf('.')) : '' + ; + this.staticElementBasePath = currentPath.replace(fileName, ''); + this.staticElementFileExt = fileExt; + } + Ext.getCmp(elementNameCmpId).on({ + afterrender: { + fn: function(cmp) { + const elementName = cmp.getValue().trim() || this.config.record.name; + let path; + if (this.staticsAutomated) { + path = MODx.getStaticElementsPath(elementName, this.staticElementCategoryName, this.staticElementType); + } else { + path = MODx.util.Format.staticElementPathFragment(elementName); + path = `${this.staticElementBasePath}${path}${this.staticElementFileExt}`; + } + Ext.getCmp(staticFileCmpId).setValue(path); + }, + scope: this, + delay: 250 + }, + keyup: { + fn: function(cmp, e) { + const elementName = cmp.getValue().trim(); + let path; + if (this.staticsAutomated) { + path = MODx.getStaticElementsPath(elementName, this.staticElementCategoryName, this.staticElementType); + } else { + path = MODx.util.Format.staticElementPathFragment(elementName); + path = `${this.staticElementBasePath}${path}${this.staticElementFileExt}`; + } + Ext.getCmp(staticFileCmpId).setValue(path); + }, + scope: this + } + }); + } + +}; Ext.extend(MODx.window.DuplicateElement,MODx.Window, { - setStaticElementsPath: function(f) { - if (this.config.record.static === true) { - var category = this.config.record.category; - if (typeof category !== 'number') { - if (Ext.getCmp('modx-' + this.config.record.type + '-category').getValue() > 0) { - category = Ext.getCmp('modx-' + this.config.record.type + '-category').lastSelectionText; - } + getElementCategoryName: function(categoryId) { - var path = MODx.getStaticElementsPath(f.getValue(), category, this.config.record.type + 's'); - Ext.getCmp('modx-' + this.ident + '-static_file').setValue(path); - } else { - // If category is set but is a number, retrieve full category name. - if (typeof category === "number" && category > 0) { - MODx.Ajax.request({ - url: MODx.config.connector_url - ,params: { - action: 'Element/Category/GetList' - ,id: category - } - ,listeners: { - 'success': {fn:function(response) { - for (var i = 0; i < response.results.length; i++) { - if (response.results[i].id === category) { - category = response.results[i].name; - } + if (typeof categoryId === 'number' && categoryId > 0) { + MODx.Ajax.request({ + url: MODx.config.connector_url + ,params: { + action: 'Element/Category/GetList' + ,id: categoryId + } + ,listeners: { + success: { + fn: function(response) { + response.results.forEach(result => { + if (result.id === categoryId) { + this.staticElementCategoryName = result.name; } - - var path = MODx.getStaticElementsPath(f.getValue(), category, this.config.record.type + 's'); - Ext.getCmp('modx-' + this.ident + '-static_file').setValue(path); - },scope:this} - } - }); + }); + }, + scope: this + } } - } + }); + } else { + this.staticElementCategoryName = ''; } } }); @@ -232,34 +297,32 @@ Ext.reg('modx-window-element-duplicate',MODx.window.DuplicateElement); MODx.window.CreateCategory = function(config) { config = config || {}; - this.ident = config.ident || 'ccat'+Ext.id(); + // this.ident = config.ident || 'ccat'+Ext.id(); Ext.applyIf(config,{ title: _('category_create') ,id: this.ident ,url: MODx.config.connector_url ,action: 'Element/Category/Create' + ,modxPseudoModal: true ,fields: [{ xtype: 'modx-description' ,html: _('category_create_desc') },{ fieldLabel: _('name') ,name: 'category' - ,id: 'modx-'+this.ident+'-category' + // ,id: 'modx-'+this.ident+'-category' ,xtype: 'textfield' - ,anchor: '100%' },{ fieldLabel: _('parent') ,name: 'parent' ,hiddenName: 'parent' - ,id: 'modx-'+this.ident+'-parent' + // ,id: 'modx-'+this.ident+'-parent' ,xtype: 'modx-combo-category' - ,anchor: '100%' },{ fieldLabel: _('rank') ,name: 'rank' - ,id: 'modx-'+this.ident+'-rank' + // ,id: 'modx-'+this.ident+'-rank' ,xtype: 'numberfield' - ,anchor: '100%' }] }); MODx.window.CreateCategory.superclass.constructor.call(this,config); @@ -294,13 +357,11 @@ MODx.window.RenameCategory = function(config) { ,id: 'modx-'+this.ident+'-category' ,width: 150 ,value: config.record.category - ,anchor: '100%' },{ fieldLabel: _('rank') ,name: 'rank' ,id: 'modx-'+this.ident+'-rank' ,xtype: 'numberfield' - ,anchor: '100%' }] }); MODx.window.RenameCategory.superclass.constructor.call(this,config); @@ -381,18 +442,20 @@ Ext.reg('modx-window-namespace-update',MODx.window.UpdateNamespace); MODx.window.QuickCreateChunk = function(config) { + config = config || {}; - this.ident = config.ident || `qcechunk${Ext.id()}`; + const windowId = `window-qce-chunk-${Ext.id()}`; Ext.applyIf(config,{ - title: _('quick_create_chunk') + id: windowId + ,title: _('quick_create_chunk') ,width: 700 ,layout: 'form' ,url: MODx.config.connector_url ,action: 'Element/Chunk/Create' ,cls: 'qce-window qce-create' - ,modal: true - ,monitorResize: true + ,modxPseudoModal: true + ,modxFbarSaveSwitches: ['clearCache'] ,fields: [{ xtype: 'hidden' ,name: 'id' @@ -434,13 +497,11 @@ MODx.window.QuickCreateChunk = function(config) { ,items: [{ xtype: 'modx-combo-category' ,name: 'category' - ,id: `modx-${this.ident}-category` ,fieldLabel: _('category') ,description: MODx.expandHelp ? '' : _('chunk_category_desc') ,value: config.record.category || 0 },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-category` ,html: _('chunk_category_desc') ,cls: 'desc-under' }] @@ -469,7 +530,6 @@ MODx.window.QuickCreateChunk = function(config) { ,items: [{ xtype: 'textarea' ,name: 'description' - ,id: `modx-${this.ident}-description` ,description: MODx.expandHelp ? '' : _('chunk_description_desc') ,fieldLabel: _('description') ,grow: true @@ -478,32 +538,9 @@ MODx.window.QuickCreateChunk = function(config) { ,value: config.record.description || '' },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-description` ,html: _('chunk_description_desc') ,cls: 'desc-under' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,id: `modx-${this.ident}-clear-cache` - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') - ,ctCls: 'add-label-space' - ,inputValue: 1 - ,checked: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-clear-cache` - ,html: _('clear_cache_on_save_desc') - ,cls: 'desc-under toggle-slider-above' - }] }] }] },{ @@ -522,7 +559,6 @@ MODx.window.QuickCreateChunk = function(config) { xtype: 'textarea' ,fieldLabel: _('chunk_code') ,name: 'snippet' - ,id: `modx-${this.ident}-code` ,grow: true ,growMin: 90 ,growMax: this.isSmallScreen ? 160 : 300 @@ -548,20 +584,7 @@ MODx.window.QuickUpdateChunk = function(config) { title: _('quick_update_chunk') ,action: 'Element/Chunk/Update' ,cls: 'qce-window qce-update' - ,buttons: [{ - text: config.cancelBtnText || _('cancel') - ,scope: this - ,handler: function() { this.hide(); } - },{ - text: config.saveBtnText || _('save') - ,scope: this - ,handler: function() { this.submit(false); } - },{ - text: config.saveBtnText || _('save_and_close') - ,cls: 'primary-button' - ,scope: this - ,handler: this.submit - }] + ,modxFbarButtons: 'c-s-sc' }); MODx.window.QuickUpdateChunk.superclass.constructor.call(this,config); }; @@ -570,18 +593,19 @@ Ext.reg('modx-window-quick-update-chunk',MODx.window.QuickUpdateChunk); MODx.window.QuickCreateTemplate = function(config) { + config = config || {}; - this.ident = config.ident || `qcetemplate${Ext.id()}`; + const windowId = `window-qce-template-${Ext.id()}`; Ext.applyIf(config,{ - title: _('quick_create_template') + id: windowId + ,title: _('quick_create_template') ,width: 700 - ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Element/Template/Create' ,cls: 'qce-window qce-create' - ,modal: true - ,monitorResize: true + ,modxPseudoModal: true + ,modxFbarSaveSwitches: ['clearCache'] ,fields: [{ xtype: 'hidden' ,name: 'id' @@ -623,13 +647,11 @@ MODx.window.QuickCreateTemplate = function(config) { ,items: [{ xtype: 'modx-combo-category' ,name: 'category' - ,id: `modx-${this.ident}-category` ,fieldLabel: _('category') ,description: MODx.expandHelp ? '' : _('template_category_desc') ,value: config.record.category || 0 },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-category` ,html: _('template_category_desc') ,cls: 'desc-under' }] @@ -648,7 +670,7 @@ MODx.window.QuickCreateTemplate = function(config) { ,labelAlign: 'top' } ,items: [{ - columnWidth: 0.5 + columnWidth: 1 ,defaults: { anchor: '100%' ,msgTarget: 'under' @@ -658,7 +680,6 @@ MODx.window.QuickCreateTemplate = function(config) { ,items: [{ xtype: 'textarea' ,name: 'description' - ,id: `modx-${this.ident}-description` ,description: MODx.expandHelp ? '' : _('template_description_desc') ,fieldLabel: _('description') ,grow: true @@ -667,32 +688,9 @@ MODx.window.QuickCreateTemplate = function(config) { ,value: config.record.description || '' },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-description` ,html: _('template_description_desc') ,cls: 'desc-under' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,id: `modx-${this.ident}-clear-cache` - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') - ,ctCls: 'add-label-space' - ,inputValue: 1 - ,checked: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-clear-cache` - ,html: _('clear_cache_on_save_desc') - ,cls: 'desc-under toggle-slider-above' - }] }] }] },{ @@ -711,7 +709,6 @@ MODx.window.QuickCreateTemplate = function(config) { xtype: 'textarea' ,fieldLabel: _('template_code') ,name: 'content' - ,id: `modx-${this.ident}-code` ,grow: true ,growMin: 120 ,growMax: this.isSmallScreen ? 160 : 300 @@ -737,20 +734,7 @@ MODx.window.QuickUpdateTemplate = function(config) { title: _('quick_update_template') ,action: 'Element/Template/Update' ,cls: 'qce-window qce-update' - ,buttons: [{ - text: config.cancelBtnText || _('cancel') - ,scope: this - ,handler: function() { this.hide(); } - },{ - text: config.saveBtnText || _('save') - ,scope: this - ,handler: function() { this.submit(false); } - },{ - text: config.saveBtnText || _('save_and_close') - ,cls: 'primary-button' - ,scope: this - ,handler: this.submit - }] + ,modxFbarButtons: 'c-s-sc' }); MODx.window.QuickUpdateTemplate.superclass.constructor.call(this,config); }; @@ -759,18 +743,19 @@ Ext.reg('modx-window-quick-update-template',MODx.window.QuickUpdateTemplate); MODx.window.QuickCreateSnippet = function(config) { + config = config || {}; - this.ident = config.ident || `qcesnippet${Ext.id()}`; + const windowId = `window-qce-snippet-${Ext.id()}`; Ext.applyIf(config,{ - title: _('quick_create_snippet') + id: windowId + ,title: _('quick_create_snippet') ,width: 700 - ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Element/Snippet/Create' ,cls: 'qce-window qce-create' - ,modal: true - ,monitorResize: true + ,modxPseudoModal: true + ,modxFbarSaveSwitches: ['clearCache'] ,fields: [{ xtype: 'hidden' ,name: 'id' @@ -812,13 +797,11 @@ MODx.window.QuickCreateSnippet = function(config) { ,items: [{ xtype: 'modx-combo-category' ,name: 'category' - ,id: `modx-${this.ident}-category` ,fieldLabel: _('category') ,description: MODx.expandHelp ? '' : _('snippet_category_desc') ,value: config.record.category || 0 },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-category` ,html: _('snippet_category_desc') ,cls: 'desc-under' }] @@ -837,7 +820,7 @@ MODx.window.QuickCreateSnippet = function(config) { ,labelAlign: 'top' } ,items: [{ - columnWidth: 0.5 + columnWidth: 1 ,defaults: { anchor: '100%' ,msgTarget: 'under' @@ -847,7 +830,6 @@ MODx.window.QuickCreateSnippet = function(config) { ,items: [{ xtype: 'textarea' ,name: 'description' - ,id: `modx-${this.ident}-description` ,description: MODx.expandHelp ? '' : _('snippet_description_desc') ,fieldLabel: _('description') ,grow: true @@ -856,32 +838,9 @@ MODx.window.QuickCreateSnippet = function(config) { ,value: config.record.description || '' },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-description` ,html: _('snippet_description_desc') ,cls: 'desc-under' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,id: `modx-${this.ident}-clear-cache` - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') - ,ctCls: 'add-label-space' - ,inputValue: 1 - ,checked: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-clear-cache` - ,html: _('clear_cache_on_save_desc') - ,cls: 'desc-under toggle-slider-above' - }] }] }] },{ @@ -926,20 +885,7 @@ MODx.window.QuickUpdateSnippet = function(config) { title: _('quick_update_snippet') ,action: 'Element/Snippet/Update' ,cls: 'qce-window qce-update' - ,buttons: [{ - text: config.cancelBtnText || _('cancel') - ,scope: this - ,handler: function() { this.hide(); } - },{ - text: config.saveBtnText || _('save') - ,scope: this - ,handler: function() { this.submit(false); } - },{ - text: config.saveBtnText || _('save_and_close') - ,cls: 'primary-button' - ,scope: this - ,handler: this.submit - }] + ,modxFbarButtons: 'c-s-sc' }); MODx.window.QuickUpdateSnippet.superclass.constructor.call(this,config); }; @@ -948,15 +894,19 @@ Ext.reg('modx-window-quick-update-snippet',MODx.window.QuickUpdateSnippet); MODx.window.QuickCreatePlugin = function(config) { + config = config || {}; - this.ident = config.ident || `qceplugin${Ext.id()}`; + const windowId = `window-qce-plugin-${Ext.id()}`; Ext.applyIf(config,{ - title: _('quick_create_plugin') + id: windowId + ,title: _('quick_create_plugin') ,width: 700 ,layout: 'anchor' ,url: MODx.config.connector_url ,action: 'Element/Plugin/Create' + ,modxPseudoModal: true + ,modxFbarSaveSwitches: ['clearCache'] ,fields: [{ xtype: 'hidden' ,name: 'id' @@ -998,13 +948,11 @@ MODx.window.QuickCreatePlugin = function(config) { ,items: [{ xtype: 'modx-combo-category' ,name: 'category' - ,id: `modx-${this.ident}-category` ,fieldLabel: _('category') ,description: MODx.expandHelp ? '' : _('plugin_category_desc') ,value: config.record.category || 0 },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-category` ,html: _('plugin_category_desc') ,cls: 'desc-under' }] @@ -1033,7 +981,6 @@ MODx.window.QuickCreatePlugin = function(config) { ,items: [{ xtype: 'textarea' ,name: 'description' - ,id: `modx-${this.ident}-description` ,description: MODx.expandHelp ? '' : _('plugin_description_desc') ,fieldLabel: _('description') ,grow: true @@ -1042,7 +989,6 @@ MODx.window.QuickCreatePlugin = function(config) { ,value: config.record.description || '' },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-description` ,html: _('plugin_description_desc') ,cls: 'desc-under' }] @@ -1055,7 +1001,6 @@ MODx.window.QuickCreatePlugin = function(config) { ,items: [{ xtype: 'xcheckbox' ,name: 'disabled' - ,id: `modx-${this.ident}-disabled` ,hideLabel: true ,boxLabel: _('plugin_disabled') ,description: MODx.expandHelp ? '' : _('plugin_disabled_desc') @@ -1064,23 +1009,8 @@ MODx.window.QuickCreatePlugin = function(config) { ,checked: config.record.disabled || 0 },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-disabled` ,html: _('plugin_disabled_desc') ,cls: 'desc-under toggle-slider-above' - },{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,id: `modx-${this.ident}-clear-cache` - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') - ,inputValue: 1 - ,checked: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-clear-cache` - ,html: _('clear_cache_on_save_desc') - ,cls: 'desc-under toggle-slider-above' }] }] }] @@ -1100,7 +1030,6 @@ MODx.window.QuickCreatePlugin = function(config) { xtype: 'textarea' ,fieldLabel: _('plugin_code') ,name: 'plugincode' - ,id: `modx-${this.ident}-code` ,grow: true ,growMin: 90 ,growMax: this.isSmallScreen ? 160 : 300 @@ -1126,20 +1055,7 @@ MODx.window.QuickUpdatePlugin = function(config) { title: _('quick_update_plugin') ,action: 'Element/Plugin/Update' ,cls: 'qce-window qce-update' - ,buttons: [{ - text: config.cancelBtnText || _('cancel') - ,scope: this - ,handler: function() { this.hide(); } - },{ - text: config.saveBtnText || _('save') - ,scope: this - ,handler: function() { this.submit(false); } - },{ - text: config.saveBtnText || _('save_and_close') - ,cls: 'primary-button' - ,scope: this - ,handler: this.submit - }] + ,modxFbarButtons: 'c-s-sc' }); MODx.window.QuickUpdatePlugin.superclass.constructor.call(this,config); }; @@ -1148,17 +1064,21 @@ Ext.reg('modx-window-quick-update-plugin',MODx.window.QuickUpdatePlugin); MODx.window.QuickCreateTV = function(config) { + config = config || {}; - this.ident = config.ident || `qcetv${Ext.id()}`; + const windowId = `window-qce-tv-${Ext.id()}`; Ext.applyIf(config,{ - title: _('quick_create_tv') - ,width: 700 + id: windowId + ,title: _('quick_create_tv') + ,width: 640 ,url: MODx.config.connector_url ,action: 'Element/TemplateVar/Create' ,cls: 'qce-window qce-create' - ,modal: true - ,monitorResize: true + ,modxPseudoModal: true + ,modxFbarSaveSwitches: ['clearCache'] + // ,saveBtnText: 'Test Save' + // ,cancelBtnText: 'Test Cancel' ,fields: [{ xtype: 'hidden' ,name: 'id' @@ -1187,9 +1107,41 @@ MODx.window.QuickCreateTV = function(config) { xtype: 'textfield' ,name: 'name' ,fieldLabel: _('name') + ,description: MODx.expandHelp ? '' : _('tv_name_desc', { + tag: `[[*${_('example_tag_tv_name')}]]` + }) ,allowBlank: false ,maxLength: 50 ,value: config.record.name || '' + ,enableKeyEvents: true + ,listeners: { + keyup: { + fn: function(cmp, e) { + let title = Ext.util.Format.stripTags(cmp.getValue()), + tagTitle + ; + title = Ext.util.Format.htmlEncode(title); + tagTitle = title.length > 0 ? title : _('example_tag_tv_name'); + cmp.nextSibling().getEl().child('.example-replace-name').update(tagTitle); + } + ,scope: this + } + } + },{ + xtype: 'box' + ,hidden: MODx.expandHelp ? false : true + ,html: _('tv_name_desc', { + tag: `[[*${_('example_tag_tv_name')}]]` + }) + ,cls: 'desc-under' + ,listeners: { + afterrender: { + fn: function(cmp) { + MODx.util.insertTagCopyUtility(cmp, 'tv'); + } + ,scope: this + } + } }] },{ columnWidth: 0.5 @@ -1202,6 +1154,11 @@ MODx.window.QuickCreateTV = function(config) { ,fieldLabel: _('tv_type') ,name: 'type' ,value: config.record.type || 'text' + },{ + xtype: 'box' + ,hidden: MODx.expandHelp ? false : true + ,html: _('tv_type_desc') + ,cls: 'desc-under' }] }] }] @@ -1228,14 +1185,13 @@ MODx.window.QuickCreateTV = function(config) { ,items: [{ xtype: 'textfield' ,name: 'caption' - ,id: `modx-${this.ident}-caption` ,fieldLabel: _('caption') ,description: MODx.expandHelp ? '' : _('tv_caption_desc') ,maxLength: 50 ,value: config.record.caption || '' },{ - xtype: 'label' - ,forId: `modx-${this.ident}-caption` + xtype: 'box' + ,hidden: MODx.expandHelp ? false : true ,html: _('tv_caption_desc') ,cls: 'desc-under' }] @@ -1248,13 +1204,11 @@ MODx.window.QuickCreateTV = function(config) { ,items: [{ xtype: 'modx-combo-category' ,name: 'category' - ,id: `modx-${this.ident}-category` ,fieldLabel: _('category') ,description: MODx.expandHelp ? '' : _('tv_category_desc') ,value: config.record.category || 0 },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-category` + xtype: MODx.expandHelp ? 'box' : 'hidden' ,html: _('tv_category_desc') ,cls: 'desc-under' }] @@ -1273,7 +1227,7 @@ MODx.window.QuickCreateTV = function(config) { ,labelAlign: 'top' } ,items: [{ - columnWidth: 0.5 + columnWidth: 1 ,defaults: { anchor: '100%' ,msgTarget: 'under' @@ -1283,41 +1237,17 @@ MODx.window.QuickCreateTV = function(config) { ,items: [{ xtype: 'textarea' ,name: 'description' - ,id: `modx-${this.ident}-description` ,fieldLabel: _('description') ,description: MODx.expandHelp ? '' : _('tv_description_desc') ,grow: true - ,growMin: 50 + ,growMin: 30 ,growMax: this.isSmallScreen ? 90 : 120 ,value: config.record.description || '' },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-description` ,html: _('tv_description_desc') ,cls: 'desc-under' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'xcheckbox' - ,name: 'clearCache' - ,id: `modx-${this.ident}-clear-cache` - ,hideLabel: true - ,boxLabel: _('clear_cache_on_save') - ,description: MODx.expandHelp ? '' : _('clear_cache_on_save_desc') - ,ctCls: 'add-label-space' - ,inputValue: 1 - ,checked: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-clear-cache` - ,html: _('clear_cache_on_save_desc') - ,cls: 'desc-under toggle-slider-above' - }] }] }] },{ @@ -1343,7 +1273,6 @@ MODx.window.QuickCreateTV = function(config) { ,items: [{ xtype: 'textarea' ,name: 'els' - ,id: `modx-${this.ident}-elements` ,fieldLabel: _('tv_elements') ,description: MODx.expandHelp ? '' : _('tv_elements_short_desc') ,grow: true @@ -1352,7 +1281,6 @@ MODx.window.QuickCreateTV = function(config) { ,value: config.record.els || '' },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-elements` ,html: _('tv_elements_short_desc') ,cls: 'desc-under' }] @@ -1381,7 +1309,6 @@ MODx.window.QuickCreateTV = function(config) { ,items: [{ xtype: 'textarea' ,name: 'default_text' - ,id: `modx-${this.ident}-default-text` ,fieldLabel: _('tv_default') ,description: MODx.expandHelp ? '' : _('tv_default_desc') ,grow: true @@ -1390,7 +1317,6 @@ MODx.window.QuickCreateTV = function(config) { ,value: config.record.default_text || '' },{ xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: `modx-${this.ident}-default-text` ,html: _('tv_default_desc') ,cls: 'desc-under' }] @@ -1407,6 +1333,14 @@ MODx.window.QuickCreateTV = function(config) { ,fn: this.submit ,scope: this }] + /* + ,buttons: [{ + text: 'Test One' + },{ + text: 'Test Two' + ,cls: 'primary-button' + }] + */ }); MODx.window.QuickCreateTV.superclass.constructor.call(this,config); }; @@ -1420,20 +1354,7 @@ MODx.window.QuickUpdateTV = function(config) { title: _('quick_update_tv') ,action: 'Element/TemplateVar/Update' ,cls: 'qce-window qce-update' - ,buttons: [{ - text: config.cancelBtnText || _('cancel') - ,scope: this - ,handler: function() { this.hide(); } - },{ - text: config.saveBtnText || _('save') - ,scope: this - ,handler: function() { this.submit(false); } - },{ - text: config.saveBtnText || _('save_and_close') - ,cls: 'primary-button' - ,scope: this - ,handler: this.submit - }] + ,modxFbarButtons: 'c-s-sc' }); MODx.window.QuickUpdateTV.superclass.constructor.call(this,config); }; @@ -1442,63 +1363,64 @@ Ext.reg('modx-window-quick-update-tv',MODx.window.QuickUpdateTV); MODx.window.DuplicateContext = function(config) { + config = config || {}; - this.ident = config.ident || 'dupctx'+Ext.id(); Ext.Ajax.timeout = 0; + const windowId = `window-dup-context-${Ext.id()}`, + preserveAliasCmpId = `${windowId}-modx-preserve_alias`, + preserveMenuIndexCmpId = `${windowId}-modx-preserve_menuindex` + ; + Ext.applyIf(config,{ - title: _('duplicate') - ,id: this.ident + id: windowId + ,title: _('duplicate') ,url: MODx.config.connector_url ,action: 'Context/Duplicate' + ,modxPseudoModal: true ,fields: [{ xtype: 'statictextfield' - ,id: 'modx-'+this.ident+'-key' - ,fieldLabel: _('old_key') ,name: 'key' - ,anchor: '100%' + ,fieldLabel: _('old_key') ,submitValue: true },{ xtype: 'textfield' - ,id: 'modx-'+this.ident+'-newkey' - ,fieldLabel: _('new_key') ,name: 'newkey' - ,anchor: '100%' + ,fieldLabel: _('new_key') ,value: '' },{ xtype: 'checkbox' - ,id: 'modx-'+this.ident+'-preserveresources' + ,name: 'preserve_resources' ,hideLabel: true ,boxLabel: _('preserve_resources') - ,name: 'preserve_resources' - ,anchor: '100%' ,checked: true ,listeners: { - 'check': {fn: function(cb,checked) { - if (checked) { - this.fp.getForm().findField('modx-'+this.ident+'-preservealias').setValue(true).enable(); - this.fp.getForm().findField('modx-'+this.ident+'-preservemenuindex').setValue(true).enable(); - } else { - this.fp.getForm().findField('modx-'+this.ident+'-preservealias').setValue(false).disable(); - this.fp.getForm().findField('modx-'+this.ident+'-preservemenuindex').setValue(false).disable(); - } - },scope:this} + check: { + fn: function(cb, checked) { + const form = this.fp.getForm(); + if (checked) { + form.findField(preserveAliasCmpId).setValue(true).enable(); + form.findField(preserveMenuIndexCmpId).setValue(true).enable(); + } else { + form.findField(preserveAliasCmpId).setValue(false).disable(); + form.findField(preserveMenuIndexCmpId).setValue(false).disable(); + } + }, + scope: this + } } - },{ xtype: 'checkbox' - ,id: 'modx-'+this.ident+'-preservealias' - ,hideLabel: true - ,boxLabel: _('preserve_alias') // Todo: add translation + ,id: preserveAliasCmpId ,name: 'preserve_alias' - ,anchor: '100%' + ,hideLabel: true + ,boxLabel: _('preserve_alias') ,checked: true },{ xtype: 'checkbox' - ,id: 'modx-'+this.ident+'-preservemenuindex' - ,hideLabel: true - ,boxLabel: _('preserve_menuindex') // Todo: add translation + ,id: preserveMenuIndexCmpId ,name: 'preserve_menuindex' - ,anchor: '100%' + ,hideLabel: true + ,boxLabel: _('preserve_menuindex') ,checked: true }] }); @@ -1508,12 +1430,14 @@ Ext.extend(MODx.window.DuplicateContext,MODx.Window); Ext.reg('modx-window-context-duplicate',MODx.window.DuplicateContext); MODx.window.Login = function(config) { + config = config || {}; - this.ident = config.ident || 'dupctx'+Ext.id(); Ext.Ajax.timeout = 0; + const windowId = `window-login-extend-${Ext.id()}`; + Ext.applyIf(config,{ - title: _('login') - ,id: this.ident + id: windowId + ,title: _('login') ,url: MODx.config.connectors_url ,action: 'Security/Login' ,fields: [{ @@ -1521,17 +1445,13 @@ MODx.window.Login = function(config) { ,xtype: 'modx-description' },{ xtype: 'textfield' - ,id: 'modx-'+this.ident+'-username' - ,fieldLabel: _('username') ,name: 'username' - ,anchor: '100%' + ,fieldLabel: _('username') },{ xtype: 'textfield' + ,name: 'password' ,inputType: 'password' - ,id: 'modx-'+this.ident+'-password' ,fieldLabel: _('password') - ,name: 'password' - ,anchor: '100%' },{ xtype: 'hidden' ,name: 'rememberme' @@ -1540,7 +1460,9 @@ MODx.window.Login = function(config) { ,buttons: [{ text: _('logout') ,scope: this - ,handler: function() { location.href = '?logout=1' } + ,handler: function() { + location.href = '?logout=1' + } },{ text: _('login') ,cls: 'primary-button' diff --git a/manager/templates/default/header.tpl b/manager/templates/default/header.tpl index 1b0ed0252f1..a90993ae8ce 100644 --- a/manager/templates/default/header.tpl +++ b/manager/templates/default/header.tpl @@ -20,11 +20,52 @@ {/if} + {$maincssjs} @@ -34,6 +75,7 @@ + From b54df67108dc22c28e04000a48f80cf72733beef Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 6 May 2022 14:59:39 -0400 Subject: [PATCH 04/12] Fix errors found via Lexicon unit test --- core/lexicon/en/chunk.inc.php | 1 - core/lexicon/en/default.inc.php | 3 +++ core/lexicon/en/element.inc.php | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/lexicon/en/chunk.inc.php b/core/lexicon/en/chunk.inc.php index 5c5dd6b37f0..3d975b1afb6 100644 --- a/core/lexicon/en/chunk.inc.php +++ b/core/lexicon/en/chunk.inc.php @@ -34,7 +34,6 @@ // Temporarily match old keys to new ones to ensure compatibility // --fields $_lang['chunk_desc_category'] = $_lang['chunk_category_desc']; -$_lang['chunk_desc_name'] = $_lang['chunk_name_desc']; $_lang['chunk_lock_msg'] = $_lang['chunk_lock_desc']; // --tabs diff --git a/core/lexicon/en/default.inc.php b/core/lexicon/en/default.inc.php index cfef37adb0c..f3a1fe1c467 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -574,6 +574,8 @@ // All $_lang['static_file'] = 'Static File'; $_lang['static_file_desc'] = 'The external file location where the source code for this element is stored.'; + // Temporarily match old keys to new ones to ensure compatibility + $_lang['static_file_msg'] = $_lang['static_file_desc']; // Chunks $_lang['example_tag_chunk_name'] = 'NameOfChunk'; @@ -584,6 +586,7 @@ $_lang['chunk_tag_copied'] = 'Chunk tag copied!'; // Temporarily match old keys to new ones to ensure compatibility $_lang['chunk_desc_description'] = $_lang['chunk_description_desc']; + $_lang['chunk_desc_name'] = $_lang['chunk_name_desc']; // Plugins $_lang['plugin_code'] = 'Plugin Code (PHP)'; diff --git a/core/lexicon/en/element.inc.php b/core/lexicon/en/element.inc.php index 8bbd93499d1..e7613c40e39 100644 --- a/core/lexicon/en/element.inc.php +++ b/core/lexicon/en/element.inc.php @@ -38,7 +38,6 @@ // Temporarily match old keys to new ones to ensure compatibility $_lang['is_static_msg'] = $_lang['is_static_desc']; -$_lang['static_file_msg'] = $_lang['static_file_desc']; $_lang['static_source_msg'] = $_lang['static_source_desc']; /* From ecc7e2b764b3af40e3efe6cc6661d921d7987c7c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:06:03 -0400 Subject: [PATCH 05/12] System setting changes and additions --- .../data/transport.core.system_settings.php | 50 +++++++++---------- core/lexicon/en/default.inc.php | 20 +++++++- core/lexicon/en/setting.inc.php | 25 +++++----- 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/_build/data/transport.core.system_settings.php b/_build/data/transport.core.system_settings.php index aa4fe87d77b..3426cc7f533 100644 --- a/_build/data/transport.core.system_settings.php +++ b/_build/data/transport.core.system_settings.php @@ -2157,55 +2157,55 @@ 'area' => 'static_elements', 'editedon' => null, ], '', true, true); -$settings['enable_overlays'] = $xpdo->newObject(modSystemSetting::class); -$settings['enable_overlays']->fromArray([ - 'key' => 'enable_overlays', - 'value' => true, +$settings['mask_disabled_modal'] = $xpdo->newObject(modSystemSetting::class); +$settings['mask_disabled_modal']->fromArray([ + 'key' => 'mask_disabled_modal', + 'value' => false, 'xtype' => 'combo-boolean', 'namespace' => 'core', 'area' => 'manager', 'editedon' => null, ], '', true, true); -$settings['overlay_color'] = $xpdo->newObject(modSystemSetting::class); -$settings['overlay_color']->fromArray([ - 'key' => 'overlay_color', - 'value' => '#0d141d', +$settings['mask_color_modal'] = $xpdo->newObject(modSystemSetting::class); +$settings['mask_color_modal']->fromArray([ + 'key' => 'mask_color_modal', + 'value' => '#ffffff', 'xtype' => 'textfield', 'namespace' => 'core', 'area' => 'manager', 'editedon' => null, ], '', true, true); -$settings['overlay_opacity_blocking'] = $xpdo->newObject(modSystemSetting::class); -$settings['overlay_opacity_blocking']->fromArray([ - 'key' => 'overlay_opacity_blocking', +$settings['mask_opacity_modal'] = $xpdo->newObject(modSystemSetting::class); +$settings['mask_opacity_modal']->fromArray([ + 'key' => 'mask_opacity_modal', 'value' => 50, 'xtype' => 'numberfield', 'namespace' => 'core', 'area' => 'manager', 'editedon' => null, ], '', true, true); -$settings['overlay_opacity_nonblocking'] = $xpdo->newObject(modSystemSetting::class); -$settings['overlay_opacity_nonblocking']->fromArray([ - 'key' => 'overlay_opacity_nonblocking', - 'value' => 50, - 'xtype' => 'numberfield', +$settings['mask_disabled_pseudomodal'] = $xpdo->newObject(modSystemSetting::class); +$settings['mask_disabled_pseudomodal']->fromArray([ + 'key' => 'mask_disabled_pseudomodal', + 'value' => false, + 'xtype' => 'combo-boolean', 'namespace' => 'core', 'area' => 'manager', 'editedon' => null, ], '', true, true); -$settings['overlay_tint_blocking'] = $xpdo->newObject(modSystemSetting::class); -$settings['overlay_tint_blocking']->fromArray([ - 'key' => 'overlay_tint_blocking', - 'value' => 0, - 'xtype' => 'numberfield', +$settings['mask_color_pseudomodal'] = $xpdo->newObject(modSystemSetting::class); +$settings['mask_color_pseudomodal']->fromArray([ + 'key' => 'mask_color_pseudomodal', + 'value' => '#0d141d', + 'xtype' => 'textfield', 'namespace' => 'core', 'area' => 'manager', 'editedon' => null, ], '', true, true); -$settings['overlay_tint_nonblocking'] = $xpdo->newObject(modSystemSetting::class); -$settings['overlay_tint_nonblocking']->fromArray([ - 'key' => 'overlay_tint_nonblocking', - 'value' => 70, +$settings['mask_opacity_pseudomodal'] = $xpdo->newObject(modSystemSetting::class); +$settings['mask_opacity_pseudomodal']->fromArray([ + 'key' => 'mask_opacity_pseudomodal', + 'value' => 50, 'xtype' => 'numberfield', 'namespace' => 'core', 'area' => 'manager', diff --git a/core/lexicon/en/default.inc.php b/core/lexicon/en/default.inc.php index f3a1fe1c467..fbe902cd21d 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -46,7 +46,7 @@ $_lang['cache_sitepublishing_file_error'] = '

ERROR: Could not write site publishing file to cache.

'; $_lang['cache_unpublish_event_error'] = '

ERROR: Could not determine next unpublish event!

[[+info]]
'; $_lang['cached'] = 'Cached'; -$_lang['cancel'] = 'Close'; +$_lang['cancel'] = 'Cancel'; $_lang['caption'] = 'Caption'; $_lang['caption_desc'] = 'The name to show beside the input when editing a TV on a Resource form.'; $_lang['categories'] = 'Categories'; @@ -262,6 +262,23 @@ $_lang['manage_files'] = 'Manage Files'; $_lang['manager'] = 'Manager'; $_lang['manager_log_err_save'] = 'An error occurred while logging the manager action.'; + +$_lang['mask_config_field_color'] = 'Mask Color'; +$_lang['mask_config_field_disabled'] = 'Disable Mask'; +$_lang['mask_config_field_disabled_desc'] = 'Remove the window backdrop to fully reveal the page below.'; +$_lang['mask_config_field_opacity'] = 'Mask Opacity'; +$_lang['mask_config_field_update_global'] = 'Update Global Settings'; +$_lang['mask_config_field_update_global_desc'] = 'Apply these changes to the global MODx settings.'; +$_lang['mask_config_field_update_user'] = 'Update User Settings'; +$_lang['mask_config_field_update_user_desc'] = 'Apply these changes to the current user’s settings. If switched off, changes made here will be temporary and lost upon logout.'; +$_lang['mask_config_window_title'] = 'Configure Mask'; +$_lang['mask_toolbar_tool_title'] = 'Mask Settings'; +$_lang['mask_toolbar_tool_qtip'] = 'Open mask configuration window'; + +$_lang['mask_config_confirm_session_only'] = ''; +$_lang[''] = ''; +$_lang[''] = ''; + $_lang['media'] = 'Media'; $_lang['menu_order'] = 'Menu Order'; $_lang['mime_type'] = 'MIME Type'; @@ -624,7 +641,6 @@ $_lang['tv_category_desc'] = 'Use to group TVs in Resource editing pages and within the Elements tree.'; $_lang['tv_default'] = 'Default Value'; $_lang['tv_default_desc'] = 'The content this TV will show if user-entered content is not provided.'; -// $_lang['tv_description_desc'] = 'Usage information for this TV shown next to its caption in Resource editing pages and as a tooltip in the Elements tree.'; $_lang['tv_description_desc'] = 'Usage information for this TV shown next to its caption in Resource editing pages, as a tooltip in the Elements tree, and within search results.'; $_lang['tv_elements'] = 'Input Option Values'; $_lang['tv_elements_short_desc'] = 'Defines the selectable options for this TV, which may be manually entered or built with a one-line database query.'; diff --git a/core/lexicon/en/setting.inc.php b/core/lexicon/en/setting.inc.php index e7684bde7d4..85d2d16af78 100644 --- a/core/lexicon/en/setting.inc.php +++ b/core/lexicon/en/setting.inc.php @@ -494,23 +494,24 @@ $_lang['setting_package_installer_at_top'] = 'Pin Package-Installer at top'; $_lang['setting_package_installer_at_top_desc'] = 'If enabled, the Installer entry will be pinned to the top of the Extras menu. Otherwise it will be positioned according to its menuindex.'; -$_lang['setting_enable_overlays'] = 'Enable Overlays'; -$_lang['setting_enable_overlays_desc'] = 'If enabled, a semi-transparent overlay will mask the manager’s main interface below editing windows and dialog boxes, enhancing user focus on the front window(s).'; -$_lang['setting_overlay_color'] = 'Overlay Color'; -$_lang['setting_overlay_color_desc'] = 'Any valid css color specified in Hexadecimal, RGB/A, HSL/A, W3C named, or transparent format.'; +$_lang['setting_mask_disabled_pseudomodal'] = 'Disable Window Masking'; +$_lang['setting_mask_disabled_pseudomodal_desc'] = 'Removes the semi-transparent backdrop that masks the manager’s main interface below one or more editing windows. This setting may also be managed directly from an editing window’s top tool bar.'; -$_lang['setting_overlay_opacity_blocking'] = 'Overlay Opacity (Blocking)'; -$_lang['setting_overlay_opacity_blocking_desc'] = 'Controls how opaque a dialog window’s underlying overlay (mask) will be. This type of overlay is also used as a mask for disabled grids and blocks interaction with the elements below. Valid values range from 0 (completely transparent) to 100 (completely opaque).'; +$_lang['setting_mask_color_pseudomodal'] = 'Window Mask Color'; +$_lang['setting_mask_color_pseudomodal_desc'] = 'Any valid css color specified in Hexadecimal, RGB/A, HSL/A, W3C named, or transparent format. This setting may also be managed directly from an editing window’s top tool bar.'; -$_lang['setting_overlay_opacity_nonblocking'] = 'Overlay Opacity (Non-Blocking)'; -$_lang['setting_overlay_opacity_nonblocking_desc'] = 'Controls how opaque an editing window’s non-blocking underlying overlay (mask) will be. This type of overlay allows interaction with the elements below. Valid values range from 0 (completely transparent) to 100 (completely opaque).'; +$_lang['setting_mask_opacity_pseudomodal'] = 'Window Mask Opacity'; +$_lang['setting_mask_opacity_pseudomodal_desc'] = 'Controls how opaque an editing window’s backdrop (mask) will be. Valid values range from 5 (more transparent) to 95 (more opaque). This setting may also be managed directly from an editing window’s top tool bar.'; -$_lang['setting_overlay_tint_blocking'] = 'Overlay Tint (Blocking)'; -$_lang['setting_overlay_tint_blocking_desc'] = 'The percentage of white added to lighten a dialog window’s underlying overlay color. This type of overlay is also used as a mask for disabled grids and blocks interaction with the elements below. Valid values range from 0 (no lightening) to 100 (completely white) in increments of 5.'; +$_lang['setting_mask_disabled_modal'] = 'Disable Modal Masking'; +$_lang['setting_mask_disabled_modal_desc'] = 'Removes the semi-transparent backdrop that masks the manager’s main interface below dialog windows. When a modal is active, interaction with the main interface is blocked regardless of this setting’s value. This setting may also be managed directly from an editing window’s top tool bar.'; -$_lang['setting_overlay_tint_nonblocking'] = 'Overlay Tint (Non-Blocking)'; -$_lang['setting_overlay_tint_nonblocking_desc'] = 'The percentage of white added to lighten an editing window’s non-blocking underlying overlay color. This type of overlay allows interaction with the elements below. Valid values range from 0 (no lightening) to 100 (completely white) in increments of 5.'; +$_lang['setting_mask_color_modal'] = 'Modal Mask Color'; +$_lang['setting_mask_color_modal_desc'] = 'Any valid css color specified in Hexadecimal, RGB/A, HSL/A, W3C named, or transparent format. This setting may also be managed directly from an editing window’s top tool bar.'; + +$_lang['setting_mask_opacity_modal'] = 'Modal Mask Opacity'; +$_lang['setting_mask_opacity_modal_desc'] = 'Controls how opaque a dialog window’s backdrop (mask) will be. Valid values range from 5 (more transparent) to 95 (more opaque). This setting may also be managed directly from an editing window’s top tool bar.'; $_lang['setting_parser_recurse_uncacheable'] = 'Delay Uncacheable Parsing'; $_lang['setting_parser_recurse_uncacheable_desc'] = 'If disabled, uncacheable elements may have their output cached inside cacheable element content. Disable this ONLY if you are having problems with complex nested parsing which stopped working as expected.'; From 5ea0702a46333622d44a2a6d122434cc8d73a613 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:15:25 -0400 Subject: [PATCH 06/12] Persist active user's usergroup ids in MODx.config Adds replacement method for getting usergroup ids, allowing additional params to be passed. Reason for change is to support access control of window settings (made available in a new config window) that honors usergroup permissions. --- .../Revolution/Processors/System/ConfigJs.php | 1 + core/src/Revolution/modUser.php | 34 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/core/src/Revolution/Processors/System/ConfigJs.php b/core/src/Revolution/Processors/System/ConfigJs.php index c93f83f0fa3..ab8b2f120c0 100644 --- a/core/src/Revolution/Processors/System/ConfigJs.php +++ b/core/src/Revolution/Processors/System/ConfigJs.php @@ -90,6 +90,7 @@ public function process() $this->modx->_userConfig ), 'user' => $this->modx->user->get('id'), + 'user_usergroups' => $this->modx->user->getUserGroupIds(true), 'version' => $this->modx->version['full_version'], 'resource_classes' => $resourceClasses, 'resource_classes_drop' => $resourceClassesDrop, diff --git a/core/src/Revolution/modUser.php b/core/src/Revolution/modUser.php index 9cdee5042dd..a0bf6e229a7 100644 --- a/core/src/Revolution/modUser.php +++ b/core/src/Revolution/modUser.php @@ -634,7 +634,7 @@ public function getResourceGroups($ctx = '') } /** - * Gets all the User Group IDs of the groups this user belongs to. + * (DEPRECATED) Gets all the User Group IDs of the groups this user belongs to. * * @access public * @return array An array of User Group IDs. @@ -685,6 +685,38 @@ public function getPrimaryGroup() return $userGroup; } + /** + * Gets all the User Group IDs of the groups this user belongs to. + * + * @param bool $sortByRank Whether to return the results in ranked order + * @param string $sortDirection If sortByRank, will sort in the specified direction + * @return array An array of User Group IDs. + */ + public function getUserGroupIds(bool $sortByRank = false, string $sortDirection = 'ASC') : array + { + $groups = []; + $id = $this->get('id') ? (string)$this->get('id') : '0'; + if (isset($_SESSION["modx.user.{$id}.userGroups"]) && $this->xpdo->user->get('id') == $this->get('id')) { + $groups = $_SESSION["modx.user.{$id}.userGroups"]; + } else { + $c = $this->xpdo->newQuery(modUserGroup::class); + $c->where(['`UserGroupMembers`.`member`' => $this->get('id')]); + if ($sortByRank) { + $c->sortby('`UserGroupMembers`.`rank`', $sortDirection); + } + $memberGroups = $this->xpdo->getCollectionGraph(modUserGroup::class, '{"UserGroupMembers":{}}', $c); + if ($memberGroups) { + /** @var modUserGroup $group */ + foreach ($memberGroups as $group) { + $groups[] = $group->get('id'); + } + } + $_SESSION["modx.user.{$id}.userGroups"] = $groups; + } + + return $groups; + } + /** * Gets all the User Group names of the groups this user belongs to. * From 3cbc1fec8d1718fcf7c5fb6af8bfb406cca3b7cc Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:24:32 -0400 Subject: [PATCH 07/12] System settings-related updates --- .../Security/User/Setting/GetListIn.php | 64 +++++++++++++++++++ .../Security/User/Setting/Update.php | 6 +- .../modext/widgets/core/modx.grid.settings.js | 45 ++++++++++++- 3 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 core/src/Revolution/Processors/Security/User/Setting/GetListIn.php diff --git a/core/src/Revolution/Processors/Security/User/Setting/GetListIn.php b/core/src/Revolution/Processors/Security/User/Setting/GetListIn.php new file mode 100644 index 00000000000..7e48f05bf95 --- /dev/null +++ b/core/src/Revolution/Processors/Security/User/Setting/GetListIn.php @@ -0,0 +1,64 @@ +setDefaultProperties(['user' => 0]); + + return parent::initialize(); + } + + /** + * Filter by user and a mulitple-key query + * @return array + */ + public function prepareCriteria() + { + $criteria = []; + $criteria[] = ['user' => (int)$this->getProperty('user')]; + // $msg = "\r\n prepareCriteria, \$properties:\r\n" . print_r($this->getProperties(), true); + // $this->modx->log(\modX::LOG_LEVEL_ERROR, $msg, '', __CLASS__); + + if ($keys = $this->getProperty('keys', '')) { + $keys = json_decode($keys); + // $this->modx->log( + // \modX::LOG_LEVEL_ERROR, + // "\r\t prepareCriteria: + // \t\t\$var1: {none} + // \t\t\$keys: " . print_r($keys, true) + // ); + $criteria[] = ['key:IN' => $keys]; + } + return $criteria; + } + +} diff --git a/core/src/Revolution/Processors/Security/User/Setting/Update.php b/core/src/Revolution/Processors/Security/User/Setting/Update.php index 6f6305df3b4..75201aafa4e 100644 --- a/core/src/Revolution/Processors/Security/User/Setting/Update.php +++ b/core/src/Revolution/Processors/Security/User/Setting/Update.php @@ -30,8 +30,8 @@ class Update extends \MODX\Revolution\Processors\System\Settings\Update * @return bool|string|null */ public function initialize() - { - $user = (int)$this->getProperty('fk', 0); + { + $user = (int)$this->getProperty('fk', $this->getProperty('user', 0)); if (!$user) { return $this->modx->lexicon('user_err_ns'); } @@ -53,6 +53,4 @@ public function initialize() return true; } - } - diff --git a/manager/assets/modext/widgets/core/modx.grid.settings.js b/manager/assets/modext/widgets/core/modx.grid.settings.js index ba4ebeef9ff..c8609f52256 100644 --- a/manager/assets/modext/widgets/core/modx.grid.settings.js +++ b/manager/assets/modext/widgets/core/modx.grid.settings.js @@ -281,7 +281,8 @@ MODx.grid.SettingsGrid = function(config = {}) { ,scrollOffset: 0 }); MODx.grid.SettingsGrid.superclass.constructor.call(this,config); - this.addEvents('createSetting', 'updateSetting'); + this.addEvents('createSetting', 'updateSetting', 'updateSettingFromGrid', 'removeSettingFromGrid'); + console.log(`Default Src: ${MODx.config.default_media_source}`); const gridFilterData = [ { filterId: 'filter-ns', dependentParams: ['area'] }, @@ -290,17 +291,57 @@ MODx.grid.SettingsGrid = function(config = {}) { this.on({ createSetting: function(...args) { + // console.log('createSetting args', ...args); if (args[0].a.response.status === 200) { this.refreshFilterOptions(gridFilterData); + if (args[0].a.result.object.key.indexOf('mask_') === 0) { + MODx.maskConfig.fireEvent('syncSettingFromGrid', { + action: 'create', + data: args[0].a.result.object, + gridType: this.settingsType + }); + // MODx.maskConfig.fireEvent('createSettingFromGrid', args[0].a.result.object); + } } }, updateSetting: function(...args) { + // console.log('updateSetting args', ...args); if (args[0].a.response.status === 200) { this.refreshFilterOptions(gridFilterData); + if (args[0].a.result.object.key.indexOf('mask_') === 0) { + MODx.maskConfig.fireEvent('syncSettingFromGrid', { + action: 'update', + data: args[0].a.result.object, + gridType: this.settingsType + }); + // MODx.maskConfig.fireEvent('updateSettingFromGrid', args[0].a.result.object); + } } }, - afterRemoveRow: function() { + afterAutoSave: function(response) { + if (response.success && response.object.key.indexOf('mask_') === 0) { + MODx.maskConfig.fireEvent('syncSettingFromGrid', { + action: 'update', + data: response.object, + gridType: this.settingsType + }); + // MODx.maskConfig.fireEvent('updateSettingFromGrid', response.object); + } + }, + afterRemoveRow: function(record) { this.refreshFilterOptions(gridFilterData); + if (record && record.key.indexOf('mask_') === 0) { + // const target = this.settingsType || this.getSettingsType(); + MODx.maskConfig.fireEvent('syncSettingFromGrid', { + action: 'remove', + data: record, + gridType: this.settingsType + }); + // MODx.maskConfig.fireEvent('removeSettingFromGrid', { + // record, + // target + // }); + } } }); From 7ccb3dbe403fa27ed8a1e737ca75d8ae7e78bec7 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:28:07 -0400 Subject: [PATCH 08/12] Misc window updates --- _build/templates/default/sass/_windows.scss | 8 +- .../modext/widgets/modx.panel.welcome.js | 45 +--- .../widgets/resource/modx.window.resource.js | 5 +- .../widgets/system/modx.grid.content.type.js | 237 +++++++++--------- 4 files changed, 128 insertions(+), 167 deletions(-) diff --git a/_build/templates/default/sass/_windows.scss b/_build/templates/default/sass/_windows.scss index f9cc49939dd..44ca0603897 100644 --- a/_build/templates/default/sass/_windows.scss +++ b/_build/templates/default/sass/_windows.scss @@ -306,15 +306,15 @@ .ext-el-mask { background-color: $colorSplashShadow; opacity: 0; - transition: opacity .5s; - /*z-index: 10;*/ /* this is handeled by extjs and set to 9000 on show */ + transition: opacity 0.75s ease-in-out, background-color 0.25s; + z-index: 200; - &.clickthrough { + &.pseudomodal { pointer-events: none; } &.fade-in { - opacity: .5; + // opacity: .5; } // This affects the grid mask diff --git a/manager/assets/modext/widgets/modx.panel.welcome.js b/manager/assets/modext/widgets/modx.panel.welcome.js index 5210d3e4afc..87673181a63 100644 --- a/manager/assets/modext/widgets/modx.panel.welcome.js +++ b/manager/assets/modext/widgets/modx.panel.welcome.js @@ -233,10 +233,9 @@ Ext.extend(MODx.panel.Welcome, MODx.Panel, { Ext.reg('modx-panel-welcome', MODx.panel.Welcome); -MODx.window.DashboardWidgetAdd = function (config) { +MODx.window.DashboardWidgetAdd = function (config = {}) { + // console.log('listerners to delete: ', config.listeners); delete config.listeners; - - config = config || {}; this.ident = Ext.id(); Ext.applyIf(config, { title: _('widget_add'), @@ -244,17 +243,14 @@ MODx.window.DashboardWidgetAdd = function (config) { url: MODx.config.connector_url, baseParams: { action: 'System/Dashboard/User/Create', - dashboard: config.dashboard.id, + dashboard: config.dashboard.id }, modal: true, resizable: false, collapsible: false, maximizable: false, fields: this.getFields(config), - keys: this.getKeys(config), - buttons: this.getButtons(config), - closeAction: 'close', - success: function () { + success: function() { this._reload = true; var combo = Ext.getCmp(this.ident + '-widget'); if (combo) { @@ -279,7 +275,7 @@ Ext.extend(MODx.window.DashboardWidgetAdd, MODx.Window, { _reload: false, - getFields: function (config) { + getFields: function(config) { return [{ hideLabel: true, xtype: 'displayfield', @@ -320,35 +316,6 @@ Ext.extend(MODx.window.DashboardWidgetAdd, MODx.Window, { value: 'half', anchor: '100%' }]; - }, - - getButtons: function (config) { - return [{ - text: config.cancelBtnText || _('cancel'), - scope: this, - handler: function () { - config.closeAction !== 'close' - ? this.hide() - : this.close(); - } - }, { - text: config.saveBtnText || _('save'), - cls: 'primary-button', - scope: this, - handler: function () { - this.submit(false); - }, - }]; - }, - - getKeys: function () { - return [{ - key: Ext.EventObject.ENTER, - shift: true, - fn: function () { - this.submit(false); - }, scope: this - }]; - }, + } }); Ext.reg('modx-window-dashboard-widget-add', MODx.window.DashboardWidgetAdd); diff --git a/manager/assets/modext/widgets/resource/modx.window.resource.js b/manager/assets/modext/widgets/resource/modx.window.resource.js index 31484211f5d..a064b3fd0bd 100644 --- a/manager/assets/modext/widgets/resource/modx.window.resource.js +++ b/manager/assets/modext/widgets/resource/modx.window.resource.js @@ -65,15 +65,14 @@ MODx.window.CreateResource = function(config = {}) { }); } Ext.applyIf(config, { - autoHeight: true, + // autoHeight: true, title: _('document_new'), url: MODx.config.connector_url, baseParams: { action: 'Resource/Create' }, width: 600, - cls: 'qce-window qce-create', - modxPseudoModal: true, + // cls: 'modx-window qce-window qce-create', fields: [{ xtype: 'textfield', fieldLabel: _('resource_pagetitle'), diff --git a/manager/assets/modext/widgets/system/modx.grid.content.type.js b/manager/assets/modext/widgets/system/modx.grid.content.type.js index 16bda8b568a..4915f2f2133 100644 --- a/manager/assets/modext/widgets/system/modx.grid.content.type.js +++ b/manager/assets/modext/widgets/system/modx.grid.content.type.js @@ -109,29 +109,30 @@ MODx.grid.ContentType = function(config) { }; Ext.extend(MODx.grid.ContentType,MODx.grid.Grid,{ getMenu: function() { - var m = []; + const m = []; m.push({ - text: _('edit') - ,handler: function(btn, e) { - var window = new MODx.window.CreateContentType({ - record: this.menu.record - ,title: _('edit') - ,action: 'System/ContentType/Update' - ,listeners: { + text: _('edit'), + handler: function(btn, e) { + const window = new MODx.window.CreateContentType({ + record: this.menu.record, + title: _('edit'), + action: 'System/ContentType/Update', + isUpdate: true, + listeners: { success: { - fn: this.refresh - ,scope: this + fn: this.refresh, + scope: this } } }); window.setRecord(this.menu.record); window.show(e.target); - } - ,scope: this + }, + scope: this }); m.push({ - text: _('delete') - ,handler: this.confirm.createDelegate(this,['System/ContentType/Remove',_('content_type_remove_confirm')]) + text: _('delete'), + handler: this.confirm.createDelegate(this, ['System/ContentType/Remove', _('content_type_remove_confirm')]) }); return m; @@ -163,121 +164,115 @@ Ext.reg('modx-grid-content-type', MODx.grid.ContentType); * @param {Object} config An object of options. * @xtype modx-window-content-type-create */ -MODx.window.CreateContentType = function(config) { - config = config || {}; - this.ident = config.ident || 'modx-cct'+Ext.id(); - Ext.applyIf(config,{ - title: _('create') - ,width: 600 - ,url: MODx.config.connector_url - ,action: 'System/ContentType/Create' - ,bwrapCssClass: 'x-window-with-tabs' - ,fields: [{ - xtype: 'modx-tabs' - ,items: [{ - title: _('content_type_main_tab') - ,layout: 'form' - ,items: [{ - layout: 'column' - ,border: false - ,defaults: { - layout: 'form' - ,labelAlign: 'top' - ,anchor: '100%' - ,border: false - } - ,items: [{ - columnWidth: .6 - ,defaults: { +MODx.window.CreateContentType = function(config = {}) { + const action = config.isUpdate ? 'update' : 'create'; + this.itemId = `window-content-type-${action}-${Ext.id()}`; + Ext.applyIf(config, { + title: _('create'), + width: 600, + url: MODx.config.connector_url, + action: 'System/ContentType/Create', + bwrapCssClass: 'x-window-with-tabs', + fields: [{ + xtype: 'modx-tabs', + items: [{ + title: _('content_type_main_tab'), + layout: 'form', + items: [{ + layout: 'column', + border: false, + defaults: { + layout: 'form', + labelAlign: 'top', + anchor: '100%', + border: false + }, + items: [{ + columnWidth: 0.6, + defaults: { msgTarget: 'under' - } - ,items: [{ - xtype: 'hidden' - ,name: 'id' - },{ - fieldLabel: _('name') - ,name: 'name' - ,id: this.ident+'-name' - ,xtype: 'textfield' - ,anchor: '100%' - ,allowBlank: false - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: this.ident+'-name' - ,html: _('name_desc') - ,cls: 'desc-under' - },{ - fieldLabel: _('mime_type') - ,description: MODx.expandHelp ? '' : _('mime_type_desc') - ,name: 'mime_type' - ,id: this.ident+'-mime-type' - ,xtype: 'textfield' - ,anchor: '100%' - ,allowBlank: false - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: this.ident+'-mime-type' - ,html: _('mime_type_desc') - ,cls: 'desc-under' + }, + items: [{ + xtype: 'hidden', + name: 'id' + }, { + fieldLabel: _('name'), + name: 'name', + xtype: 'textfield', + anchor: '100%', + allowBlank: false + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('name_desc'), + cls: 'desc-under' + }, { + fieldLabel: _('mime_type'), + description: MODx.expandHelp ? '' : _('mime_type_desc'), + name: 'mime_type', + xtype: 'textfield', + anchor: '100%', + allowBlank: false + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('mime_type_desc'), + cls: 'desc-under' }] - },{ - columnWidth: .4 - ,defaults: { + }, { + columnWidth: 0.4, + defaults: { msgTarget: 'under' - } - ,items: [{ - fieldLabel: _('icon') - ,description: MODx.expandHelp ? '' : _('icon_desc') - ,name: 'icon' - ,id: this.ident+'-icon' - ,xtype: 'textfield' - ,anchor: '100%' - ,allowBlank: true - },{ - fieldLabel: _('file_extensions') - ,description: MODx.expandHelp ? '' : _('file_extensions_desc') - ,name: 'file_extensions' - ,id: this.ident+'-file-extensions' - ,xtype: 'textfield' - ,anchor: '100%' - ,allowBlank: true - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: this.ident+'-file-extensions' - ,html: _('file_extensions_desc') - ,cls: 'desc-under' + }, + items: [{ + fieldLabel: _('icon'), + description: MODx.expandHelp ? '' : _('icon_desc'), + name: 'icon', + xtype: 'textfield', + anchor: '100%', + allowBlank: true + }, { + fieldLabel: _('file_extensions'), + description: MODx.expandHelp ? '' : _('file_extensions_desc'), + name: 'file_extensions', + xtype: 'textfield', + anchor: '100%', + allowBlank: true + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('file_extensions_desc'), + cls: 'desc-under' }] }] - },{ - xtype: 'xcheckbox' - ,hideLabel: true - ,boxLabel: _('binary_desc') - ,name: 'binary' - ,hiddenName: 'binary' - ,id: this.ident+'-binary' - ,anchor: '100%' - },{ - fieldLabel: _('description') - ,name: 'description' - ,id: 'modx-'+this.ident+'-description' - ,xtype: 'textarea' - ,anchor: '100%' - ,grow: true - },{ - xtype: 'hidden' - ,name: 'headers' + }, { + xtype: 'xcheckbox', + hideLabel: true, + boxLabel: _('binary_desc'), + name: 'binary', + hiddenName: 'binary', + anchor: '100%' + }, { + fieldLabel: _('description'), + name: 'description', + xtype: 'textarea', + anchor: '100%', + grow: true + }, { + xtype: 'hidden', + name: 'headers' }] - },{ - title: _('content_type_header_tab') - ,layout: 'anchor' - ,anchor: '100%' - ,items: [{ - xtype: 'modx-content-type-headers-grid' - ,id: 'headers' + }, { + title: _('content_type_header_tab'), + layout: 'anchor', + anchor: '100%', + items: [{ + xtype: 'modx-content-type-headers-grid', + id: 'headers' }] }] - }] - ,keys: [] + }], + keys: [] }); MODx.window.CreateContentType.superclass.constructor.call(this, config); From d745eb5ce49ff13f5a6c7817496a4f347561071a Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:30:54 -0400 Subject: [PATCH 09/12] Remove values js library Now that user changes to color and opacity are interactive via new config window, no need for this library (for calculating color variants) --- manager/assets/lib/values.min.js | 19 -------- manager/templates/default/header.tpl | 72 +++++++++++++--------------- 2 files changed, 33 insertions(+), 58 deletions(-) delete mode 100644 manager/assets/lib/values.min.js diff --git a/manager/assets/lib/values.min.js b/manager/assets/lib/values.min.js deleted file mode 100644 index 39a9e3f59f3..00000000000 --- a/manager/assets/lib/values.min.js +++ /dev/null @@ -1,19 +0,0 @@ -!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e=e||self).Values=r()}(this,(function(){"use strict";var e={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},r=new RegExp("[^#a-f\\d]","gi"),n=new RegExp("^#?[a-f\\d]{3}[a-f\\d]?$|^#?[a-f\\d]{6}([a-f\\d]{2})?$","i"),t=new RegExp(/^#([a-f0-9]{3,4}|[a-f0-9]{4}(?:[a-f0-9]{2}){1,2})\b$/,"i"),a="-?\\d*(?:\\.\\d+)",i="("+a+"?)",o="("+a+"?%)",s=("^\n hsla?\\(\n \\s*(-?\\d*(?:\\.\\d+)?(?:deg|rad|turn)?)\\s*,\n \\s*"+o+"\\s*,\n \\s*"+o+"\\s*\n (?:,\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),l=new RegExp(s),u=("^\n hsla?\\(\n \\s*(-?\\d*(?:\\.\\d+)?(?:deg|rad|turn)?)\\s*\n \\s+"+o+"\n \\s+"+o+"\n \\s*(?:\\s*\\/\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),h=new RegExp(u),d=("^\n rgba?\\(\n \\s*"+i+"\\s*,\n \\s*"+i+"\\s*,\n \\s*"+i+"\\s*\n (?:,\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),g=new RegExp(d),p=("^\n rgba?\\(\n \\s*"+o+"\\s*,\n \\s*"+o+"\\s*,\n \\s*"+o+"\\s*\n (?:,\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n $\n").replace(/\n|\s/g,""),c=new RegExp(p),f=("^\n rgba?\\(\n \\s*"+i+"\n \\s+"+i+"\n \\s+"+i+"\n \\s*(?:\\s*\\/\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n$\n").replace(/\n|\s/g,""),v=new RegExp(f),b=("^\n rgba?\\(\n \\s*"+o+"\n \\s+"+o+"\n \\s+"+o+"\n \\s*(?:\\s*\\/\\s*(-?\\d*(?:\\.\\d+)?%?)\\s*)?\n \\)\n$\n").replace(/\n|\s/g,""),m=new RegExp(b),y=new RegExp(/^transparent$/,"i"),w=function(e,r,n){return Math.min(Math.max(r,e),n)},k=function(e){var r=e;return"number"!=typeof r&&(r=r.endsWith("%")?255*parseFloat(r)/100:parseFloat(r)),w(Math.round(r),0,255)},x=function(e){return w(parseFloat(e),0,100)};function M(e){var r=e;return"number"!=typeof r&&(r=r.endsWith("%")?parseFloat(r)/100:parseFloat(r)),w(r,0,1)}function E(e){var t=function(e,t){if(void 0===t&&(t={}),"string"!=typeof e||r.test(e)||!n.test(e))throw new TypeError("Expected a valid hex string");var a=1;8===(e=e.replace(/^#/,"")).length&&(a=parseInt(e.slice(6,8),16)/255,e=e.slice(0,6)),4===e.length&&(a=parseInt(e.slice(3,4).repeat(2),16)/255,e=e.slice(0,3)),3===e.length&&(e=e[0]+e[0]+e[1]+e[1]+e[2]+e[2]);var i=parseInt(e,16),o=i>>16,s=i>>8&255,l=255&i;return"array"===t.format?[o,s,l,a]:{red:o,green:s,blue:l,alpha:a}}(e,{format:"array"});return F([null,t[0],t[1],t[2],t[3]])}function F(e){var r=e[1],n=e[2],t=e[3],a=e[4];return void 0===a&&(a=1),{type:"rgb",values:[r,n,t].map(k),alpha:M(null===a?1:a)}} -/** - * parse-css-color - * @version v0.1.2 - * @link http://github.com/noeldelgado/parse-css-color/ - * @license MIT - */var R=function(r){if("string"!=typeof r)return null;var n=t.exec(r);if(n)return E(n[0]);var a=h.exec(r)||l.exec(r);if(a)return function(e){var r=e[1],n=e[2],t=e[3],a=e[4];void 0===a&&(a=1);var i=r;return{type:"hsl",values:[i=i.endsWith("turn")?360*parseFloat(i)/1:i.endsWith("rad")?Math.round(180*parseFloat(i)/Math.PI):parseFloat(i),x(n),x(t)],alpha:M(null===a?1:a)}}(a);var i=v.exec(r)||m.exec(r)||g.exec(r)||c.exec(r);if(i)return F(i);if(y.exec(r))return F([null,0,0,0,0]);var o=e[r.toLowerCase()];return o?F([null,o[0],o[1],o[2],1]):null};var $=function(e){var r,n,t,a,i,o=e[0]/360,s=e[1]/100,l=e[2]/100;if(0==s)return[i=255*l,i,i];r=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(t=o+1/3*-(u-1))<0&&t++,t>1&&t--,i=6*t<1?r+6*(n-r)*t:2*t<1?n:3*t<2?r+(n-r)*(2/3-t)*6:r,a[u]=255*i;return a};var q=function(e,r,n){return Math.min(Math.max(e,r),n)};function S(e){var r=Math.round(q(e,0,255)).toString(16);return 1==r.length?"0"+r:r}var I=function(e){var r=4===e.length?S(255*e[3]):"";return"#"+S(e[0])+S(e[1])+S(e[2])+r};var j=function(e){var r,n,t=e[0]/255,a=e[1]/255,i=e[2]/255,o=Math.min(t,a,i),s=Math.max(t,a,i),l=s-o;return s==o?r=0:t==s?r=(a-i)/l:a==s?r=2+(i-t)/l:i==s&&(r=4+(t-a)/l),(r=Math.min(60*r,360))<0&&(r+=360),n=(o+s)/2,[r,100*(s==o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]}; -/** - * mix-css-color - * @version v0.1.1 - * @link http://github.com/noeldelgado/mix-css-color/ - * @license MIT - */function C(e){var r=R(e);return null===r?null:("hsl"===r.type&&(r.values=$(r.values)),r)}function W(e,r,n){void 0===n&&(n=50);var t=C(e),a=C(r);if(!t||!a)return null;var i=Math.min(Math.max(0,n),100)/100,o=2*i-1,s=t.alpha-a.alpha,l=((o*s==-1?o:(o+s)/(1+o*s))+1)/2,u=1-l,h=t.values.map((function(e,r){return Math.round(t.values[r]*l+a.values[r]*u)})),d=h[0],g=h[1],p=h[2],c=parseFloat((t.alpha*i+a.alpha*(1-i)).toFixed(8));return{hex:I([d,g,p]),hexa:I([d,g,p,c]),rgba:[d,g,p,c],hsla:j([d,g,p]).map(Math.round).concat([c])}} -/** - * values.js - Get the tints and shades of a color - * @version v2.0.0 - * @link http://noeldelgado.github.io/values.js/ - * @license MIT - */var _=function(e,r){return null===e||isNaN(e)||"string"==typeof e?r:e},N=function(e,r,n){var t;void 0===e&&(e="#000"),void 0===r&&(r="base"),void 0===n&&(n=0),t=[[0,0,0],1,r,n],this.rgb=t[0],this.alpha=t[1],this.type=t[2],this.weight=t[3];var a=null===e?"#000":e;if("string"!=typeof a)throw new TypeError("Input should be a string: "+a);var i=R(a);if(!i)throw new Error("Unable to parse color from string: "+a);return this["_setFrom"+i.type.toUpperCase()](i.values.concat([i.alpha]))},O={hex:{configurable:!0}};return O.hex.get=function(){return this.hexString().replace(/^#/,"")},N.prototype.setColor=function(e){var r=R(e);return r?this["_setFrom"+r.type.toUpperCase()](r.values.concat([r.alpha])):null},N.prototype.tint=function(e,r){return void 0===r&&(r=_(e,50)),new N("rgb("+W("#fff",this.rgbString(),r).rgba+")","tint",r)},N.prototype.shade=function(e,r){return void 0===r&&(r=_(e,50)),new N("rgb("+W("#000",this.rgbString(),r).rgba+")","shade",r)},N.prototype.tints=function(e,r){var n=this;return void 0===r&&(r=_(e,10)),Array.from({length:100/r},(function(e,t){return n.tint((t+1)*r)}))},N.prototype.shades=function(e,r){var n=this;return void 0===r&&(r=_(e,10)),Array.from({length:100/r},(function(e,t){return n.shade((t+1)*r)}))},N.prototype.all=function(e){return void 0===e&&(e=10),this.tints(e).reverse().concat([Object.assign(this)],this.shades(e))},N.prototype.hexString=function(){return I(this.alpha>=1?this.rgb:this.rgb.concat([this.alpha]))},N.prototype.rgbString=function(){var e=(this.alpha>=1?this.rgb:this.rgb.concat([this.alpha])).join(", ");return(this.alpha>=1?"rgb":"rgba")+"("+e+")"},N.prototype.getBrightness=function(){return Math.round(this.rgb.reduce((function(e,r){return e+r}))/765*100)},N.prototype._setFromRGB=function(e){var r;return r=[[e[0],e[1],e[2]],e[3]],this.rgb=r[0],this.alpha=r[1],this},N.prototype._setFromHSL=function(e){var r,n=e[0],t=e[1],a=e[2],i=e[3];return r=[$([n,t,a]).map(Math.round),i],this.rgb=r[0],this.alpha=r[1],this},Object.defineProperties(N.prototype,O),N.VERSION="v2.0.0",N})); diff --git a/manager/templates/default/header.tpl b/manager/templates/default/header.tpl index a90993ae8ce..a55d73b720d 100644 --- a/manager/templates/default/header.tpl +++ b/manager/templates/default/header.tpl @@ -20,51 +20,12 @@ {/if} - @@ -115,6 +76,39 @@ fn }); } + // Must instantiate mask module here, as it needs the config to have already been created, which occurs after modx.js is loaded + MODx.maskConfig = new MODx.MaskManager(); + if (!MODx.maskConfig.hasSessionConfig) { + MODx.maskConfig.createSessionConfig(); + } + /* + Create the session storage + + localStoageItem: { + // Current gets created first load (copy of fallbacks below) + // Current gets updated settings crud events or changes in mask config window + current: { + modal: { + { ... 3 attrs } + }, + pseudomodal: { + { ... 3 attrs } + } + }, + // Fallbacks change on settings crud events, saving the highest precedence + // setting for each attr changed here (high to low = user > usergroup > system) + // Created initially with settings or defaults gathered after login + // Use to revert changes kept in LC to initial state + fallbacks: { + modal: { + { ... 3 attrs } + }, + pseudomodal: { + { ... 3 attrs } + } + } + } + */ {/literal} From 52e9fb587de22d7e667d257b47d5f0b6955d1b83 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:32:32 -0400 Subject: [PATCH 10/12] Base js class updates --- manager/assets/modext/core/modx.js | 671 ++++++++++- manager/assets/modext/util/utilities.js | 58 + .../assets/modext/widgets/core/modx.grid.js | 2 + .../assets/modext/widgets/core/modx.panel.js | 102 +- .../assets/modext/widgets/core/modx.window.js | 1026 +++++++++++------ 5 files changed, 1449 insertions(+), 410 deletions(-) diff --git a/manager/assets/modext/core/modx.js b/manager/assets/modext/core/modx.js index 7c498e7ee69..3de017efd5a 100644 --- a/manager/assets/modext/core/modx.js +++ b/manager/assets/modext/core/modx.js @@ -372,11 +372,15 @@ Ext.extend(MODx,Ext.Component,{ ,login_context: 'mgr' } ,listeners: { - 'success': {fn:function(r) { - if (this.fireEvent('afterLogout',r)) { - location.href = './'; - } - },scope:this} + success: { + fn: function(r) { + MODx.maskConfig.destroySessionConfig(); + if (this.fireEvent('afterLogout', r)) { + window.location.href = './'; + } + }, + scope: this + } } }); } @@ -855,6 +859,663 @@ Ext.reg('modx-ajax',MODx.Ajax); MODx = new MODx(); +/** + * Used to fetch and control window and modal backdrops, as well as grid masks. + * Note: This class is instantiated after the full MODx config has been loaded (currently in header.tpl) + * @param {Object} config + */ +MODx.MaskManager = function(config = {}) { + this.settingsKeys = { + modal: { + disabled: 'mask_disabled_modal', + color: 'mask_color_modal', + opacity: 'mask_opacity_modal' + }, + pseudomodal: { + disabled: 'mask_disabled_pseudomodal', + color: 'mask_color_pseudomodal', + opacity: 'mask_opacity_pseudomodal' + } + }; + this.settingsXtypes = { + disabled: 'combo-boolean', + color: 'textfield', + opacity: 'numberfield' + }; + Ext.apply(config, { + attributes: { + modal: { + disabled: MODx.util.Types.castToBoolean(MODx.config.mask_disabled_modal), + color: MODx.config.mask_color_modal || '#ffffff', + opacity: parseInt(MODx.config.mask_opacity_modal) / 100 || 0.5 + }, + pseudomodal: { + disabled: MODx.util.Types.castToBoolean(MODx.config.mask_disabled_pseudomodal), + color: MODx.config.mask_color_pseudomodal || '#0d141d', + opacity: parseInt(MODx.config.mask_opacity_pseudomodal) / 100 || 0.5 + }, + grid: { + disabled: false, + color: MODx.config.mask_color_grid || '#ffffff', + opacity: parseInt(MODx.config.mask_opacity_grid) / 100 || 0.5 + } + } + }); + this.config = config; + MODx.MaskManager.superclass.constructor.call(this, config); + this.addEvents({ + actionsReady: false, + actionsDone: false, + actionsFail: false + }); + this.on({ + actionsReady: function() { + // console.log('Continuing ... writing changes ... this:', this); + this.commitSettingsChanges(); + }, + actionsDone: function() { + // MODx.msg.status({ + // title: 'Action Complete', + // message: 'Updates to your mask configuration settings were successful!' + // }); + console.log('actionsDone :: this', this); + if (this.saveStatus) { + this.saveStatus.exit(); + } + }, + actionsFail: function(response) { + // MODx.msg.status({ + // title: 'Action Complete', + // message: 'Updates to your mask configuration settings were successful!' + // }); + console.log('actionsFail :: response', response); + if (this.saveStatus) { + this.saveStatus.exit(); + } + }, + /* + @ NEW SESSION: + - MODx.config will have all correct settings vals + + @ GRID SETTINGS CHANGE: + If to User... + - This one's easy, as User has top precedence; update session and + cache with new value without additional checks + If to Usergroup... + - If no matching key exists for User + ? Is User in multiple groups (which takes precedence?) + Y - Check for key in higher precedence group, if any + N - Update session data and overrides obj + ? How to check for current User keys + 1 - Always via db query, OR + 2 - Query db at session start, add key(s) if any to + session LS history, then add/remove as needed on + change (session overrides obj, no db queries needed) + + ## TRACK KEY EXISTENCE at User and Usergroup levels with overrides obj + overrides: { + user: ['key1', 'key2', ...], + groups: { + // obj keys correspond to usergroup id + 1: ['key1', 'key2', ...], + 3: ['key1', 'key2', ...], + ... + } + } + */ + /** + * Fired after direct changes (via settings grids) to mask configuration + * are made. Triggers update of the mask session values as needed + * to ensure changes are immediately reflected in the UI (without reloading) + * @param {Object} response The post-save response data + */ + syncSettingFromGrid: function(response) { + console.log('createSettingFromGrid :: response', response); + const { action, data, gridType } = response; + if (typeof data?.key?.length) { + const { type, attribute } = this.getMaskPropNamesFromKey(data.key); + console.log(` + syncSettingFromGrid :: + Action: ${action} + Mask type: ${type} + Mask attr: ${attribute} + Grid Type: ${gridType} + `); + if (type && attribute) { + /* + Note that for the session values, we want the decimal opacity + value (for direct use in css) instead of the whole number equivalent + which is used for the setting value itself + */ + const + value = action === 'remove' ? this.getMaskAttribute(type, attribute, true) : data.value, + rawValue = attribute === 'opacity' && value > 1 ? value / 100 : value, + newValue = this.prepareSettingValue(data.xtype, rawValue) + ; + if (this.hasSessionConfig) { + const overridesData = { + action: action, + gridType: gridType, + key: data.key + }; + switch(gridType) { + case 'user': + case 'system': + // do something + break; + case 'usergroup': + if (data?.group && MODx.config.user_usergroups.includes(data.group)) { + console.log(`Sync settings for usergroup ${data.group}`); + overridesData.groupId = data.group + } + break; + // no default + } + this.updateSessionConfig(type, { + [attribute]: newValue + }, true, overridesData); + } + /* + When enabling previously disabled mask via the settings grids, + the mask element will need to be created here to ensure it + appears on subsequent window openings (without reloading page) + */ + /* + let mask = document.querySelector(`.ext-el-mask.${type}`); + if (!mask) { + const referenceEl = Ext.getBody().last(); + // console.log('Trying to create mask before this el:', referenceEl); + mask = MODx.maskConfig.createMask(referenceEl, type); + } + if (attribute === 'color') { + mask.style.backgroundColor = newValue; + } + */ + } + } + }, + createSettingFromGrid: function(data) { + console.log('createSettingFromGrid :: data', data); + }, + + updateSettingFromGrid: function(data) { + console.log('updateSettingFromGrid :: data', data); + if (typeof data?.key?.length) { + const { type, attribute } = this.getMaskPropNamesFromKey(data.key); + console.log(` + updateSettingFromGrid :: + Mask type: ${type} + Mask attr: ${attribute} + `); + if (type && attribute) { + /* + Note that for the session values, we want the decimal opacity + value (for direct use in css) instead of the whole number equivalent + which is used for the setting value itself + */ + const + rawValue = attribute === 'opacity' && data.value > 1 ? data.value / 100 : data.value, + newValue = this.prepareSettingValue(data.xtype, rawValue) + ; + if (this.hasSessionConfig) { + this.updateSessionConfig(type, { + [attribute]: newValue + }); + } + /* + When enabling previously disabled mask via the settings grids, + the mask element will need to be created here to ensure it + appears on subsequent window openings (without reloading page) + */ + let mask = document.querySelector(`.ext-el-mask.${type}`); + if (!mask) { + const referenceEl = Ext.getBody().last(); + // console.log('Trying to create mask before this el:', referenceEl); + mask = MODx.maskConfig.createMask(referenceEl, type); + } + if (attribute === 'color') { + mask.style.backgroundColor = newValue; + } + } + } + }, + /** + * Upon setting removal, updates the session mask config (if present) + * with the appropriate fallback value for the removed setting; + * @param {Object} data + */ + removeSettingFromGrid: function(data) { + console.log('removeSettingFromGrid :: data', data); + const record = data.record; + if (typeof record?.key?.length) { + const { type, attribute } = this.getMaskPropNamesFromKey(record.key); + console.log(` + removeSettingFromGrid :: + Mask type: ${type} + Mask attr: ${attribute} + `); + if (type && attribute) { + /* + Note that for the session values, we want the decimal opacity + value (for direct use in css) instead of the whole number equivalent + which is used for the setting value itself + */ + const + // rawValue = attribute === 'opacity' && data.value > 1 ? data.value / 100 : data.value, + value = this.getMaskAttribute(type, attribute, true), + rawValue = attribute === 'opacity' && value > 1 ? value / 100 : value, + newValue = this.prepareSettingValue(record.xtype, rawValue) + ; + if (this.hasSessionConfig) { + this.updateSessionConfig(type, { + [attribute]: newValue + }); + } + /* + When enabling previously disabled mask via the settings grids, + the mask element will need to be created here to ensure it + appears on subsequent window openings (without reloading page) + */ + let mask = document.querySelector(`.ext-el-mask.${type}`); + if (!mask) { + const referenceEl = Ext.getBody().last(); + // console.log('Trying to create mask before this el:', referenceEl); + mask = MODx.maskConfig.createMask(referenceEl, type); + } + if (attribute === 'color') { + mask.style.backgroundColor = newValue; + } + } + } + } + }); +}; +Ext.extend(MODx.MaskManager, Ext.Component, { + sessionMaskKey: 'sessionMaskConfig', + cache: {}, + hasSessionConfig: false, + saveStatus: null, + /** + * + * @param {Ext.Element} reference The element this mask should be inserted before + * @param {String} type The window type + * @param {String} event + * @param {*} returnMask + * @returns + */ + createMask: function(reference, type = 'pseudomodal', event = 'render', returnMask = true) { + let ready; + // Note that window reference components will have an el property + // while other general elements will not + const insertBefore = reference?.el?.dom || reference.dom; + if (type === 'pseudomodal') { + ready = event === 'render' + ? MODx.openPseudoModals.length === 0 + : MODx.openPseudoModals.length >= 1 + ; + if (ready && MODx.util.isEmptyObject(MODx.mask)) { + MODx.mask = Ext.getBody().createChild({ cls: 'ext-el-mask pseudomodal' }, insertBefore); + MODx.mask.setStyle('background-color', MODx.maskConfig.getMaskAttribute('pseudomodal', 'color')); + MODx.mask.hide(); + if (returnMask) { + return MODx.mask; + } + } + } + }, + /** + * Get a mask's css value (or disabled status) based on its window type and attribute + * @param {String} type The window type (modal, pseudomodal) + * @param {String} attribute The mask attribute to get (color, opacity, disabled) + * @returns The current value of the requested attribute + */ + getMaskAttribute: function(type, attribute, getFallback = false) { + const sessionBranch = getFallback ? 'fallback' : 'current' ; + // if (!getFallback && !MODx.util.isEmptyObject(this.cache)) { + // console.log(`Getting attr from cache (${sessionBranch} branch) ...`, this.cache); + // return this.cache.attributes[type][attribute]; + // } + if (!MODx.util.isEmptyObject(this.cache)) { + console.log(`Getting attr from cache (${sessionBranch} branch) ...`, this.cache); + return this.cache[sessionBranch][type][attribute]; + } + const sessionConfig = this.getSessionConfig(); + if (!sessionConfig) { + console.log(`Getting ${type} ${attribute} from initial config: (${typeof this.attributes[type][attribute]}) ${this.attributes[type][attribute]}; MODx config val = (${typeof MODx.config[this.settingsKeys[type][attribute]]}) ${MODx.config[this.settingsKeys[type][attribute]]}`); + return this.attributes[type][attribute]; + } + console.log(`Getting attr from session storage (${sessionBranch} branch) ...`, this.cache); + // return sessionConfig?.attributes[type][attribute]; + return sessionConfig[sessionBranch][type][attribute]; + }, + createSessionConfig: function() { + console.log('Initial MODx config:', MODx.config); + const config = { + current: this.config.attributes, + fallback: this.config.attributes, + overrides: { + user: [], + groups: new Map() + } + }; + MODx.config.user_usergroups.forEach(groupId => { + config.overrides.groups.set(groupId, []); + }); + + console.log('session config skel:', config); + this.saveSessionConfig(config); + this.hasSessionConfig = true; + }, + saveSessionConfig: function(config) { + this.cache = config; + localStorage.setItem(this.sessionMaskKey, JSON.stringify(config, MODx.util.JsonTools.mapReplacer)); + }, + getSessionConfig: function() { + let sessionConfig = localStorage.getItem(this.sessionMaskKey); + if (!sessionConfig) { + this.hasSessionConfig = false; + return false; + } + sessionConfig = JSON.parse(sessionConfig, MODx.util.JsonTools.mapReviver); + if (MODx.util.isEmptyObject(this.cache)) { + this.cache = sessionConfig; + } + this.hasSessionConfig = true; + return sessionConfig; + }, + destroySessionConfig: function() { + localStorage.removeItem(this.sessionMaskKey); + }, + clearSessionConfig: function() { + localStorage.removeItem(this.sessionMaskKey); + this.hasSessionConfig = false; + }, + updateSessionConfig: function(type, config, updateFallback = false, overridesData = null) { + let sessionConfig = this.getSessionConfig(); + if (!sessionConfig) { + sessionConfig = this.config; + } + Object.keys(config).forEach(key => { + // console.log(`Updating ${type} key (${key} to ${config[key]})`); + // sessionConfig.attributes[type][key] = config[key]; + sessionConfig.current[type][key] = config[key]; + if (updateFallback) { + sessionConfig.fallback[type][key] = config[key]; + } + }); + if (overridesData) { + const + keyList = sessionConfig.overrides.groups.get(overridesData.groupId), + keyListHasKey = keyList.includes(overridesData.key) + ; + // console.log(`Override keyList for group ${overridesData.groupId}`, keyList); + if (overridesData.action === 'remove' && keyListHasKey) { + keyList = keyList.filter(key => key !== overridesData.key); + } else if (['create', 'update'].includes(overridesData.action) && !keyListHasKey) { + keyList.push(overridesData.key); + } + sessionConfig.overrides.groups.get(overridesData.groupId, keyList); + } + // this.cache = sessionConfig; + // localStorage.setItem(this.sessionMaskKey, JSON.stringify(sessionConfig)); + // this.hasSessionConfig = true; + this.saveSessionConfig(sessionConfig); + }, + /** + * Get the mask type and attribute prop names based on a settings key + * @param {String} queryKey The settings key being processed + * @returns {Object} + */ + getMaskPropNamesFromKey: function(queryKey) { + const props = { + type: null, + attribute: null + }; + for (const maskType in this.settingsKeys) { + const result = Object.keys(this.settingsKeys[maskType]).find(key => this.settingsKeys[maskType][key] === queryKey); + if (result) { + props.type = maskType; + props.attribute = result; + break; + } + } + return props; + }, + /** + * Prepare global/user setting values for comparison to form values + * and/or for updating the session configuration + * @param {String} xtype The Ext xtype for the setting's editor + * @param {Boolean} initialValue Current setting value retrieved from config or database + */ + prepareSettingValue: function(xtype, initialValue = null) { + let value = initialValue; + if (xtype.includes('number')) { + value = parseFloat(value); + } else if (xtype.includes('boolean')) { + value = MODx.util.Types.castToBoolean(value); + } + return value; + }, + updateSystemSettings: function(windowType, settingsTarget, values, userId) { + const + params = { + namespace: 'core', + area: 'manager' + }, + exitDelay = 150, + /** + * + */ + SetActionMap = target => { + const + currentSettings = { + user: {}, + global: {} + }, + buildMap = (target, settings) => { + this.settingsMap.keys.forEach(key => { + const + userSettingExists = Object.hasOwn(settings.user, key), + globalSettingExists = Object.hasOwn(settings.global, key), + userSettingSaveAction = userSettingExists ? 'update' : 'create', + globalSettingSaveAction = globalSettingExists ? 'update' : 'create', + payload = { + ...params, + key: key, + value: this.valuesMap[key], + xtype: this.settingsMap.xtypes[key], + status: 0 + } + ; + if (target === 'user') { + // Remove setting if it matches the global setting + if (userSettingExists && settings.global[key] === this.valuesMap[key]) { + this.actionMap.user.delete.push({ + key: key, + user: MODx.config.user, + status: 0 + }); + this.actionMap.totalActions++; + // Create or update otherwise + } else if ( + (!userSettingExists && settings.global[key] !== this.valuesMap[key]) + || (userSettingExists && settings.user[key] !== this.valuesMap[key]) + ) { + this.actionMap.user[userSettingSaveAction].push({ + ...payload, + user: MODx.config.user, + }); + this.actionMap.totalActions++; + } + } + // Remove user settings since, in this case, they would match the global one being updated + if (target === 'both' && userSettingExists) { + this.actionMap.user.delete.push({ + key: key, + user: MODx.config.user, + status: 0 + }); + this.actionMap.totalActions++; + } + // Handle global settings for all targets; note that we elect to re-create the global key/value if it's missing + if (!globalSettingExists || (['both', 'global'].includes(target) && settings.global[key] !== this.valuesMap[key])) { + this.actionMap.global[globalSettingSaveAction].push(payload); + this.actionMap.totalActions++; + } + }); + + } + ; + this.settingsMap.keys.forEach(key => { + currentSettings.global[key] = this.prepareSettingValue(this.settingsMap.xtypes[key], MODx.config[key]); + }); + + // Fetch user settings to determine which ones are present and can be acted upon + MODx.Ajax.request({ + url: MODx.config.connector_url, + params: { + ...params, + action: 'Security/User/Setting/GetListIn', + user: MODx.config.user, + keys: JSON.stringify(this.settingsMap.keys) + }, + listeners: { + success: { + fn: function(response) { + response.results.forEach(result => { + if (this.settingsMap.keys.includes(result.key)) { + currentSettings.user[result.key] = this.prepareSettingValue(this.settingsMap.xtypes[result.key], result.value); + } + }); + buildMap(target, currentSettings); + this.fireEvent('actionsReady'); + }, + scope: this + }, + failure: { + fn: function(response) { + this.fireEvent('actionsFail', response); + }, + scope: this + } + } + }); + } + ; + this.saveStatus = new MODx.window.SaveProgress({ exitDelay }) + // start status window + this.saveStatus.init(); + + this.settingsMap = { + keys: [], + xtypes: {} + }; + this.valuesMap = {}; + this.actionMap = { + totalActions: 0, + actionErrors: [], + user: { + create: [], + update: [], + delete: [] + }, + global: { + create: [], + update: [] + } + }; + Object.entries(values).forEach(([key, value]) => { + const settingKey = this.settingsKeys[windowType][key]; + this.settingsMap.keys.push(settingKey); + this.settingsMap.xtypes[settingKey] = this.settingsXtypes[key]; + if (settingKey.includes('_opacity')) { + value = value <= 1 ? parseInt(value * 100) : value ; + } + this.valuesMap[settingKey] = value; + }); + SetActionMap(settingsTarget); + }, + commitSettingsChanges: function(target) { + console.log('commitSettingsChanges :: actionMap', this.actionMap); + const + userActionBase = 'Security/User/Setting/', + globalActionBase = 'System/Settings/', + processorsMap = { + create: 'Create', + update: 'Update', + delete: 'Remove' + }, + onSuccess = response => { + taskSuccesses++; + taskIndex++; + console.log(` + - - onSuccess - - + Incrementing success count to: ${taskSuccesses} + Completed ${taskIndex} of ${this.actionMap.totalActions} actions + + `, response); + if (taskIndex === this.actionMap.totalActions) { + this.fireEvent('actionsDone'); + } + }, + onFailure = response => { + taskFailures++; + taskIndex++; + console.log(` + - - onFailure - - + Dang it, something went wrong!!! + `, response); + // this.fireEvent('actionsFail', response); + }, + baseRequest = { + url: MODx.config.connector_url, + listeners: { + success: { fn: onSuccess }, + failure: { fn: onFailure } + } + } + ; + let + taskIndex = 0, + taskSuccesses = 0, + taskFailures = 0 + ; + + for (const action in this.actionMap.user) { + // console.log('User action processing: ', action); + const + tasks = this.actionMap.user[action], + actionParam = userActionBase + processorsMap[action] + ; + if (tasks.length > 0) { + tasks.forEach(params => { + const request = { + ...baseRequest, + params: { ...params, action: actionParam } + }; + // console.log('Full request obj:', request); + MODx.Ajax.request(request); + }); + } + } + for (const action in this.actionMap.global) { + // console.log('Global action processing: ', action); + const + tasks = this.actionMap.global[action], + actionParam = globalActionBase + processorsMap[action] + ; + if (tasks.length > 0) { + tasks.forEach(params => { + MODx.Ajax.request({ + ...baseRequest, + params: { ...params, action: actionParam } + }); + }); + } + } + + } +}); MODx.form.Handler = function(config) { config = config || {}; diff --git a/manager/assets/modext/util/utilities.js b/manager/assets/modext/util/utilities.js index 679bcc23102..a50ebdb6a03 100644 --- a/manager/assets/modext/util/utilities.js +++ b/manager/assets/modext/util/utilities.js @@ -739,6 +739,64 @@ MODx.util.url = { } }; +MODx.util.Color = { + rgbToHex: rgbString => { + if (rgbString.indexOf('#') === 0) { + return rgbString; + } + const + sep = rgbString.indexOf(',') > -1 ? ',' : ' ', + rgbValues = rgbString.substr(4).split(')')[0].split(sep) + ; + let r = (+rgbValues[0]).toString(16), + g = (+rgbValues[1]).toString(16), + b = (+rgbValues[2]).toString(16); + if (r.length === 1) { r = `0${r}`; } + if (g.length === 1) { g = `0${g}`; } + if (b.length === 1) { b = `0${b}`; } + + return `#${r}${g}${b}`; + } +}; + +MODx.util.Types = { + castToBoolean: value => !( + (typeof value === 'string' && (['0', 'false', 'no'].includes(value.toLowerCase()))) + || value === false + || value === 0 + || (Ext.isObject(value) && MODx.util.isEmptyObject(value)) + || Ext.isEmpty(value) + ) +}; + +MODx.util.isEmptyObject = obj => { + if (!Ext.isObject(obj)) { + console.warn('The item passed to isEmptyObject is not an object.'); + return null; + } + return JSON.stringify(obj) === '{}'; +}; + +MODx.util.JsonTools = { + mapReplacer: (key, value) => { + if (value instanceof Map) { + return { + dataType: 'Map', + value: [...value] + }; + } + return value; + }, + mapReviver: (key, value) => { + if (typeof value === 'object' && value !== null) { + if (value.dataType === 'Map') { + return new Map(value.value); + } + } + return value; + } +}; + /** * Utility methods for tree objects */ diff --git a/manager/assets/modext/widgets/core/modx.grid.js b/manager/assets/modext/widgets/core/modx.grid.js index c25fce154e7..3df889b7ed3 100644 --- a/manager/assets/modext/widgets/core/modx.grid.js +++ b/manager/assets/modext/widgets/core/modx.grid.js @@ -269,6 +269,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ * @param {Object} response - The processor save response object. See modConnectorResponse::outputContent (PHP) */ ,onAfterAutoSave: function(response) { + console.log('onAfterAutoSave running...'); if (!response.success && response.message === '') { var msg = ''; if (response.data.length) { @@ -359,6 +360,7 @@ Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{ } ,removeActiveRow: function(r) { + console.log('removeActiveRow...'); if (this.fireEvent('afterRemoveRow',r)) { var rx = this.getSelectionModel().getSelected(); this.getStore().remove(rx); diff --git a/manager/assets/modext/widgets/core/modx.panel.js b/manager/assets/modext/widgets/core/modx.panel.js index 288549249a7..23b39f45dd6 100644 --- a/manager/assets/modext/widgets/core/modx.panel.js +++ b/manager/assets/modext/widgets/core/modx.panel.js @@ -87,53 +87,69 @@ Ext.extend(MODx.FormPanel,Ext.FormPanel,{ ,errorHandlingTabs: [] ,errorHandlingIgnoreTabs: [] - ,submit: function(o) { - var fm = this.getForm(); - if (fm.isValid() || o.bypassValidCheck) { - o = o || {}; - o.headers = { - 'Powered-By': 'MODx' - ,'modAuth': MODx.siteId + ,submit: function(options = {}) { + const form = this.getForm(); + if (form.isValid() || options.bypassValidCheck) { + const + exitDelay = 150, + status = new MODx.window.SaveProgress({ exitDelay }) + ; + status.init(); + options.headers = { + 'Powered-By': 'MODx', + modAuth: MODx.siteId }; - if (this.fireEvent('beforeSubmit',{ - form: fm - ,options: o - ,config: this.config + if (this.fireEvent('beforeSubmit', { + form: form, + options: options, + config: this.config })) { - fm.submit({ - waitMsg: this.config.saveMsg || _('saving') - ,scope: this - ,headers: o.headers - ,clientValidation: (o.bypassValidCheck ? false : true) - ,failure: function(f,a) { - if (this.fireEvent('failure',{ - form: f - ,result: a.result - ,options: o - ,config: this.config - })) { - MODx.form.Handler.errorExt(a.result,f); - } - } - ,success: function(f,a) { + form.submit({ + scope: this, + headers: options.headers, + clientValidation: !options.bypassValidCheck, + failure: function(f, a) { + /* + Need to allow time for the status window to finish + closing, otherwise it becomes unreachable when the + error message alert is shown (and even after it is dismissed) + */ + setTimeout(() => { + if (this.fireEvent('failure', { + form: f, + result: a.result, + options: options, + config: this.config + })) { + status.exit('failure'); + setTimeout(() => { + MODx.form.Handler.errorExt(a.result, f); + }, exitDelay); + } + }, exitDelay); + }, + success: function(f, a) { if (this.config.success) { - Ext.callback(this.config.success,this.config.scope || this,[f,a]); + Ext.callback(this.config.success, this.config.scope || this, [f, a]); } - this.fireEvent('success',{ - form: f - ,result: a.result - ,options: o - ,config: this.config + this.fireEvent('success', { + form: f, + result: a.result, + options: options, + config: this.config }); + status.exit(); this.clearDirty(); - this.fireEvent('setup',this.config); + this.fireEvent('setup', this.config); - //get our Active input value and keep focus - var lastActiveEle = Ext.state.Manager.get('curFocus'); - if (lastActiveEle && lastActiveEle != '') { + // get our Active input value and keep focus + const lastActiveEle = Ext.state.Manager.get('curFocus'); + if (lastActiveEle && lastActiveEle !== '') { Ext.state.Manager.clear('curFocus'); - var initFocus = document.getElementById(lastActiveEle); - if(initFocus) initFocus.focus(); + const initFocus = document.getElementById(lastActiveEle); + if (initFocus) { + initFocus.focus(); + } } } }); @@ -477,6 +493,14 @@ Ext.extend(MODx.FormPanel,Ext.FormPanel,{ } } + /** + * Moved this functionality to utilities.js. Passing through for BC, but + * deprecate usage here and will remove in future release. + */ + ,insertTagCopyUtility: function(cmp, elType) { + MODx.util.insertTagCopyUtility(cmp, elType); + } + /** * @property {Function} onChangeStaticSource - Updates the static file field based * on the chosen source. diff --git a/manager/assets/modext/widgets/core/modx.window.js b/manager/assets/modext/widgets/core/modx.window.js index c4e751289d3..8f7294d5caa 100644 --- a/manager/assets/modext/widgets/core/modx.window.js +++ b/manager/assets/modext/widgets/core/modx.window.js @@ -2,102 +2,116 @@ // these also apply for Windows that do not extend MODx.Window (like console for ex.) // we use CSS3 box-shadows in 2014, removes clutter from the DOM Ext.Window.prototype.floating = { shadow: false }; -/* override default Ext.Window component methods */ + Ext.override(Ext.Window, { - // prevents ugly slow js animations when opening a window - // we cannot do the CSS3 animations stuff in these overrides, as not all windows are animated! - // so they just prevent the normal JS animation to take effect animShow: function() { this.afterShow(); - - // some windows (like migx) don't seem to call onShow - // so we have to do a check here after onShow should have finished - var win = this; // we need a reference to this for setTimeout - // wait for onShow to finish and check if the window is already visible then, if not, try to do that - setTimeout(function() { - if (!win.el.hasClass('anim-ready')) { - win.el.addClass('anim-ready'); - setTimeout(function() { - if (win.mask !== undefined) { - // respect that the mask is not always the same object - if (win.mask instanceof Ext.Element) { - win.mask.addClass('fade-in'); - } else { - win.mask.el.addClass('fade-in'); - } + }, + animHide: function() { + this.afterHide(); + }, + render: function(...args) { + // ...args contains only one arg at index 0 - a ref to the body el + // console.log('Ext.Window (base class) render... is modal?', this.modal); + this.on({ + beforeshow: function() { + const + window = this, + windowType = this.getWindowType(window), + maskObject = windowType === 'modal' ? this.mask : MODx.mask, + showMask = !MODx.maskConfig.getMaskAttribute(windowType, 'disabled') + ; + // console.log(`SHOW : ${showMask}`); + if (showMask && windowType === 'modal') { + this.mask.addClass('modal'); + maskObject.dom.style.backgroundColor = MODx.maskConfig.getMaskAttribute(windowType, 'color'); + } + this.el.addClass('anim-ready'); + setTimeout(() => { + if (showMask) { + this.toggleMask(windowType, maskObject); } - win.el.addClass('zoom-in'); + window.el.addClass('zoom-in'); }, 250); - } - }, 300); - } - ,animHide: function() { - this.afterHide(); - - } - ,onShow: function() { - // skip MODx.msg windows, the animations do not work with them as they are always the same element! - if (!this.el.hasClass('x-window-dlg')) { - // first set the class that scales the window down a bit - // this has to be done after the full window is positioned correctly by extjs - this.addClass('anim-ready'); - // let the scale transformation to 0.7 finish before animating in - var win = this; // we need a reference to this for setTimeout - setTimeout(function() { - if (win.mask !== undefined) { - // respect that the mask is not always the same object - if (win.mask instanceof Ext.Element) { - win.mask.addClass('fade-in'); - } else { - win.mask.el.addClass('fade-in'); + }, + beforehide: function() { + if (this.el.hasClass('zoom-in')) { + const + window = this, + windowType = this.getWindowType(window), + maskObject = windowType === 'modal' ? this.mask : MODx.mask, + hideMask = window.id !== 'modx-window-configure-mask' + && (windowType === 'modal' + || (windowType === 'pseudomodal' + && MODx.openPseudoModals.length <= 1) + ) + ; + // console.log(`beforehide :: Hiding a ${windowType} window...\nArgs:`, arguments, '\nOpen modals:', MODx.openPseudoModals); + + this.el.removeClass('zoom-in'); + this.el.addClass('zoom-out'); + if (hideMask) { + this.toggleMask(windowType, maskObject, 'hide'); } + this.hidden = true; + setTimeout(() => { + if (!this.isDestroyed) { + window.el.removeClass('zoom-out'); + window.el.removeClass('anim-ready'); + window.el.hide(); + window.afterHide(); + if (hideMask) { + Ext.getBody().removeClass('x-body-masked'); + } + } + }, 250); } - win.el.addClass('zoom-in'); - }, 250); - } else { - // we need to handle MODx.msg windows (Ext.Msg singletons, e.g. always the same element, no multiple instances) differently - this.mask.addClass('fade-in'); - this.el.applyStyles({'opacity': 1}); - } - } - ,onHide: function() { - // for some unknown (to me) reason, onHide() get's called when a window is initialized, e.g. before onShow() - // so we need to prevent the following routine be applied prematurely - if (this.el.hasClass('zoom-in')) { - this.el.removeClass('zoom-in'); - if (this.mask !== undefined) { - // respect that the mask is not always the same object - if (this.mask instanceof Ext.Element) { - this.mask.removeClass('fade-in'); - } else { - this.mask.el.removeClass('fade-in'); - } + return false; } - this.addClass('zoom-out'); - // let the CSS animation finish before hiding the window - var win = this; // we need a reference to this for setTimeout - setTimeout(function() { - // we have an unsolved problem with windows that are destroyed on hide - // the zoom-out animation cannot be applied for such windows, as they - // get destroyed too early, if someone knows a solution, please tell =) - if (!win.isDestroyed) { - win.el.hide(); - // and remove the CSS3 animation classes - win.el.removeClass('zoom-out'); - win.el.removeClass('anim-ready'); - } - }, 250); - } else if (this.el.hasClass('x-window-dlg')) { - // we need to handle MODx.msg windows (Ext.Msg singletons, e.g. always the same element, no multiple instances) differently - this.el.applyStyles({'opacity': 0}); - - if (this.mask !== undefined) { - // respect that the mask is not always the same object - if (this.mask instanceof Ext.Element) { - this.mask.removeClass('fade-in'); - } else { - this.mask.el.removeClass('fade-in'); - } + }); + Ext.Window.superclass.render.call(this, ...args); + }, + + /** + * + * @param {*} window + * @returns + */ + getWindowType: function(window) { + return window.el.hasClass('x-window-dlg') || window.modal === true ? 'modal' : 'pseudomodal'; + }, + + /** + * Controls the visibility of the masking element by applying or removing a specified css selector + * @param {String} windowType Type of window being worked with (i.e., 'modal' or 'pseudomodal') + * @param {Ext.Element|Object} maskElement + * @param {String} action The toggle state being applied: 'show' or 'hide' + */ + toggleMask: function(windowType, maskElement, action = 'show') { + if (maskElement === undefined) { + return; + } + const + targetElement = maskElement instanceof Ext.Element ? maskElement : maskElement?.el + ; + if (targetElement) { + if (action === 'hide') { + // console.log('1 :: toggle targetEl:', targetElement); + targetElement.dom.style.removeProperty('opacity'); + setTimeout(() => { + // console.log('2 :: toggle targetEl:', targetElement); + /* + Sometimes an empty Ext.Element (with only an id) will be present + by the time this runs, so ensure we only try to hide Elements that + can be hidden to avoid errors + */ + if (Object.hasOwn(targetElement, 'dom')) { + targetElement.hide(); + } + }, 1000); + } else if (this.id !== 'modx-window-status-modal') { + // console.log('Showing status win mask'); + targetElement.dom.style.opacity = MODx.maskConfig.getMaskAttribute(windowType, 'opacity'); } } } @@ -112,139 +126,343 @@ Ext.override(Ext.Window, { * @param {Object} config An object of options. * @xtype modx-window */ -MODx.Window = function(config) { - config = config || {}; +MODx.Window = function(config = {}) { this.isSmallScreen = Ext.getBody().getViewSize().height <= 768; /* Update boolean modxFbarHas[___]SaveSwitch properties for later use */ - if (config.hasOwnProperty('modxFbarSaveSwitches') && config.modxFbarSaveSwitches.length > 0) { + if (Object.hasOwn(config, 'modxFbarSaveSwitches') && config.modxFbarSaveSwitches.length > 0) { config.modxFbarSaveSwitches.forEach(saveSwitch => { saveSwitch = saveSwitch[0].toUpperCase() + saveSwitch.slice(1); const configKey = `modxFbarHas${saveSwitch}Switch`; config[configKey] = true; }); - } /* Setup the standard system footer bar if fbar and buttons properties are empty. Note that buttons overrides fbar and can be used to specify a customized set of window buttons. */ - if (!config.hasOwnProperty('fbar') && (!config.hasOwnProperty('buttons') || config.buttons.length == 0)) { + if (!Object.hasOwn(config, 'fbar') && (!Object.hasOwn(config, 'buttons') || config.buttons.length === 0)) { const footerBar = this.getWindowFbar(config); if (footerBar) { config.buttonAlign = 'left'; config.fbar = footerBar; } } - Ext.applyIf(config,{ - modal: false - - ,modxFbarHasClearCacheSwitch: false - ,modxFbarHasDuplicateValuesSwitch: false - ,modxFbarHasRedirectSwitch: false - - ,modxFbarButtons: config.modxFbarButtons || 'c-s' - ,modxFbarSaveSwitches: [] - ,modxPseudoModal: false - - ,layout: 'auto' - ,closeAction: 'hide' - ,shadow: true - ,resizable: true - ,collapsible: true - ,maximizable: true - ,autoHeight: false - ,autoScroll: true - ,allowDrop: true - ,width: 400 - ,constrain: true - ,constrainHeader: true - ,cls: 'modx-window' + Ext.applyIf(config, { + modal: false, + + modxFbarHasClearCacheSwitch: false, + modxFbarHasDuplicateValuesSwitch: false, + modxFbarHasRedirectSwitch: false, + + modxFbarButtons: config.modxFbarButtons || 'c-s', + modxFbarSaveSwitches: [], /* - ,buttons: [{ - text: config.cancelBtnText || _('cancel') - ,scope: this - ,handler: function() { config.closeAction !== 'close' ? this.hide() : this.close(); } - },{ - text: config.saveBtnText || _('save') - ,cls: 'primary-button' - ,scope: this - ,handler: this.submit - }] + Windows are pseudomodal by default unless: + 1] a config value is passed + 2] the window's modal property is set to true */ - ,record: {} - ,keys: [{ - key: Ext.EventObject.ENTER - ,fn: function(keyCode, event) { - var elem = event.getTarget(); - var component = Ext.getCmp(elem.id); + modxPseudoModal: Ext.isBoolean(config.modxPseudoModal) ? config.modxPseudoModal : !config.modal, + + layout: 'auto', + closeAction: 'hide', + shadow: true, + resizable: true, + collapsible: true, + maximizable: true, + autoHeight: false, + autoScroll: true, + allowDrop: true, + width: 400, + constrain: true, + constrainHeader: true, + cls: 'modx-window', + record: {}, + keys: [ + { + key: Ext.EventObject.ENTER, + fn: function(keyCode, event) { + const + elem = event.getTarget(), + component = Ext.getCmp(elem.id) + ; if (component instanceof Ext.form.TextArea) { - return component.append("\n"); - } else { - this.submit(); + return component.append('\n'); } - } - ,scope: this - }] - ,tools: [{ + this.submit(); + }, + scope: this + }, { + key: Ext.EventObject.RIGHT, + alt: true, + handler: function(keyCode, event) { + console.log('Alt right'); + if (MODx.openPseudoModals.length > 1) { + console.log('Key shortcut :: focus next window...'); + } + }, + scope: this + }, { + key: Ext.EventObject.LEFT, + alt: true, + handler: function(keyCode, event) { + console.log('Alt left'); + if (MODx.openPseudoModals.length > 1) { + console.log('Key shortcut :: focus prev window...'); + } + }, + scope: this + } + ], + tools: [{ id: 'gear', - title: 'Window Settings', - // href: '#' - menu: { - xtype: 'menu', - anchor: true, - items: [ - { - xtype: 'menucheckitem', - text: 'Remove Masks', - checked: true - // bind: '{indented}' - }, { - text: 'Disabled Item', - disabled: true, - separator: true + title: _('mask_toolbar_tool_title'), + qtip: _('mask_toolbar_tool_qtip'), + handler: function(evt, toolEl, panel, toolConfig) { + const targetWindowType = panel.getWindowType(panel); + let + configWindow = Ext.getCmp('modx-window-configure-mask'), + mask = document.querySelector(`.ext-el-mask.${targetWindowType}`) + ; + const + isDisabled = MODx.maskConfig.getMaskAttribute(targetWindowType, 'disabled'), + maskStyles = mask && !isDisabled ? window.getComputedStyle(mask) : null, + opacity = maskStyles + ? maskStyles.opacity + : MODx.maskConfig.getMaskAttribute(targetWindowType, 'opacity'), + bgColor = maskStyles + ? MODx.util.Color.rgbToHex(maskStyles.backgroundColor) + : MODx.maskConfig.getMaskAttribute(targetWindowType, 'color'), + onColorInput = e => { + mask.style.backgroundColor = e.target.value; + }, + onOpacityInput = e => { + mask.style.opacity = e.target.value / 100; + }, + setFieldsDisabled = (fieldMap, disabled = true, selectAll = false) => { + const filterList = [ + 'modx-mask-settings--opacity', + 'modx-mask-settings--color' + ]; + Object.keys(fieldMap).forEach(fieldKey => { + if (selectAll === true || filterList.includes(fieldKey)) { + fieldMap[fieldKey].setDisabled(disabled); + } + }); + }, + /** + * Controlled destruction of window needed to allow animation to work properly + */ + dismiss = windowCmp => { + if (windowCmp instanceof MODx.Window) { + const + colorInput = document.getElementById('modx-mask-color'), + opacityInput = document.getElementById('modx-mask-opacity') + ; + colorInput.removeEventListener('input', onColorInput); + opacityInput.removeEventListener('input', onOpacityInput); + windowCmp.hide(); + setTimeout(() => windowCmp.destroy(), 250); + } + } + ; + if (!configWindow) { + configWindow = new MODx.Window({ + title: _('mask_config_window_title'), + width: panel.width - 100, + id: 'modx-window-configure-mask', + cls: 'modx-window configure', + modxPseudoModal: false, + autoHeight: true, + fields: [ + { + xtype: 'checkbox', + itemId: 'modx-mask-settings--disabled', + boxLabel: _('mask_config_field_disabled'), + description: MODx.expandHelp ? '' : _('mask_config_field_disabled_desc'), + checked: isDisabled, + listeners: { + check: function(cmp, checked) { + const + form = cmp.ownerCt.getForm(), + fields = form.items.map + ; + if (checked) { + mask.style.opacity = 0; + setFieldsDisabled(fields); + } else { + if (!mask) { + const maskCmp = MODx.maskConfig.createMask(panel, targetWindowType, 'configure'); + mask = maskCmp.dom; + maskCmp.show(); + } + mask.style.opacity = opacity; + setFieldsDisabled(fields, false, true); + } + } + } + }, + { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('mask_config_field_disabled_desc'), + cls: 'desc-under toggle-slider-above' + }, + { + xtype: 'textfield', + itemId: 'modx-mask-settings--opacity', + id: 'modx-mask-opacity', + inputType: 'range', + fieldLabel: _('mask_config_field_opacity'), + min: 5, + max: 95, + step: 5, + disabled: isDisabled, + value: opacity <= 1 ? opacity * 100 : opacity + }, + { + xtype: 'textfield', + itemId: 'modx-mask-settings--color', + id: 'modx-mask-color', + inputType: 'color', + fieldLabel: _('mask_config_field_color'), + enableKeyEvents: true, + disabled: isDisabled, + value: bgColor + }, + { + xtype: 'checkbox', + itemId: 'modx-mask-settings--set-user', + boxLabel: 'Update User Settings', + description: MODx.expandHelp ? '' : _('mask_config_field_update_user_desc'), + disabled: isDisabled + }, + { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('mask_config_field_update_user_desc'), + cls: 'desc-under toggle-slider-above' + }, + { + xtype: 'checkbox', + itemId: 'modx-mask-settings--set-global', + boxLabel: _('mask_config_field_update_global'), + description: MODx.expandHelp ? '' : _('mask_config_field_update_global_desc'), + disabled: isDisabled + }, + { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('mask_config_field_update_global_desc'), + cls: 'desc-under toggle-slider-above' + } + ], + buttons: [ + { + text: _('cancel'), + handler: function(btn, e) { + mask.style.backgroundColor = MODx.maskConfig.getMaskAttribute(targetWindowType, 'color'); + mask.style.opacity = isDisabled ? 0 : MODx.maskConfig.getMaskAttribute(targetWindowType, 'opacity'); + dismiss(configWindow); + } + }, + { + text: _('save'), + cls: 'primary-button', + handler: function(btn, e) { + const + form = configWindow.fp.getForm(), + fields = form.items, + values = { + disabled: Boolean(fields.map['modx-mask-settings--disabled'].getValue()), + color: MODx.util.Color.rgbToHex(fields.map['modx-mask-settings--color'].getValue()), + opacity: fields.map['modx-mask-settings--opacity'].getValue() / 100 + }, + saveToGlobalSettings = Boolean(fields.map['modx-mask-settings--set-global'].getValue()), + saveToUserSettings = Boolean(fields.map['modx-mask-settings--set-user'].getValue()) + ; + if (!saveToGlobalSettings && !saveToUserSettings) { + /* + - Show confirm window stating changes only last for session, + with a 'Do not show this warning again' checkbox (persisted + in a localStorage item). + - Will need to check two condiditions (depends on if user is sudo + or primary (id = 1) user, where both save switches will be available) + */ + console.log('Let’s show a dialog confirming changes will be lost at end of session...'); + } + MODx.maskConfig.updateSessionConfig(targetWindowType, values); + if (saveToGlobalSettings || saveToUserSettings) { + let settingsTarget; + if (saveToGlobalSettings && saveToUserSettings) { + settingsTarget = 'both'; + } else { + settingsTarget = saveToGlobalSettings ? 'global' : 'user'; + } + MODx.maskConfig.updateSystemSettings(targetWindowType, settingsTarget, values, MODx.config.user); + } + dismiss(configWindow); + } + } + ], + tools: [{ + id: 'close', + handler: function() { + dismiss(configWindow); + } + }], + listeners: { + afterrender: function(cmp) { + const { tools } = cmp; + if (tools) { + Object.keys(tools).forEach(tool => { + if (tool !== 'close') { + tools[tool].hide(); + } + }); + } + } + }, + onEsc: function() { + dismiss(configWindow); + } + }); + configWindow.show(evt.target); + // console.log('config win close action: ', configWindow.closeAction); } - ] + configWindow.toFront(); + /* + Show live adjustments to mask settings + + Note: Setting up listeners here and not on the opacity and color Ext components + above because we need to listen for the 'input' event (which is not defined in Ext 3.4) + for range and color types. While we could extend the textfield (or its base) component + to define/add that listener for global use, electing to keep it simple here. + */ + const + colorInput = document.getElementById('modx-mask-color'), + opacityInput = document.getElementById('modx-mask-opacity') + ; + colorInput.addEventListener('input', onColorInput); + opacityInput.addEventListener('input', onOpacityInput); } }] }); - MODx.Window.superclass.constructor.call(this,config); + MODx.Window.superclass.constructor.call(this, config); this.options = config; this.config = config; this.addEvents({ - success: true - ,failure: true - ,beforeSubmit: true - ,updateWindow: false + success: true, + failure: true, + beforeSubmit: true }); this._loadForm(); this.on({ render: function() { - console.log('window render, this:', this); - if (MODx.config.enable_overlays) { - if (this.modxPseudoModal) { - if (MODx.openPseudoModals.length === 0) { - MODx.mask = this.container.createChild({cls:'ext-el-mask clickthrough'}, this.el.dom); - MODx.mask.setStyle('backgroundColor', overlayCssColorNonblocking); - // console.log('render, dynamic mask color: ', overlayCssColorNonblocking); - MODx.mask.hide(); - MODx.mask.resizeMask = function() { - // console.log('window resized!'); - MODx.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true)); - }; - // console.log('custom mask el: ', MODx.mask); - window.addEventListener('resize', MODx.mask.resizeMask); - } - MODx.openPseudoModals.push({ - modalId: this.itemId - }); - // console.log('open modxPseudoModals: ',MODx.openPseudoModals); - } - if (this.modal) { - console.log('rendering real modal...'); - } + if (this.modxPseudoModal && !MODx.maskConfig.getMaskAttribute('pseudomodal', 'disabled')) { + MODx.maskConfig.createMask(this, 'pseudomodal', 'render', false); } }, afterrender: function() { @@ -253,22 +471,15 @@ MODx.Window = function(config) { this.resizeWindow(); }, beforeShow: function() { - if (this.modxPseudoModal && !MODx.mask.isVisible()) { + if (this.modxPseudoModal && !MODx.util.isEmptyObject(MODx.mask) && !MODx.mask?.isVisible()) { Ext.getBody().addClass('x-body-masked'); - MODx.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true)); MODx.mask.show(); } }, show: function() { - // console.log('showing a modxPseudoModal...'); - // console.log(`modxPseudoModal opacity: ${overlayOpacityNonblocking}`); - if (this.modxPseudoModal && MODx.mask.isVisible()) { - setTimeout(function() { - MODx.mask.setStyle('opacity', overlayOpacityNonblocking); - // MODx.mask.addClass('fade-in'); - }, 250); + if (this.modxPseudoModal) { + this.registerPseudomodal(this); } - // console.log('show, mask color: ', MODx.mask.getColor('backgroundColor')); if (this.config.blankValues) { this.fp.getForm().reset(); } @@ -278,35 +489,55 @@ MODx.Window = function(config) { this.syncSize(); this.focusFirstField(); }, - beforehide: function() { - if (this.modxPseudoModal && MODx.mask && MODx.openPseudoModals.length === 1) { - MODx.mask.removeClass('fade-in'); - } - }, hide: function() { if (this.modxPseudoModal) { - if (MODx.openPseudoModals.length > 1) { - MODx.openPseudoModals.forEach((modxPseudoModal, i) => { - if (modxPseudoModal.modalId == this.itemId) { - MODx.openPseudoModals.splice(i, 1); + this.unregisterPseudomodal(this.getWindowIdentifier()); + } + /* + Re-focus one of the open windows, if any, so the esc key + can be used to close each successive open window + + TODO: Track all non-dialog modals in obj that will replace + MODx.openPseudoModals; it should take the shape of - + ### + MODx.openModals = { + pseudo: [ + { + windowId: stringid, + window: windowObj + }, + ... + ], + // Note: There can only be one standard modal open at a time + // A single configuration and/or dialog modal may coexist on top of either the standard or pseudo + standard: [ + { + windowId: stringid, + window: windowObj } - }); - } else { - MODx.openPseudoModals = []; - MODx.mask.hide(); - MODx.mask.remove(); - Ext.getBody().removeClass('x-body-masked'); - window.removeEventListener('resize', MODx.mask.resizeMask); + ] } - // console.log('hide, openPseudoModals: ', MODx.openPseudoModals); + ### + */ + if (MODx.openPseudoModals.length > 0) { + console.log('Bringing first pseudomodal to front...', MODx.openPseudoModals); + MODx.openPseudoModals[0].window.toFront(); + } + }, + destroy: function() { + if (this.modxPseudoModal) { + this.unregisterPseudomodal(this.getWindowIdentifier()); } } }); Ext.EventManager.onWindowResize(this.resizeWindow, this); }; -Ext.extend(MODx.Window,Ext.Window,{ +Ext.extend(MODx.Window, Ext.Window, { _loadForm: function() { - if (this.checkIfLoaded(this.config.record || null)) { return false; } + if (this.checkIfLoaded(this.config.record || null)) { + console.log('Form already loaded'); + return false; + } var r = this.config.record; /* set values here, since setValue after render seems to be broken */ @@ -329,58 +560,28 @@ Ext.extend(MODx.Window,Ext.Window,{ insert a hidden field in the form to to be able to relay its value to the processor */ - if (this.config.hasOwnProperty('modxFbarSaveSwitches') && this.config.modxFbarSaveSwitches.length > 0) { + if (Object.hasOwn(this.config, 'modxFbarSaveSwitches') && this.config.modxFbarSaveSwitches.length > 0) { + // console.log('We have footer bar switches to build!'); this.config.modxFbarSaveSwitches.forEach(saveSwitch => { + let defaultValue = 1; + // console.log('saveSwitch: ', saveSwitch); switch (saveSwitch) { case 'redirect': - defaultValue = this.config.redirect ; + defaultValue = this.config.redirect === false ? 0 : 1; break; case 'duplicateValues': defaultValue = 0; break; - default: - defaultValue = 1; - + // no default } this.setFbarSwitchHiddenField(saveSwitch, defaultValue); }); - - } - console.log('final fields: ', this.config.fields); - /* - if (this.modxFbarHasClearCacheSwitch) { - // console.log('adding hidden cache switch...'); - const switchId = `${this.id}-clearcache`, - switchCmp = Ext.getCmp(switchId) - ; - if (switchCmp) { - this.config.fields.push({ - xtype: 'hidden' - ,name: 'clearCache' - ,id: `${switchId}-hidden` - ,value: 1 - }); - } - } - if (this.modxFbarHasRedirectSwitch) { - // console.log('adding hidden redirect switch..., default val: ',this.config.redirect); - const switchId = `${this.id}-redirect`, - switchCmp = Ext.getCmp(switchId) - ; - if (switchCmp) { - this.config.fields.push({ - xtype: 'hidden' - ,name: 'redirect' - ,id: `${switchId}-hidden` - ,value: this.config.redirect ? 1 : 0 - }); - } } - */ + // console.log('final fields: ', this.config.fields); this.fp = this.createForm({ - url: this.config.url - ,baseParams: this.config.baseParams || { action: this.config.action || '' } - ,items: this.config.fields || [] + url: this.config.url, + baseParams: this.config.baseParams || { action: this.config.action || '' }, + items: this.config.fields || [] }); var w = this; this.fp.getForm().items.each(function(f) { @@ -389,93 +590,118 @@ Ext.extend(MODx.Window,Ext.Window,{ }); }); this.renderForm(); - } + }, - ,focusFirstField: function() { + focusFirstField: function() { if (this.fp && this.fp.getForm() && this.fp.getForm().items.getCount() > 0) { var fld = this.findFirstTextField(); - if (fld) { fld.focus(false,200); } + if (fld) { fld.focus(false, 200); } } - } + }, - ,findFirstTextField: function(i) { + findFirstTextField: function(i) { i = i || 0; var fld = this.fp.getForm().items.itemAt(i); - if (!fld) return false; + if (!fld) { return false; } if (fld.isXType('combo') || fld.isXType('checkbox') || fld.isXType('radio') || fld.isXType('displayfield') || fld.isXType('statictextfield') || fld.isXType('hidden')) { i = i+1; fld = this.findFirstTextField(i); } return fld; - } + }, - ,submit: function(close) { - close = close === false ? false : true; - var f = this.fp.getForm(); - if (f.isValid() && this.fireEvent('beforeSubmit',f.getValues())) { - // console.log('window form submit, this:', this); - // console.log('window form submit, form:', f); - console.log('window form submit, form vals:', f.getValues()); - // return false; + submit: function(closeOnSuccess) { + const + close = closeOnSuccess !== false, + f = this.fp.getForm() + ; + if (f.isValid() && this.fireEvent('beforeSubmit', f.getValues())) { + const + exitDelay = 150, + status = new MODx.window.SaveProgress({ exitDelay }) + ; + status.init(); f.submit({ - waitMsg: this.config.waitMsg || _('saving') - ,submitEmptyText: this.config.submitEmptyText !== false - ,scope: this - ,failure: function(frm,a) { - if (this.fireEvent('failure',{f:frm,a:a})) { - MODx.form.Handler.errorExt(a.result,frm); - } - this.doLayout(); - } - ,success: function(frm,a) { + submitEmptyText: this.config.submitEmptyText !== false, + scope: this, + failure: function(frm, a) { + /* + Need to allow time for the status window to finish + closing, otherwise it becomes unreachable when the + error message alert is shown (and even after it is dismissed) + */ + setTimeout(() => { + if (this.fireEvent('failure', { + f: frm, + a: a + })) { + status.exit('failure'); + setTimeout(() => { + MODx.form.Handler.errorExt(a.result, frm); + }, exitDelay); + } + this.doLayout(); + }, exitDelay); + }, + success: function(frm, a) { if (this.config.success) { - Ext.callback(this.config.success,this.config.scope || this,[frm,a]); + Ext.callback(this.config.success, this.config.scope || this, [frm, a]); } - this.fireEvent('success',{f:frm,a:a}); - if (close) { this.config.closeAction !== 'close' ? this.hide() : this.close(); } + this.fireEvent('success', { + f: frm, + a: a + }); + if (close) { + if (this.config.closeAction !== 'close') { + this.hide(); + } else { + this.close(); + } + } + status.exit(); this.doLayout(); } }); } - } - - ,createForm: function(config) { - Ext.applyIf(this.config,{ - formFrame: true - ,border: false - ,bodyBorder: false - ,autoHeight: true + }, + + createForm: function(config) { + Ext.applyIf(this.config, { + formFrame: true, + border: false, + bodyBorder: false, + autoHeight: true }); config = config || {}; - Ext.applyIf(config,{ - labelAlign: this.config.labelAlign || 'top' - ,labelWidth: this.config.labelWidth || 100 - ,labelSeparator: this.config.labelSeparator || '' - ,frame: this.config.formFrame - ,border: this.config.border - ,bodyBorder: this.config.bodyBorder - ,autoHeight: this.config.autoHeight - ,anchor: '100% 100%' - ,errorReader: MODx.util.JSONReader - ,defaults: this.config.formDefaults || { - msgTarget: this.config.msgTarget || 'under' - ,anchor: '100%' - } - ,url: this.config.url - ,baseParams: this.config.baseParams || {} - ,fileUpload: this.config.fileUpload || false + Ext.applyIf(config, { + labelAlign: this.config.labelAlign || 'top', + labelWidth: this.config.labelWidth || 100, + labelSeparator: this.config.labelSeparator || '', + frame: this.config.formFrame, + border: this.config.border, + bodyBorder: this.config.bodyBorder, + autoHeight: this.config.autoHeight, + anchor: '100% 100%', + errorReader: MODx.util.JSONReader, + defaults: this.config.formDefaults || { + msgTarget: this.config.msgTarget || 'under', + anchor: '100%' + }, + url: this.config.url, + baseParams: this.config.baseParams || {}, + fileUpload: this.config.fileUpload || false }); return new Ext.FormPanel(config); - } + }, - ,renderForm: function() { + renderForm: function() { this.fp.on('destroy', function() { Ext.EventManager.removeResizeListener(this.resizeWindow, this); }, this); this.add(this.fp); - } + }, - ,checkIfLoaded: function(r) { + checkIfLoaded: function(r) { r = r || {}; if (this.fp && this.fp.getForm()) { /* so as not to duplicate form */ this.fp.getForm().reset(); @@ -483,7 +709,7 @@ Ext.extend(MODx.Window,Ext.Window,{ return true; } return false; - } + }, /* @smg6511: Suggest moving away from using this bulk setValues method and @@ -493,47 +719,47 @@ Ext.extend(MODx.Window,Ext.Window,{ procedure in the _loadForm method could be dropped too. All windows in windows.js would need to be updated before dropping. */ - ,setValues: function(r) { + setValues: function(r) { if (r === null) { return false; } this.fp.getForm().setValues(r); - } + }, - ,reset: function() { + reset: function() { this.fp.getForm().reset(); - } + }, - ,hideField: function(f) { + hideField: function(f) { f.disable(); f.hide(); var d = f.getEl().up('.x-form-item'); if (d) { d.setDisplayed(false); } - } + }, - ,showField: function(f) { + showField: function(f) { f.enable(); f.show(); var d = f.getEl().up('.x-form-item'); if (d) { d.setDisplayed(true); } - } + }, - ,loadDropZones: function() { - if (this._dzLoaded) return false; + loadDropZones: function() { + if (this._dzLoaded) { return false; } var flds = this.fp.getForm().items; flds.each(function(fld) { if (fld.isFormField && ( fld.isXType('textfield') || fld.isXType('textarea') ) && !fld.isXType('combo')) { new MODx.load({ - xtype: 'modx-treedrop' - ,target: fld - ,targetEl: fld.getEl().dom + xtype: 'modx-treedrop', + target: fld, + targetEl: fld.getEl().dom }); } }); this._dzLoaded = true; - } + }, - ,resizeWindow: function(){ + resizeWindow: function() { var viewHeight = Ext.getBody().getViewSize().height; var el = this.fp.getForm().el; if(viewHeight < this.originalHeight){ @@ -543,31 +769,81 @@ Ext.extend(MODx.Window,Ext.Window,{ el.setStyle('overflow-y', 'auto'); el.setHeight('auto'); } - } + }, + + getWindowIdentifier: function() { + return this.itemId || this.ident || this.id || Ext.id(); + }, + + registerPseudomodal: function(window) { + const windowId = this.getWindowIdentifier(); + MODx.openPseudoModals.push({ + windowId, + window + }); + // console.log('registerPseudomodal :: open modals', MODx.openPseudoModals); + }, /** - * + * Removes a pseudomodal window reference from the registry + * @param {String} windowId The window's unique identifier */ - ,setFbarSwitchHiddenField: function(fbarSwitchFieldName, defaultValue = 1) { - - const switchId = `${this.id}-${fbarSwitchFieldName}`, - switchCmp = Ext.getCmp(switchId) - ; - if (switchCmp) { - this.config.fields.push({ - xtype: 'hidden', - name: fbarSwitchFieldName, - id: `${switchId}-hidden`, - value: defaultValue + unregisterPseudomodal: function(windowId) { + // console.log(`Unegistering pseudomodal with id ${windowId}`); + if (!typeof windowId === 'string') { + console.error('Aborted unregistering a modal due to an invalid window id being passed.'); + return; + } + if (MODx.openPseudoModals.length > 1) { + MODx.openPseudoModals.forEach((modxPseudoModal, i) => { + if (modxPseudoModal.windowId === windowId) { + MODx.openPseudoModals.splice(i, 1); + } }); + // console.log(`Unregistered window (id: ${windowId})\nRemaining modals:`, MODx.openPseudoModals); + } else { + MODx.openPseudoModals = []; + // console.log(`Unregistered only window present (id: ${windowId})`, MODx.openPseudoModals); } - } + }, + + // getPseudomodalCount: function() { + + // }, /** - * + * + * @param {*} fbarSwitchFieldName + * @param {*} defaultValue */ - ,getFbarSwitch: function(windowId, fbarSwitchFieldName, switchLabel, switchIsChecked = true) { + setFbarSwitchHiddenField: function(fbarSwitchFieldName, defaultValue = 1) { + // const + // windowId = this.getWindowIdentifier(), + // switchId = `${windowId}-${fbarSwitchFieldName}`, + // switchCmp = Ext.getCmp(switchId) + // ; + const switchId = `${this.getWindowIdentifier()}-${fbarSwitchFieldName}`; + // console.log('switchCmp: ', switchCmp); + // if (switchCmp) { + // console.log(`Pushing hidden switch cmp for "${switchId}"`); + this.config.fields.push({ + xtype: 'hidden', + name: fbarSwitchFieldName, + id: `${switchId}-hidden`, + value: defaultValue + }); + // } + }, + /** + * + * @param {*} windowId + * @param {*} fbarSwitchFieldName + * @param {*} switchLabel + * @param {*} switchIsChecked + * @returns + */ + getFbarSwitch: function(windowId, fbarSwitchFieldName, switchLabel, switchIsChecked = true) { const switchCmp = { xtype: 'xcheckbox', id: `${windowId}-${fbarSwitchFieldName}`, @@ -577,10 +853,13 @@ Ext.extend(MODx.Window,Ext.Window,{ checked: switchIsChecked, listeners: { check: { - fn: function(cmp) { + fn: function(cmp, checked) { const hiddenCmp = Ext.getCmp(`${windowId}-${fbarSwitchFieldName}-hidden`); + // console.log(`fbar hidden id to find: ${windowId}-${fbarSwitchFieldName}-hidden`); + // console.log('fbar switch check evt, hiddenCmp', hiddenCmp); if (hiddenCmp) { - const value = cmp.getValue() === false ? 0 : 1; + // console.log('switch is checked?', checked); + const value = checked === false ? 0 : 1; hiddenCmp.setValue(value); } }, @@ -590,12 +869,16 @@ Ext.extend(MODx.Window,Ext.Window,{ }; // console.log(`getting switch (${fbarSwitchFieldName}): `, switchCmp); return switchCmp; - } + }, /** - * + * + * @param {*} config + * @param {*} isPrimaryButton + * @param {*} isSaveAndClose + * @returns */ - ,getSaveButton: function(config, isPrimaryButton = true, isSaveAndClose = false) { + getSaveButton: function(config, isPrimaryButton = true, isSaveAndClose = false) { // console.log('getSaveButton, this', this); const defaultBtnText = isSaveAndClose ? _('save_and_close') : _('save') ; let btn; @@ -620,22 +903,29 @@ Ext.extend(MODx.Window,Ext.Window,{ } // console.log('getSaveButton, btn:', btn); return btn; - } + }, /** - * + * + * @param {*} config + * @returns */ - ,getWindowButtons: function(config) { - const btns = [{ - text: config.cancelBtnText || _('cancel'), - handler: function() { - this.config.closeAction !== 'close' ? this.hide() : this.close(); - }, - scope: this - }], - specification = config.modxFbarButtons || 'c-s' + getWindowButtons: function(config) { + const + btns = [{ + text: config.cancelBtnText || _('close'), + handler: function() { + if (this.config.closeAction !== 'close') { + this.hide(); + } else { + this.close(); + } + }, + scope: this + }], + specification = config.modxFbarButtons || 'c-s' ; - switch(specification) { + switch (specification) { case 'c-s': btns.push(this.getSaveButton(config)); break; @@ -645,18 +935,22 @@ Ext.extend(MODx.Window,Ext.Window,{ break; case 'custom': break; + // no default } return btns; - } + }, /** - * + * + * @param {*} config + * @returns */ - ,getWindowFbar: function(config) { + getWindowFbar: function(config) { // console.log('getting window fbar...'); - const windowId = config.id, - windowButtons = this.getWindowButtons(config), - footerBar = [] + const + windowId = this.getWindowIdentifier(), + windowButtons = this.getWindowButtons(config), + footerBar = [] ; if (config.modxFbarHasClearCacheSwitch) { const cacheSwitch = this.getFbarSwitch(windowId, 'clearCache', _('clear_cache_on_save')); @@ -681,4 +975,4 @@ Ext.extend(MODx.Window,Ext.Window,{ } }); -Ext.reg('modx-window',MODx.Window); +Ext.reg('modx-window', MODx.Window); From 478fffb612e19a2ff73a3b78dc16aaf0daaaf674 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:33:24 -0400 Subject: [PATCH 11/12] Update windows.js Interim progress, tweaks --- manager/assets/modext/widgets/windows.js | 2321 +++++++++++----------- 1 file changed, 1165 insertions(+), 1156 deletions(-) diff --git a/manager/assets/modext/widgets/windows.js b/manager/assets/modext/widgets/windows.js index 125bc31c80d..7bc4a05eb2e 100644 --- a/manager/assets/modext/widgets/windows.js +++ b/manager/assets/modext/widgets/windows.js @@ -6,96 +6,69 @@ * @param {Object} config An object of options. * @xtype modx-window-resource-duplicate */ -MODx.window.DuplicateResource = function(config) { - - config = config || {}; - const windowId = `window-dup-resource-${Ext.id()}`; - - Ext.applyIf(config,{ - id: windowId - ,title: config.pagetitle ? `${_('duplicate')} ${config.pagetitle}` : _('duplication_options') - ,modxPseudoModal: true - }); - MODx.window.DuplicateResource.superclass.constructor.call(this,config); -}; -Ext.extend(MODx.window.DuplicateResource,MODx.Window,{ - _loadForm: function() { - if (this.checkIfLoaded(this.config.record)) { - this.fp.getForm().baseParams = { - action: 'Resource/Updateduplicate' - ,prefixDuplicate: true - ,id: this.config.resource - }; - return false; - } - var items = []; - items.push({ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('resource_name_new') - ,value: '' - }); - - if (this.config.hasChildren) { - items.push({ - xtype: 'xcheckbox' - ,name: 'duplicate_children' - ,boxLabel: _('duplicate_children') + ' ('+this.config.childCount+')' - ,hideLabel: true - ,checked: true - }); - } - - items.push({ - xtype: 'xcheckbox' - ,name: 'redirect' - ,boxLabel: _('duplicate_redirect') - ,hideLabel: true - ,checked: this.config.redirect - }); - - var pov = MODx.config.default_duplicate_publish_option || 'preserve'; - items.push({ - xtype: 'fieldset' - ,title: _('publishing_options') - ,items: [{ - xtype: 'radiogroup' - ,hideLabel: true - ,columns: 1 - ,value: pov - ,items: [{ - name: 'published_mode' - ,boxLabel: _('po_make_all_unpub') - ,hideLabel: true - ,inputValue: 'unpublish' - },{ - name: 'published_mode' - ,boxLabel: _('po_make_all_pub') - ,hideLabel: true - ,inputValue: 'publish' - },{ - name: 'published_mode' - ,boxLabel: _('po_preserve') - ,hideLabel: true - ,inputValue: 'preserve' +MODx.window.DuplicateResource = function(config = {}) { + const + publishingOpt = MODx.config.default_duplicate_publish_option || 'preserve', + fields = [ + { + xtype: 'textfield', + name: 'name', + fieldLabel: _('resource_name_new'), + value: '' + }, { + xtype: 'fieldset', + title: _('publishing_options'), + items: [{ + xtype: 'radiogroup', + hideLabel: true, + columns: 1, + value: publishingOpt, + items: [{ + name: 'published_mode', + boxLabel: _('po_make_all_unpub'), + hideLabel: true, + inputValue: 'unpublish' + }, { + name: 'published_mode', + boxLabel: _('po_make_all_pub'), + hideLabel: true, + inputValue: 'publish' + }, { + name: 'published_mode', + boxLabel: _('po_preserve'), + hideLabel: true, + inputValue: 'preserve' + }] }] - }] - }); - - this.fp = this.createForm({ - url: this.config.url || MODx.config.connector_url - ,baseParams: this.config.baseParams || { - action: 'Resource/Duplicate' - ,id: this.config.resource - ,prefixDuplicate: true } - ,items: items + ] + ; + this.itemId = `resource-duplicate-${Ext.id()}`; + if (config.hasChildren) { + fields.splice(1, 0, { + xtype: 'xcheckbox', + name: 'duplicate_children', + boxLabel: `${_('duplicate_children')} (${config.childCount})`, + hideLabel: true, + checked: true }); - - this.renderForm(); } -}); -Ext.reg('modx-window-resource-duplicate',MODx.window.DuplicateResource); + + Ext.applyIf(config, { + title: config.pagetitle ? `${_('duplicate')} ${config.pagetitle}` : _('duplication_options'), + modxFbarSaveSwitches: ['redirect'], + fields: fields, + url: config.url || MODx.config.connector_url, + baseParams: config.baseParams || { + action: 'Resource/Duplicate', + id: config.resource, + prefixDuplicate: true + } + }); + MODx.window.DuplicateResource.superclass.constructor.call(this, config); +}; +Ext.extend(MODx.window.DuplicateResource, MODx.Window); +Ext.reg('modx-window-resource-duplicate', MODx.window.DuplicateResource); /** * Generates the Duplicate Element window @@ -105,23 +78,22 @@ Ext.reg('modx-window-resource-duplicate',MODx.window.DuplicateResource); * @param {Object} config An object of options. * @xtype modx-window-element-duplicate */ -MODx.window.DuplicateElement = function(config) { - - config = config || {}; - - const windowId = `window-dup-element-${Ext.id()}`, - staticFileCmpId = `${windowId}-modx-static_file`, - nameFieldName = config.record.type == 'template' ? 'templatename' : 'name' , - createExampleTag = ['tv', 'chunk', 'snippet'].includes(config.record.type), - defaultExampleTag = createExampleTag ? _(`example_tag_${config.record.type}_name`) : '' , - elementNameCmpId = `${windowId}-modx-name`, - nameFieldListeners = { - change: function(cmp) { - cmp.setValue(cmp.getValue().trim()); - } - }, - nameHelpListeners = {} +MODx.window.DuplicateElement = function(config = {}) { + const + windowId = `window-dup-${config.record.type || 'element'}-${Ext.id()}`, + staticFileCmpId = `${windowId}-modx-static_file`, + nameFieldName = config.record.type === 'template' ? 'templatename' : 'name', + createExampleTag = ['tv', 'chunk', 'snippet'].includes(config.record.type), + defaultExampleTag = createExampleTag ? _(`example_tag_${config.record.type}_name`) : '', + elementNameCmpId = `${windowId}-modx-name`, + nameFieldListeners = { + change: function(cmp) { + cmp.setValue(cmp.getValue().trim()); + } + }, + nameHelpListeners = {} ; + this.itemId = windowId; if (createExampleTag) { Object.assign(nameHelpListeners, { afterrender: function(cmp) { @@ -129,92 +101,86 @@ MODx.window.DuplicateElement = function(config) { } }); } - // console.log('record:', config.record); - // console.log('name field listeners: ',nameFieldListeners); - const flds = [{ - xtype: 'hidden' - ,name: 'id' - },{ - xtype: 'hidden' - ,name: 'source' - },{ - xtype: 'textfield' - ,name: nameFieldName - ,id: elementNameCmpId - ,fieldLabel: _(`${config.record.type}_new_name`) || _('element_name_new') - ,enableKeyEvents: true - ,allowBlank: false - ,listeners: nameFieldListeners - ,value: config.record.name - },{ - xtype: 'box' - ,hidden: MODx.expandHelp ? false : true - ,html: createExampleTag - ? _(`${config.record.type}_name_desc`, { - tag: `[[*${defaultExampleTag}]]` - }) - : _(`${config.record.type}_name_desc`) || '' - ,cls: 'desc-under' - ,listeners: nameHelpListeners + + const fields = [{ + xtype: 'hidden', + name: 'id' + }, { + xtype: 'hidden', + name: 'source' + }, { + xtype: 'textfield', + name: nameFieldName, + id: elementNameCmpId, + fieldLabel: _(`${config.record.type}_new_name`) || _('element_name_new'), + description: MODx.expandHelp ? '' : this.getElementNameDescription(config.record.type, defaultExampleTag, true), + enableKeyEvents: true, + allowBlank: false, + listeners: nameFieldListeners, + value: config.record.name + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: MODx.expandHelp ? this.getElementNameDescription(config.record.type, defaultExampleTag) : '', + cls: 'desc-under', + listeners: nameHelpListeners }]; - if (config.record.type == 'tv') { - flds.push({ - xtype: 'textfield' - ,name: 'caption' - ,fieldLabel: _(`tv_new_caption`) || _('element_caption_new') - ,value: config.record.caption - },{ - xtype: 'box' - ,hidden: MODx.expandHelp ? false : true - ,html: _('tv_caption_desc') - ,cls: 'desc-under' + if (config.record.type === 'tv') { + console.log('TV record being dupd: ', config.record); + fields.push({ + xtype: 'textfield', + name: 'caption', + fieldLabel: _('tv_new_caption') || _('element_caption_new'), + value: config.record.caption + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('tv_caption_desc'), + cls: 'desc-under' }); } if (config.record.static === true) { - flds.push({ - xtype: 'textfield' - ,name: 'static_file' - ,id: staticFileCmpId - ,fieldLabel: _('static_file') - ,listeners: { + fields.push({ + xtype: 'textfield', + name: 'static_file', + id: staticFileCmpId, + fieldLabel: _('static_file'), + listeners: { change: { fn: function(cmp) { - const file = cmp.getValue().trim(); - if (!Ext.isEmpty(file)) { - const fileName = - cmp.setValue(MODx.util.Format.fileFullPath(file)); - } + // const file = cmp.getValue().trim(); + // if (!Ext.isEmpty(file)) { + // const fileName = cmp.setValue(MODx.util.Format.fileFullPath(file)); + // } }, scope: this } } - },{ - xtype: 'box' - ,hidden: MODx.expandHelp ? false : true - ,html: _('static_file_desc') - ,cls: 'desc-under' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('static_file_desc'), + cls: 'desc-under' }); } - Ext.applyIf(config,{ - id: windowId - ,title: _('duplicate_'+config.record.type) - ,url: MODx.config.connector_url - ,action: `element/${config.record.type}/duplicate` - ,width: 600 - ,fields: flds - ,labelWidth: 150 - ,modxPseudoModal: true - ,modxFbarSaveSwitches: config.record.type == 'tv' ? ['duplicateValues', 'redirect'] : ['redirect'] + Ext.applyIf(config, { + title: _(`duplicate_${config.record.type}`), + url: MODx.config.connector_url, + action: `element/${config.record.type}/duplicate`, + width: 600, + fields: fields, + labelWidth: 150, + modxFbarSaveSwitches: config.record.type === 'tv' ? ['duplicateValues', 'redirect'] : ['redirect'] }); - MODx.window.DuplicateElement.superclass.constructor.call(this,config); + MODx.window.DuplicateElement.superclass.constructor.call(this, config); if (this.config.record.static) { - - const elementAutomationType = `${this.config.record.type}s`, - staticsAutomationConfigKey = `static_elements_automate_${elementAutomationType}` + const + elementAutomationType = `${this.config.record.type}s`, + staticsAutomationConfigKey = `static_elements_automate_${elementAutomationType}` ; this.staticsAutomated = MODx.config[staticsAutomationConfigKey] ? true : false ; @@ -223,9 +189,10 @@ MODx.window.DuplicateElement = function(config) { this.staticElementType = elementAutomationType; this.getElementCategoryName(elementCategory); } else { - const currentPath = this.config.record.static_file, - fileName = currentPath.indexOf('/') !== -1 ? currentPath.split('/').pop() : currentPath, - fileExt = fileName.indexOf('.') !== -1 ? fileName.slice(fileName.lastIndexOf('.')) : '' + const + currentPath = this.config.record.static_file, + fileName = currentPath.indexOf('/') !== -1 ? currentPath.split('/').pop() : currentPath, + fileExt = fileName.indexOf('.') !== -1 ? fileName.slice(fileName.lastIndexOf('.')) : '' ; this.staticElementBasePath = currentPath.replace(fileName, ''); this.staticElementFileExt = fileExt; @@ -262,20 +229,21 @@ MODx.window.DuplicateElement = function(config) { } }); } - }; -Ext.extend(MODx.window.DuplicateElement,MODx.Window, { - +Ext.extend(MODx.window.DuplicateElement, MODx.Window, { + /** + * Get the Element's category name by its assigned category id (if any) + * @param {*} categoryId The category's numeric id + */ getElementCategoryName: function(categoryId) { - if (typeof categoryId === 'number' && categoryId > 0) { MODx.Ajax.request({ - url: MODx.config.connector_url - ,params: { - action: 'Element/Category/GetList' - ,id: categoryId - } - ,listeners: { + url: MODx.config.connector_url, + params: { + action: 'Element/Category/GetList', + id: categoryId + }, + listeners: { success: { fn: function(response) { response.results.forEach(result => { @@ -287,48 +255,62 @@ Ext.extend(MODx.window.DuplicateElement,MODx.Window, { scope: this } } - }); + }); } else { this.staticElementCategoryName = ''; } + }, + /** + * Retrieve a formatted description for an Element's name field + * @param {String} elementType The Element's short identifier (i.e., chunk, tv, etc.) + * @param {String} defaultExampleTag Pre-formatted MODx tag for placeable Elements (i.e., chunks, snippets, tvs) + * @param {Boolean} isCmpDescription Whether the target for the genereated description is the main field component (as opposed to the separate help component shown when MODx.expandHelp is active) + * @returns The formatted description + */ + getElementNameDescription: function(elementType, defaultExampleTag = '', isCmpDescription = false) { + if (Ext.isEmpty(defaultExampleTag)) { + return _(`${elementType}_name_desc`) || ''; + } + return isCmpDescription + ? _(`${elementType}_name_desc`, { + tag: `[[*${defaultExampleTag}]]` + }) + : _(`${elementType}_name_desc`, { + tag: `[[*${defaultExampleTag}]]` + }) + ; } }); -Ext.reg('modx-window-element-duplicate',MODx.window.DuplicateElement); +Ext.reg('modx-window-element-duplicate', MODx.window.DuplicateElement); -MODx.window.CreateCategory = function(config) { - config = config || {}; - // this.ident = config.ident || 'ccat'+Ext.id(); - Ext.applyIf(config,{ - title: _('category_create') - ,id: this.ident - ,url: MODx.config.connector_url - ,action: 'Element/Category/Create' - ,modxPseudoModal: true - ,fields: [{ - xtype: 'modx-description' - ,html: _('category_create_desc') - },{ - fieldLabel: _('name') - ,name: 'category' - // ,id: 'modx-'+this.ident+'-category' - ,xtype: 'textfield' - },{ - fieldLabel: _('parent') - ,name: 'parent' - ,hiddenName: 'parent' - // ,id: 'modx-'+this.ident+'-parent' - ,xtype: 'modx-combo-category' - },{ - fieldLabel: _('rank') - ,name: 'rank' - // ,id: 'modx-'+this.ident+'-rank' - ,xtype: 'numberfield' +MODx.window.CreateCategory = function(config = {}) { + this.itemId = `window-create-category-${Ext.id()}`; + Ext.applyIf(config, { + title: _('category_create'), + url: MODx.config.connector_url, + action: 'Element/Category/Create', + fields: [{ + xtype: 'modx-description', + html: _('category_create_desc') + }, { + xtype: 'textfield', + fieldLabel: _('name'), + name: 'category' + }, { + xtype: 'modx-combo-category', + fieldLabel: _('parent'), + name: 'parent', + hiddenName: 'parent' + }, { + xtype: 'numberfield', + fieldLabel: _('rank'), + name: 'rank' }] }); - MODx.window.CreateCategory.superclass.constructor.call(this,config); + MODx.window.CreateCategory.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.CreateCategory,MODx.Window); -Ext.reg('modx-window-category-create',MODx.window.CreateCategory); +Ext.extend(MODx.window.CreateCategory, MODx.Window); +Ext.reg('modx-window-category-create', MODx.window.CreateCategory); /** * Generates the Rename Category window. @@ -338,1062 +320,1015 @@ Ext.reg('modx-window-category-create',MODx.window.CreateCategory); * @param {Object} config An object of options. * @xtype modx-window-category-rename */ -MODx.window.RenameCategory = function(config) { - config = config || {}; - this.ident = config.ident || 'rencat-'+Ext.id(); - Ext.applyIf(config,{ - title: _('category_rename') - ,url: MODx.config.connector_url - ,action: 'Element/Category/Update' - ,fields: [{ - xtype: 'hidden' - ,name: 'id' - ,id: 'modx-'+this.ident+'-id' - ,value: config.record.id - },{ - xtype: 'textfield' - ,fieldLabel: _('name') - ,name: 'category' - ,id: 'modx-'+this.ident+'-category' - ,width: 150 - ,value: config.record.category - },{ - fieldLabel: _('rank') - ,name: 'rank' - ,id: 'modx-'+this.ident+'-rank' - ,xtype: 'numberfield' +MODx.window.RenameCategory = function(config = {}) { + this.itemId = `window-update-category-${Ext.id()}`; + Ext.applyIf(config, { + title: _('category_rename'), + url: MODx.config.connector_url, + action: 'Element/Category/Update', + fields: [{ + xtype: 'hidden', + name: 'id', + value: config.record.id + }, { + xtype: 'textfield', + fieldLabel: _('name'), + name: 'category', + value: config.record.category + }, { + xtype: 'numberfield', + fieldLabel: _('rank'), + name: 'rank' }] }); - MODx.window.RenameCategory.superclass.constructor.call(this,config); + MODx.window.RenameCategory.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.RenameCategory,MODx.Window); -Ext.reg('modx-window-category-rename',MODx.window.RenameCategory); - - -MODx.window.CreateNamespace = function(config) { - config = config || {}; - var r = config.record; - this.ident = config.ident || 'cns'+Ext.id(); - Ext.applyIf(config,{ - title: _('create') - ,id: this.ident - ,width: 600 - ,url: MODx.config.connector_url - ,action: 'Workspace/PackageNamespace/Create' - ,fields: [{ - xtype: 'textfield' - ,fieldLabel: _('name') - ,description: MODx.expandHelp ? '' : _('namespace_name_desc') - ,name: 'name' - ,id: 'modx-'+this.ident+'-name' - ,anchor: '100%' - ,maxLength: 100 - ,readOnly: config.isUpdate || false - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-name' - ,html: _('namespace_name_desc') - ,cls: 'desc-under' - - },{ - xtype: 'textfield' - ,fieldLabel: _('namespace_path') - ,description: MODx.expandHelp ? '' : _('namespace_path_desc') - ,name: 'path' - ,id: 'modx-'+this.ident+'-path' - ,anchor: '100%' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-path' - ,html: _('namespace_path_desc') - ,cls: 'desc-under' +Ext.extend(MODx.window.RenameCategory, MODx.Window); +Ext.reg('modx-window-category-rename', MODx.window.RenameCategory); - },{ - xtype: 'textfield' - ,fieldLabel: _('namespace_assets_path') - ,description: MODx.expandHelp ? '' : _('namespace_assets_path_desc') - ,name: 'assets_path' - ,id: 'modx-'+this.ident+'-assets-path' - ,anchor: '100%' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,forId: 'modx-'+this.ident+'-assets-path' - ,html: _('namespace_assets_path_desc') - ,cls: 'desc-under' +MODx.window.CreateNamespace = function(config = {}) { + const action = config.isUpdate ? 'update' : 'create'; + this.itemId = `window-namespace-${action}-${Ext.id()}`; + Ext.applyIf(config, { + title: _('create'), + width: 600, + url: MODx.config.connector_url, + action: 'Workspace/PackageNamespace/Create', + cls: 'qce-window qce-create', + fields: [{ + xtype: 'textfield', + fieldLabel: _('name'), + description: MODx.expandHelp ? '' : _('namespace_name_desc'), + name: 'name', + maxLength: 100, + readOnly: config.isUpdate || false + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('namespace_name_desc'), + cls: 'desc-under' + }, { + xtype: 'textfield', + fieldLabel: _('namespace_path'), + description: MODx.expandHelp ? '' : _('namespace_path_desc'), + name: 'path' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('namespace_path_desc'), + cls: 'desc-under' + }, { + xtype: 'textfield', + fieldLabel: _('namespace_assets_path'), + description: MODx.expandHelp ? '' : _('namespace_assets_path_desc'), + name: 'assets_path' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('namespace_assets_path_desc'), + cls: 'desc-under' }] }); - MODx.window.CreateNamespace.superclass.constructor.call(this,config); + MODx.window.CreateNamespace.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.CreateNamespace,MODx.Window); -Ext.reg('modx-window-namespace-create',MODx.window.CreateNamespace); - -MODx.window.UpdateNamespace = function(config) { - config = config || {}; +Ext.extend(MODx.window.CreateNamespace, MODx.Window); +Ext.reg('modx-window-namespace-create', MODx.window.CreateNamespace); +MODx.window.UpdateNamespace = function(config = {}) { Ext.applyIf(config, { - title: _('edit') - ,action: 'Workspace/PackageNamespace/Update' - ,isUpdate: true + title: _('edit'), + action: 'Workspace/PackageNamespace/Update', + isUpdate: true }); MODx.window.UpdateNamespace.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.UpdateNamespace, MODx.window.CreateNamespace, {}); -Ext.reg('modx-window-namespace-update',MODx.window.UpdateNamespace); - +Ext.extend(MODx.window.UpdateNamespace, MODx.window.CreateNamespace); +Ext.reg('modx-window-namespace-update', MODx.window.UpdateNamespace); -MODx.window.QuickCreateChunk = function(config) { - - config = config || {}; - const windowId = `window-qce-chunk-${Ext.id()}`; - - Ext.applyIf(config,{ - id: windowId - ,title: _('quick_create_chunk') - ,width: 700 - ,layout: 'form' - ,url: MODx.config.connector_url - ,action: 'Element/Chunk/Create' - ,cls: 'qce-window qce-create' - ,modxPseudoModal: true - ,modxFbarSaveSwitches: ['clearCache'] - ,fields: [{ - xtype: 'hidden' - ,name: 'id' - ,value: config.record.id || 0 - },{ +MODx.window.QuickCreateChunk = function(config = {}) { + const action = config.isUpdate ? 'update' : 'create'; + this.itemId = `window-chunk-${action}-${Ext.id()}`; + Ext.applyIf(config, { + title: _('quick_create_chunk'), + width: 700, + layout: 'form', + url: MODx.config.connector_url, + action: 'Element/Chunk/Create', + cls: 'qce-window qce-create', + modxFbarSaveSwitches: ['clearCache'], + fields: [{ + xtype: 'hidden', + name: 'id', + value: config.record.id || 0 + }, { // row 1 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,allowBlank: false - ,maxLength: 50 - ,value: config.record.name || '' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textfield', + name: 'name', + fieldLabel: _('name'), + allowBlank: false, + maxLength: 50, + value: config.record.name || '' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,description: MODx.expandHelp ? '' : _('chunk_category_desc') - ,value: config.record.category || 0 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('chunk_category_desc') - ,cls: 'desc-under' + }, { + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + items: [{ + xtype: 'modx-combo-category', + name: 'category', + fieldLabel: _('category'), + description: MODx.expandHelp ? '' : _('chunk_category_desc'), + value: config.record.category || 0 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('chunk_category_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 2 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,name: 'description' - ,description: MODx.expandHelp ? '' : _('chunk_description_desc') - ,fieldLabel: _('description') - ,grow: true - ,growMin: 50 - ,growMax: this.isSmallScreen ? 90 : 120 - ,value: config.record.description || '' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('chunk_description_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + name: 'description', + description: MODx.expandHelp ? '' : _('chunk_description_desc'), + fieldLabel: _('description'), + grow: true, + growMin: 50, + growMax: this.isSmallScreen ? 90 : 120, + value: config.record.description || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('chunk_description_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 3 - cls:'form-row-wrapper', - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,fieldLabel: _('chunk_code') - ,name: 'snippet' - ,grow: true - ,growMin: 90 - ,growMax: this.isSmallScreen ? 160 : 300 - ,value: config.record.snippet || '' + cls: 'form-row-wrapper', + layout: 'form', + labelSeparator: '', + labelAlign: 'top', + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + fieldLabel: _('chunk_code'), + name: 'snippet', + grow: true, + growMin: 90, + growMax: this.isSmallScreen ? 160 : 300, + value: config.record.snippet || '' }] - }] - ,keys: [{ - key: Ext.EventObject.ENTER - ,shift: true - ,fn: this.submit - ,scope: this + }], + keys: [{ + key: Ext.EventObject.ENTER, + shift: true, + fn: this.submit, + scope: this }] }); - MODx.window.QuickCreateChunk.superclass.constructor.call(this,config); + MODx.window.QuickCreateChunk.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickCreateChunk,MODx.Window); -Ext.reg('modx-window-quick-create-chunk',MODx.window.QuickCreateChunk); +Ext.extend(MODx.window.QuickCreateChunk, MODx.Window); +Ext.reg('modx-window-quick-create-chunk', MODx.window.QuickCreateChunk); -MODx.window.QuickUpdateChunk = function(config) { - config = config || {}; - - Ext.applyIf(config,{ - title: _('quick_update_chunk') - ,action: 'Element/Chunk/Update' - ,cls: 'qce-window qce-update' - ,modxFbarButtons: 'c-s-sc' +MODx.window.QuickUpdateChunk = function(config = {}) { + Ext.applyIf(config, { + title: _('quick_update_chunk'), + action: 'Element/Chunk/Update', + cls: 'qce-window qce-update', + modxFbarButtons: 'c-s-sc', + isUpdate: true }); - MODx.window.QuickUpdateChunk.superclass.constructor.call(this,config); + MODx.window.QuickUpdateChunk.superclass.constructor.call(this, config); }; Ext.extend(MODx.window.QuickUpdateChunk, MODx.window.QuickCreateChunk); -Ext.reg('modx-window-quick-update-chunk',MODx.window.QuickUpdateChunk); - - -MODx.window.QuickCreateTemplate = function(config) { +Ext.reg('modx-window-quick-update-chunk', MODx.window.QuickUpdateChunk); - config = config || {}; - const windowId = `window-qce-template-${Ext.id()}`; - - Ext.applyIf(config,{ - id: windowId - ,title: _('quick_create_template') - ,width: 700 - ,url: MODx.config.connector_url - ,action: 'Element/Template/Create' - ,cls: 'qce-window qce-create' - ,modxPseudoModal: true - ,modxFbarSaveSwitches: ['clearCache'] - ,fields: [{ - xtype: 'hidden' - ,name: 'id' - ,value: config.record.id || 0 - },{ +MODx.window.QuickCreateTemplate = function(config = {}) { + const action = config.isUpdate ? 'update' : 'create'; + this.itemId = `window-template-${action}-${Ext.id()}`; + Ext.applyIf(config, { + title: _('quick_create_template'), + width: 700, + url: MODx.config.connector_url, + action: 'Element/Template/Create', + cls: 'qce-window qce-create', + modxFbarSaveSwitches: ['clearCache'], + fields: [{ + xtype: 'hidden', + name: 'id', + value: config.record.id || 0 + }, { // row 1 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textfield' - ,name: 'templatename' - ,fieldLabel: _('name') - ,allowBlank: false - ,maxLength: 50 - ,value: config.record.templatename || '' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textfield', + name: 'templatename', + fieldLabel: _('name'), + allowBlank: false, + maxLength: 50, + value: config.record.templatename || '' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,description: MODx.expandHelp ? '' : _('template_category_desc') - ,value: config.record.category || 0 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('template_category_desc') - ,cls: 'desc-under' + }, { + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + items: [{ + xtype: 'modx-combo-category', + name: 'category', + fieldLabel: _('category'), + description: MODx.expandHelp ? '' : _('template_category_desc'), + value: config.record.category || 0 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('template_category_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 2 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 1 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,name: 'description' - ,description: MODx.expandHelp ? '' : _('template_description_desc') - ,fieldLabel: _('description') - ,grow: true - ,growMin: 50 - ,growMax: this.isSmallScreen ? 90 : 120 - ,value: config.record.description || '' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('template_description_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 1, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + name: 'description', + description: MODx.expandHelp ? '' : _('template_description_desc'), + fieldLabel: _('description'), + grow: true, + growMin: 50, + growMax: this.isSmallScreen ? 90 : 120, + value: config.record.description || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('template_description_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 3 - cls:'form-row-wrapper', - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,fieldLabel: _('template_code') - ,name: 'content' - ,grow: true - ,growMin: 120 - ,growMax: this.isSmallScreen ? 160 : 300 - ,value: config.record.content || '' + cls: 'form-row-wrapper', + layout: 'form', + labelSeparator: '', + labelAlign: 'top', + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + fieldLabel: _('template_code'), + name: 'content', + grow: true, + growMin: 120, + growMax: this.isSmallScreen ? 160 : 300, + value: config.record.content || '' }] - }] - ,keys: [{ - key: Ext.EventObject.ENTER - ,shift: true - ,fn: this.submit - ,scope: this + }], + keys: [{ + key: Ext.EventObject.ENTER, + shift: true, + fn: this.submit, + scope: this }] }); - MODx.window.QuickCreateTemplate.superclass.constructor.call(this,config); + MODx.window.QuickCreateTemplate.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickCreateTemplate,MODx.Window); -Ext.reg('modx-window-quick-create-template',MODx.window.QuickCreateTemplate); - -MODx.window.QuickUpdateTemplate = function(config) { - config = config || {}; +Ext.extend(MODx.window.QuickCreateTemplate, MODx.Window); +Ext.reg('modx-window-quick-create-template', MODx.window.QuickCreateTemplate); - Ext.applyIf(config,{ - title: _('quick_update_template') - ,action: 'Element/Template/Update' - ,cls: 'qce-window qce-update' - ,modxFbarButtons: 'c-s-sc' +MODx.window.QuickUpdateTemplate = function(config = {}) { + Ext.applyIf(config, { + title: _('quick_update_template'), + action: 'Element/Template/Update', + cls: 'qce-window qce-update', + modxFbarButtons: 'c-s-sc', + isUpdate: true }); - MODx.window.QuickUpdateTemplate.superclass.constructor.call(this,config); + MODx.window.QuickUpdateTemplate.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickUpdateTemplate,MODx.window.QuickCreateTemplate); -Ext.reg('modx-window-quick-update-template',MODx.window.QuickUpdateTemplate); - - -MODx.window.QuickCreateSnippet = function(config) { +Ext.extend(MODx.window.QuickUpdateTemplate, MODx.window.QuickCreateTemplate); +Ext.reg('modx-window-quick-update-template', MODx.window.QuickUpdateTemplate); - config = config || {}; - const windowId = `window-qce-snippet-${Ext.id()}`; - - Ext.applyIf(config,{ - id: windowId - ,title: _('quick_create_snippet') - ,width: 700 - ,url: MODx.config.connector_url - ,action: 'Element/Snippet/Create' - ,cls: 'qce-window qce-create' - ,modxPseudoModal: true - ,modxFbarSaveSwitches: ['clearCache'] - ,fields: [{ - xtype: 'hidden' - ,name: 'id' - ,value: config.record.id || 0 - },{ +MODx.window.QuickCreateSnippet = function(config = {}) { + const action = config.isUpdate ? 'update' : 'create'; + this.itemId = `window-snippet-${action}-${Ext.id()}`; + Ext.applyIf(config, { + title: _('quick_create_snippet'), + width: 700, + url: MODx.config.connector_url, + action: 'Element/Snippet/Create', + cls: 'qce-window qce-create', + modxPseudoModal: true, + modxFbarSaveSwitches: ['clearCache'], + fields: [{ + xtype: 'hidden', + name: 'id', + value: config.record.id || 0 + }, { // row 1 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,allowBlank: false - ,maxLength: 50 - ,value: config.record.name || '' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textfield', + name: 'name', + fieldLabel: _('name'), + allowBlank: false, + maxLength: 50, + value: config.record.name || '' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,description: MODx.expandHelp ? '' : _('snippet_category_desc') - ,value: config.record.category || 0 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('snippet_category_desc') - ,cls: 'desc-under' + }, { + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + items: [{ + xtype: 'modx-combo-category', + name: 'category', + fieldLabel: _('category'), + description: MODx.expandHelp ? '' : _('snippet_category_desc'), + value: config.record.category || 0 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('snippet_category_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 2 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 1 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,name: 'description' - ,description: MODx.expandHelp ? '' : _('snippet_description_desc') - ,fieldLabel: _('description') - ,grow: true - ,growMin: 50 - ,growMax: this.isSmallScreen ? 90 : 120 - ,value: config.record.description || '' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('snippet_description_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 1, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + name: 'description', + description: MODx.expandHelp ? '' : _('snippet_description_desc'), + fieldLabel: _('description'), + grow: true, + growMin: 50, + growMax: this.isSmallScreen ? 90 : 120, + value: config.record.description || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('snippet_description_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 3 - cls:'form-row-wrapper', - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,fieldLabel: _('snippet_code') - ,name: 'snippet' - ,id: `modx-${this.ident}-code` - ,grow: true - ,growMin: 90 - ,growMax: this.isSmallScreen ? 160 : 300 - ,value: config.record.snippet || '' + cls: 'form-row-wrapper', + layout: 'form', + labelSeparator: '', + labelAlign: 'top', + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + fieldLabel: _('snippet_code'), + name: 'snippet', + id: `modx-${this.ident}-code`, + grow: true, + growMin: 90, + growMax: this.isSmallScreen ? 160 : 300, + value: config.record.snippet || '' }] - }] - ,keys: [{ - key: Ext.EventObject.ENTER - ,shift: true - ,fn: this.submit - ,scope: this + }], + keys: [{ + key: Ext.EventObject.ENTER, + shift: true, + fn: this.submit, + scope: this }] }); - MODx.window.QuickCreateSnippet.superclass.constructor.call(this,config); + MODx.window.QuickCreateSnippet.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickCreateSnippet,MODx.Window); -Ext.reg('modx-window-quick-create-snippet',MODx.window.QuickCreateSnippet); +Ext.extend(MODx.window.QuickCreateSnippet, MODx.Window); +Ext.reg('modx-window-quick-create-snippet', MODx.window.QuickCreateSnippet); -MODx.window.QuickUpdateSnippet = function(config) { - config = config || {}; - - Ext.applyIf(config,{ - title: _('quick_update_snippet') - ,action: 'Element/Snippet/Update' - ,cls: 'qce-window qce-update' - ,modxFbarButtons: 'c-s-sc' +MODx.window.QuickUpdateSnippet = function(config = {}) { + Ext.applyIf(config, { + title: _('quick_update_snippet'), + action: 'Element/Snippet/Update', + cls: 'qce-window qce-update', + modxFbarButtons: 'c-s-sc', + isUpdate: true }); - MODx.window.QuickUpdateSnippet.superclass.constructor.call(this,config); + MODx.window.QuickUpdateSnippet.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickUpdateSnippet,MODx.window.QuickCreateSnippet); -Ext.reg('modx-window-quick-update-snippet',MODx.window.QuickUpdateSnippet); - - -MODx.window.QuickCreatePlugin = function(config) { +Ext.extend(MODx.window.QuickUpdateSnippet, MODx.window.QuickCreateSnippet); +Ext.reg('modx-window-quick-update-snippet', MODx.window.QuickUpdateSnippet); - config = config || {}; - const windowId = `window-qce-plugin-${Ext.id()}`; - - Ext.applyIf(config,{ - id: windowId - ,title: _('quick_create_plugin') - ,width: 700 - ,layout: 'anchor' - ,url: MODx.config.connector_url - ,action: 'Element/Plugin/Create' - ,modxPseudoModal: true - ,modxFbarSaveSwitches: ['clearCache'] - ,fields: [{ - xtype: 'hidden' - ,name: 'id' - ,value: config.record.id || 0 - },{ +MODx.window.QuickCreatePlugin = function(config = {}) { + const action = config.isUpdate ? 'update' : 'create'; + this.itemId = `window-plugin-${action}-${Ext.id()}`; + Ext.applyIf(config, { + title: _('quick_create_plugin'), + width: 700, + layout: 'anchor', + url: MODx.config.connector_url, + action: 'Element/Plugin/Create', + modxPseudoModal: true, + modxFbarSaveSwitches: ['clearCache'], + fields: [{ + xtype: 'hidden', + name: 'id', + value: config.record.id || 0 + }, { // row 1 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,allowBlank: false - ,maxLength: 50 - ,value: config.record.name || '' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textfield', + name: 'name', + fieldLabel: _('name'), + allowBlank: false, + maxLength: 50, + value: config.record.name || '' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,description: MODx.expandHelp ? '' : _('plugin_category_desc') - ,value: config.record.category || 0 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('plugin_category_desc') - ,cls: 'desc-under' + }, { + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + items: [{ + xtype: 'modx-combo-category', + name: 'category', + fieldLabel: _('category'), + description: MODx.expandHelp ? '' : _('plugin_category_desc'), + value: config.record.category || 0 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('plugin_category_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 2 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,name: 'description' - ,description: MODx.expandHelp ? '' : _('plugin_description_desc') - ,fieldLabel: _('description') - ,grow: true - ,growMin: 50 - ,growMax: this.isSmallScreen ? 90 : 120 - ,value: config.record.description || '' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('plugin_description_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + name: 'description', + description: MODx.expandHelp ? '' : _('plugin_description_desc'), + fieldLabel: _('description'), + grow: true, + growMin: 50, + growMax: this.isSmallScreen ? 90 : 120, + value: config.record.description || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('plugin_description_desc'), + cls: 'desc-under' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'xcheckbox' - ,name: 'disabled' - ,hideLabel: true - ,boxLabel: _('plugin_disabled') - ,description: MODx.expandHelp ? '' : _('plugin_disabled_desc') - ,ctCls: 'add-label-space' - ,inputValue: 1 - ,checked: config.record.disabled || 0 - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('plugin_disabled_desc') - ,cls: 'desc-under toggle-slider-above' + }, { + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + items: [{ + xtype: 'xcheckbox', + name: 'disabled', + hideLabel: true, + boxLabel: _('plugin_disabled'), + description: MODx.expandHelp ? '' : _('plugin_disabled_desc'), + ctCls: 'add-label-space', + inputValue: 1, + checked: config.record.disabled || 0 + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('plugin_disabled_desc'), + cls: 'desc-under toggle-slider-above' }] }] }] - },{ + }, { // row 3 - cls:'form-row-wrapper', - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,fieldLabel: _('plugin_code') - ,name: 'plugincode' - ,grow: true - ,growMin: 90 - ,growMax: this.isSmallScreen ? 160 : 300 - ,value: config.record.plugincode || '' + cls: 'form-row-wrapper', + layout: 'form', + labelSeparator: '', + labelAlign: 'top', + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + fieldLabel: _('plugin_code'), + name: 'plugincode', + grow: true, + growMin: 90, + growMax: this.isSmallScreen ? 160 : 300, + value: config.record.plugincode || '' }] - }] - ,keys: [{ - key: Ext.EventObject.ENTER - ,shift: true - ,fn: this.submit - ,scope: this + }], + keys: [{ + key: Ext.EventObject.ENTER, + shift: true, + fn: this.submit, + scope: this }] }); - MODx.window.QuickCreatePlugin.superclass.constructor.call(this,config); + MODx.window.QuickCreatePlugin.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickCreatePlugin,MODx.Window); -Ext.reg('modx-window-quick-create-plugin',MODx.window.QuickCreatePlugin); - -MODx.window.QuickUpdatePlugin = function(config) { - config = config || {}; +Ext.extend(MODx.window.QuickCreatePlugin, MODx.Window); +Ext.reg('modx-window-quick-create-plugin', MODx.window.QuickCreatePlugin); - Ext.applyIf(config,{ - title: _('quick_update_plugin') - ,action: 'Element/Plugin/Update' - ,cls: 'qce-window qce-update' - ,modxFbarButtons: 'c-s-sc' +MODx.window.QuickUpdatePlugin = function(config = {}) { + Ext.applyIf(config, { + title: _('quick_update_plugin'), + action: 'Element/Plugin/Update', + cls: 'qce-window qce-update', + modxFbarButtons: 'c-s-sc', + isUpdate: true }); - MODx.window.QuickUpdatePlugin.superclass.constructor.call(this,config); + MODx.window.QuickUpdatePlugin.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickUpdatePlugin,MODx.window.QuickCreatePlugin); -Ext.reg('modx-window-quick-update-plugin',MODx.window.QuickUpdatePlugin); +Ext.extend(MODx.window.QuickUpdatePlugin, MODx.window.QuickCreatePlugin); +Ext.reg('modx-window-quick-update-plugin', MODx.window.QuickUpdatePlugin); - -MODx.window.QuickCreateTV = function(config) { - - config = config || {}; - const windowId = `window-qce-tv-${Ext.id()}`; - - Ext.applyIf(config,{ - id: windowId - ,title: _('quick_create_tv') - ,width: 640 - ,url: MODx.config.connector_url - ,action: 'Element/TemplateVar/Create' - ,cls: 'qce-window qce-create' - ,modxPseudoModal: true - ,modxFbarSaveSwitches: ['clearCache'] - // ,saveBtnText: 'Test Save' - // ,cancelBtnText: 'Test Cancel' - ,fields: [{ - xtype: 'hidden' - ,name: 'id' - ,value: config.record.id || 0 - },{ +MODx.window.QuickCreateTV = function(config = {}) { + const action = config.isUpdate ? 'update' : 'create'; + this.itemId = `window-tv-${action}-${Ext.id()}`; + Ext.applyIf(config, { + title: _('quick_create_tv'), + width: 640, + url: MODx.config.connector_url, + action: 'Element/TemplateVar/Create', + cls: 'qce-window qce-create', + modxFbarSaveSwitches: ['clearCache'], + fields: [{ + xtype: 'hidden', + name: 'id', + value: config.record.id || 0 + }, { // row 1 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textfield' - ,name: 'name' - ,fieldLabel: _('name') - ,description: MODx.expandHelp ? '' : _('tv_name_desc', { + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textfield', + name: 'name', + fieldLabel: _('name'), + description: MODx.expandHelp ? '' : _('tv_name_desc', { tag: `[[*${_('example_tag_tv_name')}]]` - }) - ,allowBlank: false - ,maxLength: 50 - ,value: config.record.name || '' - ,enableKeyEvents: true - ,listeners: { + }), + allowBlank: false, + maxLength: 50, + value: config.record.name || '', + enableKeyEvents: true, + listeners: { keyup: { fn: function(cmp, e) { let title = Ext.util.Format.stripTags(cmp.getValue()), tagTitle - ; + ; title = Ext.util.Format.htmlEncode(title); tagTitle = title.length > 0 ? title : _('example_tag_tv_name'); cmp.nextSibling().getEl().child('.example-replace-name').update(tagTitle); - } - ,scope: this + }, + scope: this } } - },{ - xtype: 'box' - ,hidden: MODx.expandHelp ? false : true - ,html: _('tv_name_desc', { + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('tv_name_desc', { tag: `[[*${_('example_tag_tv_name')}]]` - }) - ,cls: 'desc-under' - ,listeners: { + }), + cls: 'desc-under', + listeners: { afterrender: { fn: function(cmp) { MODx.util.insertTagCopyUtility(cmp, 'tv'); - } - ,scope: this + }, + scope: this } } }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'modx-combo-tv-input-type' - ,fieldLabel: _('tv_type') - ,name: 'type' - ,value: config.record.type || 'text' - },{ - xtype: 'box' - ,hidden: MODx.expandHelp ? false : true - ,html: _('tv_type_desc') - ,cls: 'desc-under' + }, { + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + items: [{ + xtype: 'modx-combo-tv-input-type', + fieldLabel: _('tv_type'), + name: 'type', + value: config.record.type || 'text' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('tv_type_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 2 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textfield' - ,name: 'caption' - ,fieldLabel: _('caption') - ,description: MODx.expandHelp ? '' : _('tv_caption_desc') - ,maxLength: 50 - ,value: config.record.caption || '' - },{ - xtype: 'box' - ,hidden: MODx.expandHelp ? false : true - ,html: _('tv_caption_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textfield', + name: 'caption', + fieldLabel: _('caption'), + description: MODx.expandHelp ? '' : _('tv_caption_desc'), + maxLength: 50, + value: config.record.caption || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('tv_caption_desc'), + cls: 'desc-under' }] - },{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - } - ,items: [{ - xtype: 'modx-combo-category' - ,name: 'category' - ,fieldLabel: _('category') - ,description: MODx.expandHelp ? '' : _('tv_category_desc') - ,value: config.record.category || 0 - },{ - xtype: MODx.expandHelp ? 'box' : 'hidden' - ,html: _('tv_category_desc') - ,cls: 'desc-under' + }, { + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under' + }, + items: [{ + xtype: 'modx-combo-category', + name: 'category', + fieldLabel: _('category'), + description: MODx.expandHelp ? '' : _('tv_category_desc'), + value: config.record.category || 0 + }, { + xtype: MODx.expandHelp ? 'box' : 'hidden', + html: _('tv_category_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 3 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 1 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,name: 'description' - ,fieldLabel: _('description') - ,description: MODx.expandHelp ? '' : _('tv_description_desc') - ,grow: true - ,growMin: 30 - ,growMax: this.isSmallScreen ? 90 : 120 - ,value: config.record.description || '' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('tv_description_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 1, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + name: 'description', + fieldLabel: _('description'), + description: MODx.expandHelp ? '' : _('tv_description_desc'), + grow: true, + growMin: 30, + growMax: this.isSmallScreen ? 90 : 120, + value: config.record.description || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('tv_description_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 4 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 1 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,name: 'els' - ,fieldLabel: _('tv_elements') - ,description: MODx.expandHelp ? '' : _('tv_elements_short_desc') - ,grow: true - ,growMin: 30 - ,growMax: this.isSmallScreen ? 90 : 120 - ,value: config.record.els || '' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('tv_elements_short_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 1, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + name: 'els', + fieldLabel: _('tv_elements'), + description: MODx.expandHelp ? '' : _('tv_elements_short_desc'), + grow: true, + growMin: 30, + growMax: this.isSmallScreen ? 90 : 120, + value: config.record.els || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('tv_elements_short_desc'), + cls: 'desc-under' }] }] }] - },{ + }, { // row 5 - cls:'form-row-wrapper', + cls: 'form-row-wrapper', defaults: { layout: 'column' - } - ,items: [{ + }, + items: [{ defaults: { - layout: 'form' - ,labelSeparator: '' - ,labelAlign: 'top' - } - ,items: [{ - columnWidth: 0.5 - ,defaults: { - anchor: '100%' - ,msgTarget: 'under' - ,validationEvent: 'change' - ,validateOnBlur: false - } - ,items: [{ - xtype: 'textarea' - ,name: 'default_text' - ,fieldLabel: _('tv_default') - ,description: MODx.expandHelp ? '' : _('tv_default_desc') - ,grow: true - ,growMin: 30 - ,growMax: 60 - ,value: config.record.default_text || '' - },{ - xtype: MODx.expandHelp ? 'label' : 'hidden' - ,html: _('tv_default_desc') - ,cls: 'desc-under' + layout: 'form', + labelSeparator: '', + labelAlign: 'top' + }, + items: [{ + columnWidth: 0.5, + defaults: { + anchor: '100%', + msgTarget: 'under', + validationEvent: 'change', + validateOnBlur: false + }, + items: [{ + xtype: 'textarea', + name: 'default_text', + fieldLabel: _('tv_default'), + description: MODx.expandHelp ? '' : _('tv_default_desc'), + grow: true, + growMin: 30, + growMax: 60, + value: config.record.default_text || '' + }, { + xtype: 'box', + hidden: !MODx.expandHelp, + html: _('tv_default_desc'), + cls: 'desc-under' }] - },{ + }, { // using empty column here to allow full-width of previous column in mobile contexts - columnWidth: 0.5 - ,items: [] + columnWidth: 0.5, + items: [] }] }] + }], + keys: [{ + key: Ext.EventObject.ENTER, + shift: true, + fn: this.submit, + scope: this }] - ,keys: [{ - key: Ext.EventObject.ENTER - ,shift: true - ,fn: this.submit - ,scope: this - }] - /* - ,buttons: [{ - text: 'Test One' - },{ - text: 'Test Two' - ,cls: 'primary-button' - }] - */ }); - MODx.window.QuickCreateTV.superclass.constructor.call(this,config); + MODx.window.QuickCreateTV.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickCreateTV,MODx.Window); -Ext.reg('modx-window-quick-create-tv',MODx.window.QuickCreateTV); - -MODx.window.QuickUpdateTV = function(config) { - config = config || {}; +Ext.extend(MODx.window.QuickCreateTV, MODx.Window); +Ext.reg('modx-window-quick-create-tv', MODx.window.QuickCreateTV); - Ext.applyIf(config,{ - title: _('quick_update_tv') - ,action: 'Element/TemplateVar/Update' - ,cls: 'qce-window qce-update' - ,modxFbarButtons: 'c-s-sc' +MODx.window.QuickUpdateTV = function(config = {}) { + Ext.applyIf(config, { + title: _('quick_update_tv'), + action: 'Element/TemplateVar/Update', + cls: 'qce-window qce-update', + modxFbarButtons: 'c-s-sc', + isUpdate: true }); - MODx.window.QuickUpdateTV.superclass.constructor.call(this,config); + MODx.window.QuickUpdateTV.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.QuickUpdateTV,MODx.window.QuickCreateTV); -Ext.reg('modx-window-quick-update-tv',MODx.window.QuickUpdateTV); - +Ext.extend(MODx.window.QuickUpdateTV, MODx.window.QuickCreateTV); +Ext.reg('modx-window-quick-update-tv', MODx.window.QuickUpdateTV); -MODx.window.DuplicateContext = function(config) { - - config = config || {}; +MODx.window.DuplicateContext = function(config = {}) { Ext.Ajax.timeout = 0; - const windowId = `window-dup-context-${Ext.id()}`, - preserveAliasCmpId = `${windowId}-modx-preserve_alias`, - preserveMenuIndexCmpId = `${windowId}-modx-preserve_menuindex` + const + windowId = `window-dup-context-${Ext.id()}`, + preserveAliasCmpId = `${windowId}-modx-preserve_alias`, + preserveMenuIndexCmpId = `${windowId}-modx-preserve_menuindex` ; - - Ext.applyIf(config,{ - id: windowId - ,title: _('duplicate') - ,url: MODx.config.connector_url - ,action: 'Context/Duplicate' - ,modxPseudoModal: true - ,fields: [{ - xtype: 'statictextfield' - ,name: 'key' - ,fieldLabel: _('old_key') - ,submitValue: true - },{ - xtype: 'textfield' - ,name: 'newkey' - ,fieldLabel: _('new_key') - ,value: '' - },{ - xtype: 'checkbox' - ,name: 'preserve_resources' - ,hideLabel: true - ,boxLabel: _('preserve_resources') - ,checked: true - ,listeners: { + this.itemId = windowId; + Ext.applyIf(config, { + title: _('duplicate'), + url: MODx.config.connector_url, + action: 'Context/Duplicate', + fields: [{ + xtype: 'statictextfield', + name: 'key', + fieldLabel: _('old_key'), + submitValue: true + }, { + xtype: 'textfield', + name: 'newkey', + fieldLabel: _('new_key'), + value: '' + }, { + xtype: 'checkbox', + name: 'preserve_resources', + hideLabel: true, + boxLabel: _('preserve_resources'), + checked: true, + listeners: { check: { fn: function(cb, checked) { const form = this.fp.getForm(); @@ -1408,72 +1343,68 @@ MODx.window.DuplicateContext = function(config) { scope: this } } - },{ - xtype: 'checkbox' - ,id: preserveAliasCmpId - ,name: 'preserve_alias' - ,hideLabel: true - ,boxLabel: _('preserve_alias') - ,checked: true - },{ - xtype: 'checkbox' - ,id: preserveMenuIndexCmpId - ,name: 'preserve_menuindex' - ,hideLabel: true - ,boxLabel: _('preserve_menuindex') - ,checked: true + }, { + xtype: 'checkbox', + id: preserveAliasCmpId, + name: 'preserve_alias', + hideLabel: true, + boxLabel: _('preserve_alias'), + checked: true + }, { + xtype: 'checkbox', + id: preserveMenuIndexCmpId, + name: 'preserve_menuindex', + hideLabel: true, + boxLabel: _('preserve_menuindex'), + checked: true }] }); - MODx.window.DuplicateContext.superclass.constructor.call(this,config); + MODx.window.DuplicateContext.superclass.constructor.call(this, config); }; -Ext.extend(MODx.window.DuplicateContext,MODx.Window); -Ext.reg('modx-window-context-duplicate',MODx.window.DuplicateContext); +Ext.extend(MODx.window.DuplicateContext, MODx.Window); +Ext.reg('modx-window-context-duplicate', MODx.window.DuplicateContext); -MODx.window.Login = function(config) { - - config = config || {}; +MODx.window.Login = function(config = {}) { Ext.Ajax.timeout = 0; - const windowId = `window-login-extend-${Ext.id()}`; - - Ext.applyIf(config,{ - id: windowId - ,title: _('login') - ,url: MODx.config.connectors_url - ,action: 'Security/Login' - ,fields: [{ - html: '

'+_('session_logging_out')+'

' - ,xtype: 'modx-description' - },{ - xtype: 'textfield' - ,name: 'username' - ,fieldLabel: _('username') - },{ - xtype: 'textfield' - ,name: 'password' - ,inputType: 'password' - ,fieldLabel: _('password') - },{ - xtype: 'hidden' - ,name: 'rememberme' - ,value: 1 - }] - ,buttons: [{ - text: _('logout') - ,scope: this - ,handler: function() { - location.href = '?logout=1' + this.itemId = `window-login-extend-${Ext.id()}`; + Ext.applyIf(config, { + title: _('login'), + url: MODx.config.connectors_url, + action: 'Security/Login', + fields: [{ + html: `

${_('session_logging_out')}

`, + xtype: 'modx-description' + }, { + xtype: 'textfield', + name: 'username', + fieldLabel: _('username') + }, { + xtype: 'textfield', + name: 'password', + inputType: 'password', + fieldLabel: _('password') + }, { + xtype: 'hidden', + name: 'rememberme', + value: 1 + }], + buttons: [{ + text: _('logout'), + scope: this, + handler: function() { + window.location.href = '?logout=1'; } - },{ - text: _('login') - ,cls: 'primary-button' - ,scope: this - ,handler: this.submit + }, { + text: _('login'), + cls: 'primary-button', + scope: this, + handler: this.submit }] }); - MODx.window.Login.superclass.constructor.call(this,config); - this.on('success',this.onLogin,this); + MODx.window.Login.superclass.constructor.call(this, config); + this.on('success', this.onLogin, this); }; -Ext.extend(MODx.window.Login,MODx.Window,{ +Ext.extend(MODx.window.Login, MODx.Window, { onLogin: function(o) { var r = o.a.result; if (r.object && r.object.token) { @@ -1490,4 +1421,82 @@ Ext.extend(MODx.window.Login,MODx.Window,{ } } }); -Ext.reg('modx-window-login',MODx.window.Login); +Ext.reg('modx-window-login', MODx.window.Login); + +MODx.window.SaveProgress = function(config = {}) { + this.uniqueId = Ext.id(); + Ext.applyIf(config, { + title: _('please_wait'), + modal: true, + id: `modx-window-saveprogress-modal-${this.uniqueId}`, + modxPseudoModal: false, + width: 300, + minimizable: false, + maximizable: false, + closable: false, + collapsible: false, + draggable: false, + resizable: false, + cls: 'x-window-dlg x-window-plain', + items: [ + { + id: `modx-window-status-progress-text-${this.uniqueId}`, + xtype: 'box', + html: config.progressStartText || _('saving') + }, + this.setProgressBar() + ], + fbar: [], + tools: [], + listeners: { + show: { + fn: function() { + this.setProgressStart(); + } + } + } + }); + MODx.window.SaveProgress.superclass.constructor.call(this, config); + this.config = config; +}; +Ext.extend(MODx.window.SaveProgress, MODx.Window, { + init: function() { + this.show(); + }, + exit: function(exitStatus = 'success') { + if (exitStatus === 'success') { + this.setProgressDone(); + setTimeout(() => { + this.close(); + this.destroy(); + }, this.config.exitDelay || 150); + } else { + this.close(); + } + }, + setProgressBar: function() { + return new Ext.ProgressBar({ + id: 'modx-window-status-progressbar' + }); + }, + setProgressStart: function() { + this.progressBar = Ext.getCmp('modx-window-status-progressbar').wait({ + interval: 200, + increment: 20 + }); + }, + setProgressDone: function() { + this.progressBar.reset(); + Ext.fly(`modx-window-status-progress-text-${this.uniqueId}`).update(`${_('done')}!`); + }, + /** + * Override private onWindowResize method to avoid resize error when modal is + * destroyed; this should generally never run for this type of window anyway + */ + onWindowResize: function() { + if (!this.isDestroyed) { + this.prototype.onWindowResize(); + } + } +}); +Ext.reg('modx-window-saveprogress', MODx.window.SaveProgress); From e3a2d5a6dc695d6bfce46619677230c595b728e0 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:46:49 -0400 Subject: [PATCH 12/12] Update GetListIn.php Code QC --- .../Security/User/Setting/GetListIn.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/core/src/Revolution/Processors/Security/User/Setting/GetListIn.php b/core/src/Revolution/Processors/Security/User/Setting/GetListIn.php index 7e48f05bf95..2b88c16daa6 100644 --- a/core/src/Revolution/Processors/Security/User/Setting/GetListIn.php +++ b/core/src/Revolution/Processors/Security/User/Setting/GetListIn.php @@ -11,16 +11,13 @@ namespace MODX\Revolution\Processors\Security\User\Setting; -use MODX\Revolution\Processors\Model\GetProcessor; use MODX\Revolution\modUserSetting; /** * Gets a list of user settings given an array of keys to search for - * @param integer $user The user to grab from - * @param integer $start (optional) The record to start at. Defaults to 0. - * @param integer $limit (optional) The number of records to limit to. Defaults to 10. - * @param string $sort (optional) The column to sort by. Defaults to key. - * @param string $dir (optional) The direction of the sort. Defaults to ASC. + * + * @property int $user The user to filter by + * @property string $keys A json-formatted list of settings keys to additionally filter by * @package MODX\Revolution\Processors\Security\User\Setting */ class GetListIn extends \MODX\Revolution\Processors\System\Settings\GetList @@ -45,20 +42,11 @@ public function prepareCriteria() { $criteria = []; $criteria[] = ['user' => (int)$this->getProperty('user')]; - // $msg = "\r\n prepareCriteria, \$properties:\r\n" . print_r($this->getProperties(), true); - // $this->modx->log(\modX::LOG_LEVEL_ERROR, $msg, '', __CLASS__); if ($keys = $this->getProperty('keys', '')) { $keys = json_decode($keys); - // $this->modx->log( - // \modX::LOG_LEVEL_ERROR, - // "\r\t prepareCriteria: - // \t\t\$var1: {none} - // \t\t\$keys: " . print_r($keys, true) - // ); $criteria[] = ['key:IN' => $keys]; } return $criteria; } - }