From 5789551b09f04903cbaa1d7bc791b0475968458b Mon Sep 17 00:00:00 2001 From: Andreas Fernandez <a.fernandez@scripting-base.de> Date: Fri, 29 Sep 2017 22:16:33 +0200 Subject: [PATCH] [TASK] Split `Storage` module The `Storage` RequireJS module is split into `Storage/Client` and `Storage/Persistent`. The `Storage` module itself is marked as deprecated and any occurrence is migrated to use the new modules. Resolves: #82603 Releases: master Change-Id: Ie4bbb2ac3c1801b15531f0b71e35507c8ed03f06 Reviewed-on: https://review.typo3.org/54240 Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Frank Naegler <frank.naegler@typo3.org> Tested-by: Frank Naegler <frank.naegler@typo3.org> Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- Build/types/TYPO3/index.d.ts | 1 + .../Classes/Controller/BackendController.php | 155 ++++----- .../Resources/Private/TypeScript/Storage.ts | 68 ++++ .../Private/TypeScript/Storage/Client.ts | 68 ++++ .../Private/TypeScript/Storage/Persistent.ts | 220 +++++++++++++ .../Public/JavaScript/DateTimePicker.js | 4 +- .../Resources/Public/JavaScript/ModuleMenu.js | 16 +- .../Public/JavaScript/PageActions.js | 4 +- .../Resources/Public/JavaScript/Storage.js | 302 +++--------------- .../Public/JavaScript/Storage/Client.js | 69 ++++ .../Public/JavaScript/Storage/Persistent.js | 209 ++++++++++++ .../Resources/Public/JavaScript/Tabs.js | 6 +- .../Toolbar/SystemInformationMenu.js | 10 +- ...precation-82603-DeprecateStorageModule.rst | 42 +++ .../Resources/Public/JavaScript/Recordlist.js | 8 +- .../Resources/Public/JavaScript/Main.js | 6 +- 16 files changed, 828 insertions(+), 360 deletions(-) create mode 100644 typo3/sysext/backend/Resources/Private/TypeScript/Storage.ts create mode 100644 typo3/sysext/backend/Resources/Private/TypeScript/Storage/Client.ts create mode 100644 typo3/sysext/backend/Resources/Private/TypeScript/Storage/Persistent.ts create mode 100644 typo3/sysext/backend/Resources/Public/JavaScript/Storage/Client.js create mode 100644 typo3/sysext/backend/Resources/Public/JavaScript/Storage/Persistent.js create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-82603-DeprecateStorageModule.rst diff --git a/Build/types/TYPO3/index.d.ts b/Build/types/TYPO3/index.d.ts index 48eb7549ca29..09391b1b4890 100644 --- a/Build/types/TYPO3/index.d.ts +++ b/Build/types/TYPO3/index.d.ts @@ -6,6 +6,7 @@ */ declare namespace TYPO3 { export let Popover: any; + export let Storage: any; export const lang: any; export const settings: any; export namespace CMS { diff --git a/typo3/sysext/backend/Classes/Controller/BackendController.php b/typo3/sysext/backend/Classes/Controller/BackendController.php index e4d689f2e6b8..411eb4db1696 100644 --- a/typo3/sysext/backend/Classes/Controller/BackendController.php +++ b/typo3/sysext/backend/Classes/Controller/BackendController.php @@ -155,9 +155,9 @@ class BackendController $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu'); // load the storage API and fill the UC into the PersistentStorage, so no additional AJAX call is needed - $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage', 'function(Storage) { - Storage.Persistent.load(' . json_encode($this->getBackendUser()->uc) . '); - }'); + $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage/Persistent', 'function(PersistentStorage) { + PersistentStorage.load(' . json_encode($this->getBackendUser()->uc) . '); + }'); // load debug console $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DebugConsole'); @@ -255,86 +255,87 @@ class BackendController // @todo: remove this when ExtJS is removed $states = $this->getBackendUser()->uc['BackendComponents']['States']; $this->pageRenderer->addExtOnReadyCode(' - var TYPO3ExtJSStateProviderBridge = function() {}; - Ext.extend(TYPO3ExtJSStateProviderBridge, Ext.state.Provider, { - state: {}, - queue: [], - dirty: false, - prefix: "BackendComponents.States.", - initState: function(state) { - if (Ext.isArray(state)) { - Ext.each(state, function(item) { - this.state[item.name] = item.value; - }, this); - } else if (Ext.isObject(state)) { - Ext.iterate(state, function(key, value) { - this.state[key] = value; - }, this); - } else { - this.state = {}; - } - var me = this; - window.setInterval(function() { - me.submitState(me) - }, 750); - }, - get: function(name, defaultValue) { - return TYPO3.Storage.Persistent.isset(this.prefix + name) ? TYPO3.Storage.Persistent.get(this.prefix + name) : defaultValue; - }, - clear: function(name) { - TYPO3.Storage.Persistent.unset(this.prefix + name); - }, - set: function(name, value) { - if (!name) { - return; - } - this.queueChange(name, value); - }, - queueChange: function(name, value) { - var o = {}; - var i; - var found = false; - - var lastValue = this.state[name]; - for (i = 0; i < this.queue.length; i++) { - if (this.queue[i].name === name) { - lastValue = this.queue[i].value; + require([\'TYPO3/CMS/Backend/Storage/Persistent\'], function(PersistentStorage) { + var TYPO3ExtJSStateProviderBridge = function() {}; + Ext.extend(TYPO3ExtJSStateProviderBridge, Ext.state.Provider, { + state: {}, + queue: [], + dirty: false, + prefix: "BackendComponents.States.", + initState: function(state) { + if (Ext.isArray(state)) { + Ext.each(state, function(item) { + this.state[item.name] = item.value; + }, this); + } else if (Ext.isObject(state)) { + Ext.iterate(state, function(key, value) { + this.state[key] = value; + }, this); + } else { + this.state = {}; } - } - var changed = undefined === lastValue || lastValue !== value; - - if (changed) { - o.name = name; - o.value = value; + var me = this; + window.setInterval(function() { + me.submitState(me) + }, 750); + }, + get: function(name, defaultValue) { + return PersistentStorage.isset(this.prefix + name) ? PersistentStorage.get(this.prefix + name) : defaultValue; + }, + clear: function(name) { + PersistentStorage.unset(this.prefix + name); + }, + set: function(name, value) { + if (!name) { + return; + } + this.queueChange(name, value); + }, + queueChange: function(name, value) { + var o = {}; + var i; + var found = false; + + var lastValue = this.state[name]; for (i = 0; i < this.queue.length; i++) { - if (this.queue[i].name === o.name) { - this.queue[i] = o; - found = true; + if (this.queue[i].name === name) { + lastValue = this.queue[i].value; } } - if (false === found) { - this.queue.push(o); - } - this.dirty = true; - } - }, - submitState: function(context) { - if (!context.dirty) { - return; - } - for (var i = 0; i < context.queue.length; ++i) { - TYPO3.Storage.Persistent.set(context.prefix + context.queue[i].name, context.queue[i].value).done(function() { - if (!context.dirty) { - context.queue = []; + var changed = undefined === lastValue || lastValue !== value; + + if (changed) { + o.name = name; + o.value = value; + for (i = 0; i < this.queue.length; i++) { + if (this.queue[i].name === o.name) { + this.queue[i] = o; + found = true; + } } - }); + if (false === found) { + this.queue.push(o); + } + this.dirty = true; + } + }, + submitState: function(context) { + if (!context.dirty) { + return; + } + for (var i = 0; i < context.queue.length; ++i) { + PersistentStorage.set(context.prefix + context.queue[i].name, context.queue[i].value).done(function() { + if (!context.dirty) { + context.queue = []; + } + }); + } + context.dirty = false; } - context.dirty = false; - } - }); - Ext.state.Manager.setProvider(new TYPO3ExtJSStateProviderBridge()); - Ext.state.Manager.getProvider().initState(' . (!empty($states) ? json_encode($states) : []) . '); - '); + }); + Ext.state.Manager.setProvider(new TYPO3ExtJSStateProviderBridge()); + Ext.state.Manager.getProvider().initState(' . (!empty($states) ? json_encode($states) : []) . '); + });'); // Set document title: $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [TYPO3 CMS ' . TYPO3_version . ']' : 'TYPO3 CMS ' . TYPO3_version; // Renders the module page diff --git a/typo3/sysext/backend/Resources/Private/TypeScript/Storage.ts b/typo3/sysext/backend/Resources/Private/TypeScript/Storage.ts new file mode 100644 index 000000000000..ae329c0ea99d --- /dev/null +++ b/typo3/sysext/backend/Resources/Private/TypeScript/Storage.ts @@ -0,0 +1,68 @@ +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +import Client = require('./Storage/Client'); +import Persistent = require('./Storage/Persistent'); + +/** + * Module: TYPO3/CMS/Backend/Storage + * Adds a public API for the browsers' localStorage called + * TYPO3.Storage.Client and the Backend Users "uc", + * available via TYPO3.Storage.Persistent + * @exports TYPO3/CMS/Backend/Storage + * @deprecated + */ +class Storage { + public Client: any; + public Persistent: any; + + constructor() { + if (console) { + console.warn( + 'TYPO3/CMS/Backend/Storage and TYPO3.Storage are deprecated since TYPO3 v9 and will be removed in TYPO3 v10.', + ); + } + this.Client = Client; + this.Persistent = Persistent; + } +} + +let storageObject; +try { + // fetch from opening window + if (window.opener && window.opener.TYPO3 && window.opener.TYPO3.Storage) { + storageObject = window.opener.TYPO3.Storage; + } + + // fetch from parent + if (parent && parent.window.TYPO3 && parent.window.TYPO3.Storage) { + storageObject = parent.window.TYPO3.Storage; + } + + // fetch object from outer frame + if (top && top.TYPO3.Storage) { + storageObject = top.TYPO3.Storage; + } +} catch (e) { + // This only happens if the opener, parent or top is some other url (eg a local file) + // which loaded the current window. Then the browser's cross domain policy jumps in + // and raises an exception. + // For this case we are safe and we can create our global object below. +} + +if (!storageObject) { + storageObject = new Storage(); +} + +TYPO3.Storage = storageObject; +export = storageObject; diff --git a/typo3/sysext/backend/Resources/Private/TypeScript/Storage/Client.ts b/typo3/sysext/backend/Resources/Private/TypeScript/Storage/Client.ts new file mode 100644 index 000000000000..15c34190ec3c --- /dev/null +++ b/typo3/sysext/backend/Resources/Private/TypeScript/Storage/Client.ts @@ -0,0 +1,68 @@ +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Module: TYPO3/CMS/Backend/Storage/Client + * Wrapper for localStorage + * @exports TYPO3/CMS/Backend/Storage/Client + */ +class Client { + /** + * Simple localStorage wrapper, to get value from localStorage + * @param {string} key + * @returns {string} + */ + public get = (key: string): string => { + return localStorage.getItem('t3-' + key); + } + + /** + * Simple localStorage wrapper, to set value from localStorage + * + * @param {string} key + * @param {string} value + * @returns {string} + */ + public set = (key: string, value: string): void => { + localStorage.setItem('t3-' + key, value); + } + + /** + * Simple localStorage wrapper, to unset value from localStorage + * + * @param {string} key + */ + public unset = (key: string): void => { + localStorage.removeItem('t3-' + key); + } + + /** + * Simple localStorage wrapper, to clear localStorage + */ + public clear = (): void => { + localStorage.clear(); + } + + /** + * Checks if a key was set before, useful to not do all the undefined checks all the time + * + * @param {string} key + * @returns {boolean} + */ + public isset = (key: string): boolean => { + const value = this.get(key); + return (typeof value !== 'undefined' && value !== null); + } +} + +export = new Client(); diff --git a/typo3/sysext/backend/Resources/Private/TypeScript/Storage/Persistent.ts b/typo3/sysext/backend/Resources/Private/TypeScript/Storage/Persistent.ts new file mode 100644 index 000000000000..88a0c71a2a6f --- /dev/null +++ b/typo3/sysext/backend/Resources/Private/TypeScript/Storage/Persistent.ts @@ -0,0 +1,220 @@ +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +import $ = require('jquery'); + +/** + * Module: TYPO3/CMS/Backend/Storage/Persistent + * Wrapper for persistent storage in UC + * @exports TYPO3/CMS/Backend/Storage/Persistent + */ +class Persistent { + private data: any = false; + + /** + * Persistent storage, stores everything on the server via AJAX, does a greedy load on read + * common functions get/set/clear + * + * @param {String} key + * @returns {*} + */ + public get = (key: string): any => { + const me = this; + + if (this.data === false) { + let value; + this.loadFromServer().done(() => { + value = me.getRecursiveDataByDeepKey(me.data, key.split('.')); + }); + return value; + } + + return this.getRecursiveDataByDeepKey(this.data, key.split('.')); + } + + /** + * Store data persistent on server + * + * @param {String} key + * @param {String} value + * @returns {$} + */ + public set = (key: string, value: string): any => { + if (this.data !== false) { + this.data = this.setRecursiveDataByDeepKey(this.data, key.split('.'), value); + } + return this.storeOnServer(key, value); + } + + /** + * @param {string} key + * @param {string} value + * @returns {$} + */ + public addToList = (key: string, value: string): any => { + const me = this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'addToList', + key, + value, + }, + method: 'post', + }).done((data: any): any => { + me.data = data; + }); + } + + /** + * @param {string} key + * @param {string} value + * @returns {$} + */ + public removeFromList = (key: string, value: string): any => { + const me = this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'removeFromList', + key, + value, + }, + method: 'post', + }).done((data: any): any => { + me.data = data; + }); + } + + public unset = (key: string): any => { + const me = this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'unset', + key, + }, + method: 'post', + }).done((data: any): any => { + me.data = data; + }); + } + + /** + * Clears the UC + */ + public clear = (): any => { + $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'clear', + }, + }); + this.data = false; + } + + /** + * Checks if a key was set before, useful to not do all the undefined checks all the time + * + * @param {string} key + * @returns {boolean} + */ + public isset = (key: string): boolean => { + const value = this.get(key); + return (typeof value !== 'undefined' && value !== null); + } + + /** + * Loads the data from outside, only used for the initial call from BackendController + * + * @param {String} data + */ + public load = (data: any): any => { + this.data = data; + } + + /** + * Loads all data from the server + * + * @returns {$} + */ + private loadFromServer = (): any => { + const me = this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + async: false, + data: { + action: 'getAll', + }, + }).done((data) => { + me.data = data; + }); + } + + /** + * Stores data on the server, and gets the updated data on return + * to always be up-to-date inside the browser + * + * @param {string} key + * @param {string} value + * @returns {*} + */ + private storeOnServer = (key: string, value: string): any => { + const me = this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'set', + key, + value, + }, + method: 'post', + }).done((data): any => { + me.data = data; + }); + } + + /** + * Helper function used to set a value which could have been a flat object key data["my.foo.bar"] to + * data[my][foo][bar] is called recursively by itself + * + * @param {Object} data the data to be uased as base + * @param {String} keyParts the keyParts for the subtree + * @returns {Object} + */ + private getRecursiveDataByDeepKey = (data: any, keyParts: any[]): any => { + if (keyParts.length === 1) { + return (data || {})[keyParts[0]]; + } + + const firstKey = keyParts.shift(); + return this.getRecursiveDataByDeepKey(data[firstKey] || {}, keyParts); + } + + /** + * helper function used to set a value which could have been a flat object key data["my.foo.bar"] to + * data[my][foo][bar] + * is called recursively by itself + * + * @param data + * @param {any[]} keyParts + * @param {string} value + * @returns {any[]} + */ + private setRecursiveDataByDeepKey = (data: any, keyParts: any[], value: string): any[] => { + if (keyParts.length === 1) { + data = data || {}; + data[keyParts[0]] = value; + } else { + const firstKey = keyParts.shift(); + data[firstKey] = this.setRecursiveDataByDeepKey(data[firstKey] || {}, keyParts, value); + } + return data; + } +} + +export = new Persistent(); diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/DateTimePicker.js b/typo3/sysext/backend/Resources/Public/JavaScript/DateTimePicker.js index 8c396726f5a4..ec268d921509 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/DateTimePicker.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/DateTimePicker.js @@ -42,8 +42,8 @@ define(['jquery'], function($) { }); if ($dateTimeFields.length > 0) { - require(['moment', 'TYPO3/CMS/Backend/Storage', 'twbs/bootstrap-datetimepicker'], function(moment, Storage) { - var userLocale = Storage.Persistent.get('lang'); + require(['moment', 'TYPO3/CMS/Backend/Storage/Persistent', 'twbs/bootstrap-datetimepicker'], function(moment, PersistentStorage) { + var userLocale = PersistentStorage.get('lang'); var setLocale = userLocale ? moment.locale(userLocale) : false; // initialize the datepicker on each selected element diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/ModuleMenu.js b/typo3/sysext/backend/Resources/Public/JavaScript/ModuleMenu.js index 9622d2457980..24c798ad57a3 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/ModuleMenu.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/ModuleMenu.js @@ -18,13 +18,13 @@ require( [ 'jquery', - 'TYPO3/CMS/Backend/Storage', + 'TYPO3/CMS/Backend/Storage/Persistent', 'TYPO3/CMS/Backend/Icons', 'TYPO3/CMS/Backend/Viewport', 'TYPO3/CMS/Backend/Event/ClientRequest', 'TYPO3/CMS/Backend/Event/TriggerRequest' ], - function ($, Storage, Icons, Viewport, ClientRequest, TriggerRequest) { + function ($, PersistentStorage, Icons, Viewport, ClientRequest, TriggerRequest) { if (typeof TYPO3.ModuleMenu !== 'undefined') { return TYPO3.ModuleMenu.App; } @@ -61,7 +61,7 @@ require( deferred.then(function() { // check if module menu should be collapsed or not - var state = Storage.Persistent.get('BackendComponents.States.typo3-module-menu'); + var state = PersistentStorage.get('BackendComponents.States.typo3-module-menu'); if (state && state.collapsed) { TYPO3.ModuleMenu.App.toggleMenu(state.collapsed === 'true'); } @@ -157,7 +157,7 @@ require( } // Persist collapsed state in the UC of the current user - Storage.Persistent.set( + PersistentStorage.set( 'BackendComponents.States.typo3-module-menu', { collapsed: collapse @@ -394,8 +394,8 @@ require( * @returns {*} */ getCollapsedMainMenuItems: function () { - if (TYPO3.Storage.Persistent.isset('modulemenu')) { - return JSON.parse(TYPO3.Storage.Persistent.get('modulemenu')); + if (PersistentStorage.isset('modulemenu')) { + return JSON.parse(PersistentStorage.get('modulemenu')); } else { return {}; } @@ -408,7 +408,7 @@ require( addCollapsedMainMenuItem: function (item) { var existingItems = this.getCollapsedMainMenuItems(); existingItems[item] = true; - TYPO3.Storage.Persistent.set('modulemenu', JSON.stringify(existingItems)); + PersistentStorage.set('modulemenu', JSON.stringify(existingItems)); }, /** @@ -418,7 +418,7 @@ require( removeCollapseMainMenuItem: function (item) { var existingItems = this.getCollapsedMainMenuItems(); delete existingItems[item]; - TYPO3.Storage.Persistent.set('modulemenu', JSON.stringify(existingItems)); + PersistentStorage.set('modulemenu', JSON.stringify(existingItems)); } }; diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/PageActions.js b/typo3/sysext/backend/Resources/Public/JavaScript/PageActions.js index b2572a941b1c..7e4999a51a58 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/PageActions.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/PageActions.js @@ -15,7 +15,7 @@ * Module: TYPO3/CMS/Backend/PageActions * JavaScript implementations for page actions */ -define(['jquery', 'TYPO3/CMS/Backend/Storage'], function($, Storage) { +define(['jquery', 'TYPO3/CMS/Backend/Storage/Persistent'], function($, PersistentStorage) { 'use strict'; /** @@ -99,7 +99,7 @@ define(['jquery', 'TYPO3/CMS/Backend/Storage'], function($, Storage) { $hiddenElements.slideUp(); } - Storage.Persistent.set('moduleData.web_layout.tt_content_showHidden', $me.prop('checked') ? 1 : 0).done(function() { + PersistentStorage.set('moduleData.web_layout.tt_content_showHidden', $me.prop('checked') ? 1 : 0).done(function() { $spinner.remove(); $me.show(); }); diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Storage.js b/typo3/sysext/backend/Resources/Public/JavaScript/Storage.js index 8ea97692f1ad..bf481179129b 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/Storage.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/Storage.js @@ -10,260 +10,50 @@ * * The TYPO3 project - inspiring people to share! */ - -/** - * Module: TYPO3/CMS/Backend/Storage - * Adds a public API for the browsers' localStorage called - * TYPO3.Storage.Client and the Backend Users "uc", - * available via TYPO3.Storage.Persistent - */ -define(['jquery'], function ($) { - 'use strict'; - - try { - // fetch from opening window - if (window.opener && window.opener.TYPO3 && window.opener.TYPO3.Storage) { - return window.opener.TYPO3.Storage; - } - - // fetch from parent - if (parent && parent.window.TYPO3 && parent.window.TYPO3.Storage) { - return parent.window.TYPO3.Storage; - } - - // fetch object from outer frame - if (top && top.TYPO3.Storage) { - return top.TYPO3.Storage; - } - } catch (e) { - // This only happens if the opener, parent or top is some other url (eg a local file) - // which loaded the current window. Then the browser's cross domain policy jumps in - // and raises an exception. - // For this case we are safe and we can create our global object below. - } - - // we didn't find an existing object, so create it - /** - * - * @type {{Client: {}, Persistent: {_data: boolean}}} - * @exports TYPO3/CMS/Backend/Storage - */ - var Storage = { - Client: {}, - Persistent: { - _data: false - } - }; - - /** - * Simple localStorage wrapper, to get value from localStorage - * @param {String} key - * @return {String} - */ - Storage.Client.get = function(key) { - return localStorage.getItem('t3-' + key); - }; - - /** - * Simple localStorage wrapper, to set value from localStorage - * @param {String} key - * @param {String} value - */ - Storage.Client.set = function(key, value) { - localStorage.setItem('t3-' + key, value); - }; - - /** - * Simple localStorage wrapper, to unset value from localStorage - * @param {String} key - */ - Storage.Client.unset = function(key) { - localStorage.removeItem('t3-' + key); - }; - - /** - * Simple localStorage wrapper, to clear localStorage - */ - Storage.Client.clear = function() { - localStorage.clear(); - }; - - /** - * Checks if a key was set before, useful to not do all the undefined checks all the time - * - * @param {String} key - * @returns {Boolean} - */ - Storage.Client.isset = function(key) { - var value = this.get(key); - return (typeof value !== 'undefined' && value !== null); - }; - - /** - * Persistent storage, stores everything on the server via AJAX, does a greedy load on read - * common functions get/set/clear - * - * @param {String} key - * @returns {*} - */ - Storage.Persistent.get = function(key) { - if (this._data === false) { - var value; - this._loadFromServer().done(function() { - value = Storage.Persistent._getRecursiveDataByDeepKey(Storage.Persistent._data, key.split('.')); - }); - return value; - } else { - return this._getRecursiveDataByDeepKey(this._data, key.split('.')); - } - }; - - /** - * Store data persistent on server - * - * @param {String} key - * @param {String} value - * @returns {jQuery} - */ - Storage.Persistent.set = function(key, value) { - if (this._data !== false) { - this._data = this._setRecursiveDataByDeepKey(this._data, key.split('.'), value); - } - return this._storeOnServer(key, value); - }; - - /** - * - * @param {String} key - * @param {String} value - * @returns {*} - */ - Storage.Persistent.addToList = function(key, value) { - return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {method: 'post', data: {'action': 'addToList', key: key, value: value}}).done(function(data) { - Storage.Persistent._data = data; - }); - }; - - /** - * - * @param {String} key - * @param {String} value - * @returns {*} - */ - Storage.Persistent.removeFromList = function(key, value) { - return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {method: 'post', data: {'action': 'removeFromList', key: key, value: value}}).done(function(data) { - Storage.Persistent._data = data; - }); - }; - - /** - * - * @param {String} key - * @returns {*} - */ - Storage.Persistent.unset = function(key) { - return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {method: 'post', data: {'action': 'unset', key: key}}).done(function(data) { - Storage.Persistent._data = data; - }); - }; - - /** - * - */ - Storage.Persistent.clear = function() { - $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'clear'}}); - this._data = false; - }; - - /** - * Checks if a key was set before, useful to not do all the undefined checks all the time - * - * @param {String} key - * @returns {Boolean} - */ - Storage.Persistent.isset = function(key) { - var value = this.get(key); - return (typeof value !== 'undefined' && typeof value !== 'null' && value != 'undefined'); - }; - - /** - * Loads the data from outside, only used for the initial call from BackendController - * - * @param {String} data - */ - Storage.Persistent.load = function(data) { - this._data = data; - }; - - /** - * Loads all data from the server - * - * @returns {*} - * @private - */ - Storage.Persistent._loadFromServer = function() { - return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'getAll'}, async: false}).done(function(data) { - Storage.Persistent._data = data; - }); - }; - - /** - * Stores data on the server, and gets the updated data on return - * to always be up-to-date inside the browser - * - * @param {String} key - * @param {String} value - * @returns {*} - * @private - */ - Storage.Persistent._storeOnServer = function(key, value) { - return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {method: 'post', data: {'action': 'set', key: key, value: value}}).done(function(data) { - Storage.Persistent._data = data; - }); - }; - - /** - * helper function used to set a value which could have been a flat object key data["my.foo.bar"] to - * data[my][foo][bar] - * is called recursively by itself - * - * @param {Object} data the data to be uased as base - * @param {String} keyParts the keyParts for the subtree - * @param {String} value the value to be set - * @returns {Object} the data object - * @private - */ - Storage.Persistent._setRecursiveDataByDeepKey = function(data, keyParts, value) { - if (keyParts.length === 1) { - data = data || {}; - data[keyParts[0]] = value; - } else { - var firstKey = keyParts.shift(); - data[firstKey] = this._setRecursiveDataByDeepKey(data[firstKey] || {}, keyParts, value); - } - return data; - }; - - /** - * Helper function used to set a value which could have been a flat object key data["my.foo.bar"] to - * data[my][foo][bar] is called recursively by itself - * - * @param {Object} data the data to be uased as base - * @param {String} keyParts the keyParts for the subtree - * @returns {Object} - * @private - */ - Storage.Persistent._getRecursiveDataByDeepKey = function(data, keyParts) { - if (keyParts.length === 1) { - return (data || {})[keyParts[0]]; - } else { - var firstKey = keyParts.shift(); - return this._getRecursiveDataByDeepKey(data[firstKey] || {}, keyParts); - } - }; - - // attach to global frame - TYPO3.Storage = Storage; - - return Storage; +define(["require", "exports", "./Storage/Client", "./Storage/Persistent"], function (require, exports, Client, Persistent) { + "use strict"; + /** + * Module: TYPO3/CMS/Backend/Storage + * Adds a public API for the browsers' localStorage called + * TYPO3.Storage.Client and the Backend Users "uc", + * available via TYPO3.Storage.Persistent + * @exports TYPO3/CMS/Backend/Storage + * @deprecated + */ + var Storage = (function () { + function Storage() { + if (console) { + console.warn('TYPO3/CMS/Backend/Storage and TYPO3.Storage are deprecated since TYPO3 v9 and will be removed in TYPO3 v10.'); + } + this.Client = Client; + this.Persistent = Persistent; + } + return Storage; + }()); + var storageObject; + try { + // fetch from opening window + if (window.opener && window.opener.TYPO3 && window.opener.TYPO3.Storage) { + storageObject = window.opener.TYPO3.Storage; + } + // fetch from parent + if (parent && parent.window.TYPO3 && parent.window.TYPO3.Storage) { + storageObject = parent.window.TYPO3.Storage; + } + // fetch object from outer frame + if (top && top.TYPO3.Storage) { + storageObject = top.TYPO3.Storage; + } + } + catch (e) { + // This only happens if the opener, parent or top is some other url (eg a local file) + // which loaded the current window. Then the browser's cross domain policy jumps in + // and raises an exception. + // For this case we are safe and we can create our global object below. + } + if (!storageObject) { + storageObject = new Storage(); + } + TYPO3.Storage = storageObject; + return storageObject; }); diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Storage/Client.js b/typo3/sysext/backend/Resources/Public/JavaScript/Storage/Client.js new file mode 100644 index 000000000000..0ed5c53b39f6 --- /dev/null +++ b/typo3/sysext/backend/Resources/Public/JavaScript/Storage/Client.js @@ -0,0 +1,69 @@ +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +define(["require", "exports"], function (require, exports) { + "use strict"; + /** + * Module: TYPO3/CMS/Backend/Storage/Client + * Wrapper for localStorage + * @exports TYPO3/CMS/Backend/Storage/Client + */ + var Client = (function () { + function Client() { + var _this = this; + /** + * Simple localStorage wrapper, to get value from localStorage + * @param {string} key + * @returns {string} + */ + this.get = function (key) { + return localStorage.getItem('t3-' + key); + }; + /** + * Simple localStorage wrapper, to set value from localStorage + * + * @param {string} key + * @param {string} value + * @returns {string} + */ + this.set = function (key, value) { + localStorage.setItem('t3-' + key, value); + }; + /** + * Simple localStorage wrapper, to unset value from localStorage + * + * @param {string} key + */ + this.unset = function (key) { + localStorage.removeItem('t3-' + key); + }; + /** + * Simple localStorage wrapper, to clear localStorage + */ + this.clear = function () { + localStorage.clear(); + }; + /** + * Checks if a key was set before, useful to not do all the undefined checks all the time + * + * @param {string} key + * @returns {boolean} + */ + this.isset = function (key) { + var value = _this.get(key); + return (typeof value !== 'undefined' && value !== null); + }; + } + return Client; + }()); + return new Client(); +}); diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Storage/Persistent.js b/typo3/sysext/backend/Resources/Public/JavaScript/Storage/Persistent.js new file mode 100644 index 000000000000..7e1a96791cc2 --- /dev/null +++ b/typo3/sysext/backend/Resources/Public/JavaScript/Storage/Persistent.js @@ -0,0 +1,209 @@ +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ +define(["require", "exports", "jquery"], function (require, exports, $) { + "use strict"; + /** + * Module: TYPO3/CMS/Backend/Storage/Persistent + * Wrapper for persistent storage in UC + * @exports TYPO3/CMS/Backend/Storage/Persistent + */ + var Persistent = (function () { + function Persistent() { + var _this = this; + this.data = false; + /** + * Persistent storage, stores everything on the server via AJAX, does a greedy load on read + * common functions get/set/clear + * + * @param {String} key + * @returns {*} + */ + this.get = function (key) { + var me = _this; + if (_this.data === false) { + var value_1; + _this.loadFromServer().done(function () { + value_1 = me.getRecursiveDataByDeepKey(me.data, key.split('.')); + }); + return value_1; + } + return _this.getRecursiveDataByDeepKey(_this.data, key.split('.')); + }; + /** + * Store data persistent on server + * + * @param {String} key + * @param {String} value + * @returns {$} + */ + this.set = function (key, value) { + if (_this.data !== false) { + _this.data = _this.setRecursiveDataByDeepKey(_this.data, key.split('.'), value); + } + return _this.storeOnServer(key, value); + }; + /** + * @param {string} key + * @param {string} value + * @returns {$} + */ + this.addToList = function (key, value) { + var me = _this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'addToList', + key: key, + value: value, + }, + method: 'post', + }).done(function (data) { + me.data = data; + }); + }; + /** + * @param {string} key + * @param {string} value + * @returns {$} + */ + this.removeFromList = function (key, value) { + var me = _this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'removeFromList', + key: key, + value: value, + }, + method: 'post', + }).done(function (data) { + me.data = data; + }); + }; + this.unset = function (key) { + var me = _this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'unset', + key: key, + }, + method: 'post', + }).done(function (data) { + me.data = data; + }); + }; + /** + * Clears the UC + */ + this.clear = function () { + $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'clear', + }, + }); + _this.data = false; + }; + /** + * Checks if a key was set before, useful to not do all the undefined checks all the time + * + * @param {string} key + * @returns {boolean} + */ + this.isset = function (key) { + var value = _this.get(key); + return (typeof value !== 'undefined' && value !== null); + }; + /** + * Loads the data from outside, only used for the initial call from BackendController + * + * @param {String} data + */ + this.load = function (data) { + _this.data = data; + }; + /** + * Loads all data from the server + * + * @returns {$} + */ + this.loadFromServer = function () { + var me = _this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + async: false, + data: { + action: 'getAll', + }, + }).done(function (data) { + me.data = data; + }); + }; + /** + * Stores data on the server, and gets the updated data on return + * to always be up-to-date inside the browser + * + * @param {string} key + * @param {string} value + * @returns {*} + */ + this.storeOnServer = function (key, value) { + var me = _this; + return $.ajax(TYPO3.settings.ajaxUrls.usersettings_process, { + data: { + action: 'set', + key: key, + value: value, + }, + method: 'post', + }).done(function (data) { + me.data = data; + }); + }; + /** + * Helper function used to set a value which could have been a flat object key data["my.foo.bar"] to + * data[my][foo][bar] is called recursively by itself + * + * @param {Object} data the data to be uased as base + * @param {String} keyParts the keyParts for the subtree + * @returns {Object} + */ + this.getRecursiveDataByDeepKey = function (data, keyParts) { + if (keyParts.length === 1) { + return (data || {})[keyParts[0]]; + } + var firstKey = keyParts.shift(); + return _this.getRecursiveDataByDeepKey(data[firstKey] || {}, keyParts); + }; + /** + * helper function used to set a value which could have been a flat object key data["my.foo.bar"] to + * data[my][foo][bar] + * is called recursively by itself + * + * @param data + * @param {any[]} keyParts + * @param {string} value + * @returns {any[]} + */ + this.setRecursiveDataByDeepKey = function (data, keyParts, value) { + if (keyParts.length === 1) { + data = data || {}; + data[keyParts[0]] = value; + } + else { + var firstKey = keyParts.shift(); + data[firstKey] = _this.setRecursiveDataByDeepKey(data[firstKey] || {}, keyParts, value); + } + return data; + }; + } + return Persistent; + }()); + return new Persistent(); +}); diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Tabs.js b/typo3/sysext/backend/Resources/Public/JavaScript/Tabs.js index ae18f2f70732..49b14ebd853e 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/Tabs.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/Tabs.js @@ -16,17 +16,17 @@ * This class handle the tabs in the TYPO3 backend. * It stores the last active tab and open it again after a reload, */ -define(['jquery', 'TYPO3/CMS/Backend/Storage', 'bootstrap'], function ($, Storage) { +define(['jquery', 'TYPO3/CMS/Backend/Storage/Client', 'bootstrap'], function ($, ClientStorage) { 'use strict'; /** * Tabs helper * - * @type {{storage: (Storage.Client|*), cacheTimeInSeconds: number, storeLastActiveTab: bool}} + * @type {{storage: (ClientStorage|*), cacheTimeInSeconds: number, storeLastActiveTab: bool}} * @exports TYPO3/CMS/Backend/Tabs */ var Tabs = { - storage: Storage.Client, + storage: ClientStorage, // cache lifetime in seconds cacheTimeInSeconds: 1800, storeLastActiveTab: true diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Toolbar/SystemInformationMenu.js b/typo3/sysext/backend/Resources/Public/JavaScript/Toolbar/SystemInformationMenu.js index 8098c9fe45e8..8128386c31ad 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/Toolbar/SystemInformationMenu.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/Toolbar/SystemInformationMenu.js @@ -18,9 +18,9 @@ define([ 'jquery', 'TYPO3/CMS/Backend/Icons', - 'TYPO3/CMS/Backend/Storage', + 'TYPO3/CMS/Backend/Storage/Persistent', 'TYPO3/CMS/Backend/Viewport' -], function($, Icons, Storage, Viewport) { +], function($, Icons, PersistentStorage, Viewport) { 'use strict'; /** @@ -124,13 +124,13 @@ define([ requestedModule = $(e.currentTarget).data('modulename'), timestamp = Math.floor((new Date()).getTime() / 1000); - if (Storage.Persistent.isset('systeminformation')) { - storedSystemInformationSettings = JSON.parse(Storage.Persistent.get('systeminformation')); + if (PersistentStorage.isset('systeminformation')) { + storedSystemInformationSettings = JSON.parse(PersistentStorage.get('systeminformation')); } moduleStorageObject[requestedModule] = {lastAccess: timestamp}; $.extend(true, storedSystemInformationSettings, moduleStorageObject); - var $ajax = Storage.Persistent.set('systeminformation', JSON.stringify(storedSystemInformationSettings)); + var $ajax = PersistentStorage.set('systeminformation', JSON.stringify(storedSystemInformationSettings)); $ajax.done(function() { // finally, open the module now TYPO3.ModuleMenu.App.showModule(requestedModule); diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-82603-DeprecateStorageModule.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-82603-DeprecateStorageModule.rst new file mode 100644 index 000000000000..e999dcaeca8f --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-82603-DeprecateStorageModule.rst @@ -0,0 +1,42 @@ +.. include:: ../../Includes.txt + +============================================== +Deprecation: #82603 - Deprecate Storage module +============================================== + +See :issue:`82603` + +Description +=========== + +The RequireJS module :js:`TYPO3/CMS/Backend/Storage` has been marked as deprecated. The module has been split into the +modules :js:`TYPO3/CMS/Backend/Storage/Client` and :js:`TYPO3/CMS/Backend/Storage/Persistent`. + +Impact +====== + +Using :js:`TYPO3/CMS/Backend/Storage` will trigger a warning in the browser's developer console. + + +Affected Installations +====================== + +All extensions using :js:`TYPO3/CMS/Backend/Storage` are affected. + + +Migration +========= + +Instead of using :js:`Storage.Client` and :js:`Storage.Persistent` use the introduced modules instead. + +Example code: + +.. code-block:: javascript + + define(['TYPO3/CMS/Backend/Storage/Persistent'], function(PersistentStorage) { + if (!PersistentStorage.isset('my-key')) { + PersistentStorage.set('my-key', 'foobar'); + } + }); + +.. index:: JavaScript, NotScanned diff --git a/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js b/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js index e421d1ad5b43..a6be684f96f2 100644 --- a/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js +++ b/typo3/sysext/recordlist/Resources/Public/JavaScript/Recordlist.js @@ -15,7 +15,7 @@ * Module: TYPO3/CMS/Recordlist/Recordlist * Usability improvements for the record list */ -define(['jquery', 'TYPO3/CMS/Backend/Storage', 'TYPO3/CMS/Backend/Icons'], function($, Storage, Icons) { +define(['jquery', 'TYPO3/CMS/Backend/Storage/Persistent', 'TYPO3/CMS/Backend/Icons'], function($, PersistentStorage, Icons) { 'use strict'; /** @@ -54,15 +54,15 @@ define(['jquery', 'TYPO3/CMS/Backend/Storage', 'TYPO3/CMS/Backend/Icons'], funct // Store collapse state in UC var storedModuleDataList = {}; - if (Storage.Persistent.isset('moduleData.list')) { - storedModuleDataList = Storage.Persistent.get('moduleData.list'); + if (PersistentStorage.isset('moduleData.list')) { + storedModuleDataList = PersistentStorage.get('moduleData.list'); } var collapseConfig = {}; collapseConfig[table] = isExpanded ? 1 : 0; $.extend(true, storedModuleDataList, collapseConfig); - Storage.Persistent.set('moduleData.list', storedModuleDataList).done(function() { + PersistentStorage.set('moduleData.list', storedModuleDataList).done(function() { $target.data('state', isExpanded ? 'collapsed' : 'expanded'); }); }; diff --git a/typo3/sysext/viewpage/Resources/Public/JavaScript/Main.js b/typo3/sysext/viewpage/Resources/Public/JavaScript/Main.js index 907f80a04d79..e66ed546c1f3 100644 --- a/typo3/sysext/viewpage/Resources/Public/JavaScript/Main.js +++ b/typo3/sysext/viewpage/Resources/Public/JavaScript/Main.js @@ -16,9 +16,9 @@ */ define([ 'jquery', - 'TYPO3/CMS/Backend/Storage', + 'TYPO3/CMS/Backend/Storage/Persistent', 'jquery-ui/resizable' -], function($, Storage) { +], function($, PersistentStorage) { 'use strict'; /** @@ -63,7 +63,7 @@ define([ if (ViewPage.queueIsRunning === false && ViewPage.queue.length >= 1) { ViewPage.queueIsRunning = true; var item = ViewPage.queue.shift(); - Storage.Persistent.set(item.storageIdentifier, item.data).done(function() { + PersistentStorage.set(item.storageIdentifier, item.data).done(function() { ViewPage.queueIsRunning = false; ViewPage.persistQueue(); }); -- GitLab