diff --git a/manager/assets/modext/util/utilities.js b/manager/assets/modext/util/utilities.js index f4c456331cd..87cd612fa82 100644 --- a/manager/assets/modext/util/utilities.js +++ b/manager/assets/modext/util/utilities.js @@ -36,6 +36,37 @@ MODx.util.Progress = { } }; +MODx.util.UrlParams = { + get() { + return this.parse(window.location.search) + }, + set(data) { + const params = decodeURIComponent(new URLSearchParams(data).toString()) + if (params.length) { + window.history.pushState(params, '', document.location.pathname + '?' + params); + } else { + window.history.pushState('', '', document.location.pathname); + } + }, + add(key, val) { + const params = this.get() + params[key] = val + this.set(params) + }, + remove(key) { + const params = this.get() + delete params[key] + this.set(params) + }, + clear() { + this.set({}) + }, + parse(str) { + const params = new URLSearchParams(str) + return Object.fromEntries(params.entries()) + } +} + /** Adds a lock mask to an element */ MODx.LockMask = function(config) { config = config || {}; diff --git a/manager/assets/modext/widgets/core/tree/modx.tree.js b/manager/assets/modext/widgets/core/tree/modx.tree.js index 1a50c69cf27..2459e1d7b75 100644 --- a/manager/assets/modext/widgets/core/tree/modx.tree.js +++ b/manager/assets/modext/widgets/core/tree/modx.tree.js @@ -901,6 +901,19 @@ Ext.extend(MODx.tree.Tree,Ext.tree.TreePanel,{ return langs; } + ,expandTreePath(dir = '/') { + const root = this.getRootNode().getPath('text') + const path = root.replace(/\/$/, '') + '/' + dir.replace(/^\//, '') + this.expandPath(path, 'text', () => { + let node = this.getNodeById(encodeURIComponent(dir)) + if (!node) { + node = this.getRootNode() + } + node.select() + this.cm.activeNode = node + }) + } + }); Ext.reg('modx-tree',MODx.tree.Tree); diff --git a/manager/assets/modext/widgets/media/modx.browser.js b/manager/assets/modext/widgets/media/modx.browser.js index 5a14243515f..cae4653e9b8 100644 --- a/manager/assets/modext/widgets/media/modx.browser.js +++ b/manager/assets/modext/widgets/media/modx.browser.js @@ -522,10 +522,11 @@ MODx.browser.Window = function(config) { this.view.run(); } ,scope: this - ,source: config.source || MODx.config.default_media_source + ,source: config.source || MODx.util.UrlParams.get().source || MODx.config.default_media_source + ,originalSource: config.source || MODx.config.default_media_source ,hideFiles: config.hideFiles || MODx.config.modx_browser_tree_hide_files ,hideTooltips: config.hideTooltips || MODx.config.modx_browser_tree_hide_tooltips || true // by default do not request image preview tooltips in the media browser - ,openTo: config.openTo || '' + ,openTo: config.openTo || MODx.util.UrlParams.get().dir || '' ,ident: this.ident ,rootIconCls: MODx.config.mgr_source_icon ,rootId: config.rootId || '/' @@ -566,6 +567,7 @@ MODx.browser.Window = function(config) { this.view.baseParams.source = s; this.view.dir = '/'; this.view.run(); + MODx.util.UrlParams.set({...MODx.util.UrlParams.get(), source: s, dir: '/'}) } ,scope: this } @@ -594,6 +596,15 @@ MODx.browser.Window = function(config) { } ,scope: this } + ,'load': { + fn: function() { + const dir = MODx.util.UrlParams.get().dir + if (dir) { + this.tree.expandTreePath(dir) + } + } + ,scope: this + } } }); @@ -604,16 +615,21 @@ MODx.browser.Window = function(config) { fn: this.onSelect ,scope: this } - ,source: config.source || MODx.config.default_media_source + ,source: config.source || MODx.util.UrlParams.get().source || MODx.config.default_media_source ,allowedFileTypes: config.allowedFileTypes || '' ,wctx: config.wctx || 'web' - ,openTo: config.openTo || '' + ,openTo: config.openTo || MODx.util.UrlParams.get().dir || '' ,multiSelect: config.multiSelect || false ,ident: this.ident ,id: this.ident+'-view' ,tree: this.tree }); + // Add event to reload on History change + window.onpopstate = (e) => { + MODx.browser.onPopState(e, this) + } + Ext.applyIf(config,{ title: _('modx_browser')+' ('+(MODx.ctx ? MODx.ctx : 'web')+')' ,cls: 'modx-browser modx-browser-window' @@ -673,8 +689,14 @@ MODx.browser.Window = function(config) { MODx.browser.Window.superclass.constructor.call(this,config); this.config = config; this.addEvents({ - 'select': true + 'select': true, + 'hide': true, }); + + this.on('hide', () => { + MODx.util.UrlParams.remove('source') + MODx.util.UrlParams.remove('dir') + }) }; Ext.extend(MODx.browser.Window,Ext.Window,{ returnEl: null @@ -696,6 +718,8 @@ Ext.extend(MODx.browser.Window,Ext.Window,{ */ ,load: function(dir) { dir = dir || (Ext.isEmpty(this.config.openTo) ? '' : this.config.openTo); + MODx.util.UrlParams.set({...MODx.util.UrlParams.get(), dir: decodeURIComponent(dir)}) + this.view.run({ dir: dir ,source: this.config.source @@ -916,10 +940,11 @@ MODx.Media = function(config) { this.view.run(); } ,scope: this - ,source: config.source || MODx.config.default_media_source + ,source: config.source || MODx.util.UrlParams.get().source || MODx.config.default_media_source + ,originalSource: config.source || MODx.config.default_media_source ,hideFiles: config.hideFiles || MODx.config.modx_browser_tree_hide_files ,hideTooltips: config.hideTooltips || MODx.config.modx_browser_tree_hide_tooltips || true // by default do not request image preview tooltips in the media browser - ,openTo: config.openTo || '' + ,openTo: config.openTo || MODx.util.UrlParams.get().dir || '' ,ident: this.ident ,rootIconCls: MODx.config.mgr_source_icon ,rootId: config.rootId || '/' @@ -960,6 +985,7 @@ MODx.Media = function(config) { this.view.baseParams.source = s; this.view.dir = '/'; this.view.run(); + MODx.util.UrlParams.set({...MODx.util.UrlParams.get(), source: s, dir: '/'}) } ,scope: this } @@ -988,6 +1014,15 @@ MODx.Media = function(config) { } ,scope: this } + ,'load': { + fn: function() { + const dir = MODx.util.UrlParams.get().dir + if (dir) { + this.tree.expandTreePath(dir) + } + } + ,scope: this + } } }); @@ -998,15 +1033,21 @@ MODx.Media = function(config) { fn: this.onSelect ,scope: this } - ,source: config.source || MODx.config.default_media_source + ,source: config.source || MODx.util.UrlParams.get().source || MODx.config.default_media_source + ,originalSource: config.source || MODx.config.default_media_source ,allowedFileTypes: config.allowedFileTypes || '' ,wctx: config.wctx || 'web' - ,openTo: config.openTo || '' + ,openTo: config.openTo || MODx.util.UrlParams.get().dir || '' ,ident: this.ident ,id: this.ident+'-view' ,tree: this.tree }); + // Add event to reload on History change + window.onpopstate = (e) => { + MODx.browser.onPopState(e, this) + } + Ext.applyIf(config, { cls: 'modx-browser modx-browser-panel' ,layout: 'border' @@ -1065,6 +1106,8 @@ Ext.extend(MODx.Media, Ext.Container, { */ ,load: function(dir) { dir = dir || (Ext.isEmpty(this.config.openTo) ? '' : this.config.openTo); + MODx.util.UrlParams.set({...MODx.util.UrlParams.get(), dir: decodeURIComponent(dir)}) + this.view.run({ dir: dir ,source: this.config.source @@ -1648,3 +1691,41 @@ Ext.extend(MODx.browser.RTE,Ext.Viewport,{ } }); Ext.reg('modx-browser-rte',MODx.browser.RTE); + +MODx.browser.onPopState = function ({state}, {tree, view}) { + const params = MODx.util.UrlParams.parse(state) + const defaultSource = view.config.originalSource + const source = params.source || defaultSource + + if (tree.sourceCombo && !tree.config.hideSourceCombo && source !== tree.sourceCombo.getValue()) { + tree.config.source = source + tree.baseParams.source = source + tree.dir = params.dir || '/' + tree.sourceCombo.getStore().load({ + scope: tree, + callback() { + tree.sourceCombo.setValue(source) + tree.refresh() + const root = tree.getRootNode(); + if (root) { + root.setText(tree.sourceCombo.getRawValue()); + } + view.run({ + dir: params.dir || '/', + source, + allowedFileTypes: view.config.allowedFileTypes || '', + wctx: view.config.wctx || 'web', + }) + tree.expandTreePath(params.dir || '/') + }, + }) + } else { + view.run({ + dir: params.dir || '/', + source, + allowedFileTypes: view.config.allowedFileTypes || '', + wctx: view.config.wctx || 'web', + }) + tree.expandTreePath(params.dir || '/') + } +}