From 2a45d393489e4e858fe8823d439149058b70a31d Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 4 Jan 2022 23:55:35 -0500 Subject: [PATCH 01/13] 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 77c9e97b32c..907b8fb40fb 100644 --- a/_build/templates/default/sass/_forms.scss +++ b/_build/templates/default/sass/_forms.scss @@ -921,7 +921,7 @@ input::-moz-focus-inner { .x-form-check-wrap, .x-fieldset-checkbox-toggle legend, - .x-fieldset legend { + .x-fieldset legend{ [type="checkbox"]{ position: absolute; @@ -1243,7 +1243,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 67a987c5d60..94eb659895d 100644 --- a/_build/templates/default/sass/index.scss +++ b/_build/templates/default/sass/index.scss @@ -2615,3 +2615,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 93a8896ba0b..50a57eb0bb0 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -572,9 +572,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 68480d95677..674556b1ad6 100644 --- a/manager/assets/modext/widgets/element/modx.panel.tv.js +++ b/manager/assets/modext/widgets/element/modx.panel.tv.js @@ -1104,7 +1104,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, // eslint-disable-next-line new-parens, no-undef 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 51cf34b5faf5bb5d206ab2fb60ea36b92de82f2c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 31 Jan 2022 00:56:29 -0500 Subject: [PATCH 02/13] Update _forms.scss --- _build/templates/default/sass/_forms.scss | 187 +++++++++++++--------- 1 file changed, 108 insertions(+), 79 deletions(-) diff --git a/_build/templates/default/sass/_forms.scss b/_build/templates/default/sass/_forms.scss index 907b8fb40fb..43b63c73d73 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; @@ -921,9 +932,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"] & { @@ -931,11 +941,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; @@ -943,9 +953,9 @@ input::-moz-focus-inner { &:before, &:after { - content: ''; + content: ""; position: absolute; - transition: all .2s ease; + transition: all 0.2s ease; font-size: inherit; } @@ -962,7 +972,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%; @@ -972,9 +982,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; @@ -988,9 +997,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; @@ -999,9 +1007,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; @@ -1016,6 +1023,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 */ @@ -1033,12 +1051,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 */ @@ -1088,13 +1105,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%; @@ -1177,7 +1194,7 @@ input::-moz-focus-inner { margin-bottom: 2px; } - input[type=text], + input[type="text"], textarea { background-color: $coreFieldBg; background-image: none; @@ -1187,7 +1204,7 @@ input::-moz-focus-inner { width: 97%; } - input[type=text] { + input[type="text"] { font-size: 13px; height: 20px !important; padding: 5px; @@ -1225,12 +1242,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 { @@ -1239,7 +1257,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 { @@ -1269,7 +1287,6 @@ input::-moz-focus-inner { .x-grid3 { .x-small-editor { - .x-form-text, .x-form-field-wrap { font: $fontSmall; @@ -1373,7 +1390,7 @@ input::-moz-focus-inner { .x-btn { padding: 1px; - transition: color .25s; + transition: color 0.25s; &.x-btn-over, &:hover, @@ -1388,7 +1405,7 @@ input::-moz-focus-inner { &.x-item-disabled { color: $buttonColor; - opacity: .4; + opacity: 0.4; } button:before { @@ -1432,7 +1449,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; @@ -1444,7 +1465,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; } @@ -1453,13 +1482,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; @@ -1501,7 +1530,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; } @@ -1548,18 +1577,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; @@ -1788,7 +1817,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, @@ -1809,6 +1838,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); } } From 9249ce4aefcdefc180c8b4bf22c0b5700890dd1c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 6 May 2022 13:20:06 -0400 Subject: [PATCH 03/13] 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 | 75 ++ .../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, 985 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 7a54d35138d..5c528fd864d 100644 --- a/_build/data/transport.core.system_settings.php +++ b/_build/data/transport.core.system_settings.php @@ -2175,5 +2175,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 43b63c73d73..65ed477a922 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 50a57eb0bb0..2975a50c64d 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -574,10 +574,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']; @@ -586,14 +592,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']; @@ -601,17 +612,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 11718d4dc07..4be845a9c8f 100644 --- a/core/lexicon/en/setting.inc.php +++ b/core/lexicon/en/setting.inc.php @@ -500,6 +500,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 00371760026..b1deea1539c 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 * ****************************************************************************/ @@ -551,6 +602,30 @@ MODx.util.Format = { .replace(new RegExp(`[${separator}]{2,}`, 'g'), separator) ; return padListItems ? formattedList.replaceAll(separator, `${separator} `) : formattedList ; + }, + + 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 674556b1ad6..d6d29b7ff4f 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 046bf498783..06e3efeea62 100644 --- a/manager/assets/modext/widgets/resource/modx.tree.resource.js +++ b/manager/assets/modext/widgets/resource/modx.tree.resource.js @@ -888,6 +888,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' } @@ -1049,6 +1051,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 30d36631f67..a87ddb984c6 100644 --- a/manager/templates/default/header.tpl +++ b/manager/templates/default/header.tpl @@ -20,11 +20,52 @@ {/if} + {$maincssjs} @@ -34,6 +75,7 @@ + From b44b425510b4bd84392ea21a24ee8df1e544dc19 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 6 May 2022 14:59:39 -0400 Subject: [PATCH 04/13] 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 2975a50c64d..332284ee67d 100644 --- a/core/lexicon/en/default.inc.php +++ b/core/lexicon/en/default.inc.php @@ -576,6 +576,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'; @@ -586,6 +588,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 05cffe91b5cb345bb5c51fc95ff07ed55f8e095b Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:06:03 -0400 Subject: [PATCH 05/13] 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 5c528fd864d..458afd5bf34 100644 --- a/_build/data/transport.core.system_settings.php +++ b/_build/data/transport.core.system_settings.php @@ -2175,55 +2175,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 332284ee67d..2755e8919dd 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'; @@ -263,6 +263,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'; @@ -626,7 +643,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 4be845a9c8f..b052e63035b 100644 --- a/core/lexicon/en/setting.inc.php +++ b/core/lexicon/en/setting.inc.php @@ -500,23 +500,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 88e6a2163798ac78fe79dc1ab6fdca70a6d51e22 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:15:25 -0400 Subject: [PATCH 06/13] 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 f7c2b788469..81fdd17d089 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 3edda0c51dbce124fca511a16994aa42b6a809dc Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:24:32 -0400 Subject: [PATCH 07/13] 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 f27ff94ec772d27c0d04128012c6bbc867ce96e1 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:28:07 -0400 Subject: [PATCH 08/13] 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 56f0d81591b..105d45189ab 100644 --- a/manager/assets/modext/widgets/modx.panel.welcome.js +++ b/manager/assets/modext/widgets/modx.panel.welcome.js @@ -229,10 +229,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'), @@ -240,17 +239,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) { @@ -275,7 +271,7 @@ Ext.extend(MODx.window.DashboardWidgetAdd, MODx.Window, { _reload: false, - getFields: function (config) { + getFields: function(config) { return [{ hideLabel: true, xtype: 'displayfield', @@ -316,35 +312,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 b6a12fab4a353d83c4c41158fadec2f9eb40db9a Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:30:54 -0400 Subject: [PATCH 09/13] 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 a87ddb984c6..3e898ca9df8 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 acd4c7e3e595ad02f14710dda6c1b0cd5812e384 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:32:32 -0400 Subject: [PATCH 10/13] 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 b1deea1539c..44df3ff477f 100644 --- a/manager/assets/modext/util/utilities.js +++ b/manager/assets/modext/util/utilities.js @@ -802,6 +802,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 fb88431782b..02fd770b2a2 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 c02646b80ac78c4bd4475ff7adb4bc7833ef7861 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:33:24 -0400 Subject: [PATCH 11/13] 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 d2ab7a17345818448011b419319d14478f50de1c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 30 Aug 2024 23:46:49 -0400 Subject: [PATCH 12/13] 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; } - } From 97a3827e9dda86c99b7c81f02b8213f21220d025 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sun, 22 Dec 2024 20:52:06 -0500 Subject: [PATCH 13/13] Update _forms.scss Solve conflict --- _build/templates/default/sass/_forms.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_build/templates/default/sass/_forms.scss b/_build/templates/default/sass/_forms.scss index 65ed477a922..fe3dc13ac83 100644 --- a/_build/templates/default/sass/_forms.scss +++ b/_build/templates/default/sass/_forms.scss @@ -915,8 +915,7 @@ input::-moz-focus-inner { } } -/* .x-form-check-wrap */ -/* Special checboxes for resources and tv configs */ +/* Switch-style checboxes for resources, tv configs, quick edit windows, etc */ #modx-chunk-tabs, #modx-plugin-tabs, #modx-resource-tabs .display-switch, @@ -924,7 +923,8 @@ input::-moz-focus-inner { #modx-template-tabs, #modx-tv-tabs .display-switch, #modx-tv-editor-tabs, -.display-switch { +#modx-window-configure-mask, +.x-window-footer .x-panel-fbar .display-switch { &.space-before { margin-top: 0.75rem;