From fecef67c5d0ee51ba94e61fa7c2f5373ae662837 Mon Sep 17 00:00:00 2001 From: Andreas Fernandez Date: Fri, 7 Aug 2020 14:03:10 +0200 Subject: [PATCH] [TASK] Reload topbar when sys_workspace records are changed This patch fixed a long existing issue regarding workspaces. If a workspace is either created, updated or deleted, the topbar menu gets reloaded to update the workspace menu now. Resolves: #91948 Releases: master, 10.4 Change-Id: I316994c209d3bebc0e72e8e7ad956c5cf30502c9 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/65207 Tested-by: TYPO3com Tested-by: Andreas Fernandez Reviewed-by: Andreas Fernandez --- .../TypeScript/Toolbar/WorkspacesMenu.ts | 38 ++++++++++++++----- .../Classes/Hook/DataHandlerHook.php | 13 +++++++ .../JavaScript/Toolbar/WorkspacesMenu.js | 2 +- typo3/sysext/workspaces/ext_localconf.php | 1 + 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/Build/Sources/TypeScript/workspaces/Resources/Public/TypeScript/Toolbar/WorkspacesMenu.ts b/Build/Sources/TypeScript/workspaces/Resources/Public/TypeScript/Toolbar/WorkspacesMenu.ts index aac11508e5c0..0b314e0a3230 100644 --- a/Build/Sources/TypeScript/workspaces/Resources/Public/TypeScript/Toolbar/WorkspacesMenu.ts +++ b/Build/Sources/TypeScript/workspaces/Resources/Public/TypeScript/Toolbar/WorkspacesMenu.ts @@ -16,6 +16,7 @@ import {AjaxResponse} from 'TYPO3/CMS/Core/Ajax/AjaxResponse'; import AjaxRequest = require('TYPO3/CMS/Core/Ajax/AjaxRequest'); import ModuleMenu = require('TYPO3/CMS/Backend/ModuleMenu'); import Viewport = require('TYPO3/CMS/Backend/Viewport'); +import RegularEvent = require('TYPO3/CMS/Core/Event/RegularEvent'); enum Identifiers { containerSelector = '#typo3-cms-workspaces-backend-toolbaritems-workspaceselectortoolbaritem', @@ -46,6 +47,18 @@ class WorkspacesMenu { } } + private static updateWorkspaceState() { + // This is a poor-mans state update in case the current active workspace has been renamed + const selectedWorkspaceLink: HTMLElement = document.querySelector(Identifiers.containerSelector + ' .t3js-workspace-item.selected .t3js-workspaces-switchlink'); + if (selectedWorkspaceLink !== null) { + const workspaceId = parseInt(selectedWorkspaceLink.dataset.workspaceid, 10); + const title = selectedWorkspaceLink.innerText.trim(); + + top.TYPO3.configuration.inWorkspace = workspaceId !== 0; + top.TYPO3.Backend.workspaceTitle = top.TYPO3.configuration.inWorkspace ? title : ''; + } + } + /** * adds the workspace title to the toolbar next to the username * @@ -62,11 +75,13 @@ class WorkspacesMenu { } } - private static updateBackendContext(title: string = ''): void { + private static updateBackendContext(): void { let topBarTitle = ''; + WorkspacesMenu.updateWorkspaceState(); + if (TYPO3.configuration.inWorkspace) { $('body').addClass(Classes.workspaceBodyClass); - topBarTitle = title || TYPO3.lang['Workspaces.workspaceTitle']; + topBarTitle = top.TYPO3.Backend.workspaceTitle || TYPO3.lang['Workspaces.workspaceTitle']; } else { $('body').removeClass(Classes.workspaceBodyClass); } @@ -79,6 +94,13 @@ class WorkspacesMenu { this.initializeEvents(); WorkspacesMenu.updateBackendContext(); }); + + new RegularEvent('typo3:datahandler:delete', (e: CustomEvent): void => { + const payload = e.detail.payload; + if (payload.table === 'sys_workspace' && payload.action === 'delete' && payload.hasErrors === false) { + Viewport.Topbar.refresh(); + } + }).bindTo(document); } /** @@ -86,14 +108,8 @@ class WorkspacesMenu { * This method is also used in the workspaces backend module. * * @param {Number} id the workspace ID - * @param {String} title the workspace title */ - public performWorkspaceSwitch(id: number, title: string): void { - top.TYPO3.Backend.workspaceTitle = title; - top.TYPO3.configuration.inWorkspace = id !== 0; - - WorkspacesMenu.updateBackendContext(title); - + public performWorkspaceSwitch(id: number): void { // first remove all checks, then set the check in front of the selected workspace const stateActiveClass = 'fa fa-check'; const stateInactiveClass = 'fa fa-empty-empty'; @@ -111,6 +127,8 @@ class WorkspacesMenu { .removeClass(stateInactiveClass) .addClass(stateActiveClass); $menuItem.addClass('selected'); + + WorkspacesMenu.updateBackendContext(); } private initializeEvents(): void { @@ -135,7 +153,7 @@ class WorkspacesMenu { data.workspaceId = 0; } - this.performWorkspaceSwitch(parseInt(data.workspaceId, 10), data.title); + this.performWorkspaceSwitch(parseInt(data.workspaceId, 10)); // append the returned page ID to the current module URL if (data.pageId) { diff --git a/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php b/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php index 5536f9f38949..5f882e934e56 100644 --- a/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php +++ b/typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php @@ -326,10 +326,18 @@ public function processCmdmap_postProcess($command, $table, $id, $value, DataHan $this->resetStageOfElements((int)$id); } elseif ($table === WorkspaceService::TABLE_WORKSPACE) { $this->flushWorkspaceElements((int)$id); + $this->emitUpdateTopbarSignal(); } } } + public function processDatamap_afterAllOperations(DataHandler $dataHandler): void + { + if (isset($dataHandler->datamap[WorkspaceService::TABLE_WORKSPACE])) { + $this->emitUpdateTopbarSignal(); + } + } + /** * Hook for \TYPO3\CMS\Core\DataHandling\DataHandler::moveRecord that cares about * moving records that are *not* in the live workspace @@ -1451,6 +1459,11 @@ public function getCommandMap(DataHandler $dataHandler): CommandMap ); } + protected function emitUpdateTopbarSignal(): void + { + BackendUtility::setUpdateSignal('updateTopbar'); + } + /** * Returns all fieldnames from a table which have the unique evaluation type set. * diff --git a/typo3/sysext/workspaces/Resources/Public/JavaScript/Toolbar/WorkspacesMenu.js b/typo3/sysext/workspaces/Resources/Public/JavaScript/Toolbar/WorkspacesMenu.js index 6d8d3139d91f..db11d7a79881 100644 --- a/typo3/sysext/workspaces/Resources/Public/JavaScript/Toolbar/WorkspacesMenu.js +++ b/typo3/sysext/workspaces/Resources/Public/JavaScript/Toolbar/WorkspacesMenu.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","TYPO3/CMS/Backend/ModuleMenu","TYPO3/CMS/Backend/Viewport"],(function(e,t,a,o,r,s){"use strict";var n,c;a=__importDefault(a),function(e){e.containerSelector="#typo3-cms-workspaces-backend-toolbaritems-workspaceselectortoolbaritem",e.activeMenuItemLinkSelector=".dropdown-menu .selected",e.menuItemSelector=".t3js-workspace-item",e.menuItemLinkSelector=".t3js-workspaces-switchlink",e.toolbarItemSelector=".dropdown-toggle",e.workspaceModuleLinkSelector=".t3js-workspaces-modulelink"}(n||(n={})),function(e){e.workspaceBodyClass="typo3-in-workspace",e.workspacesTitleInToolbarClass="toolbar-item-name"}(c||(c={}));class l{static refreshPageTree(){s.NavigationContainer&&s.NavigationContainer.PageTree&&s.NavigationContainer.PageTree.refreshTree()}static updateTopBar(e){if(a.default("."+c.workspacesTitleInToolbarClass,n.containerSelector).remove(),e&&e.length){let t=a.default("",{class:c.workspacesTitleInToolbarClass}).text(e);a.default(n.toolbarItemSelector,n.containerSelector).append(t)}}static updateBackendContext(e=""){let t="";TYPO3.configuration.inWorkspace?(a.default("body").addClass(c.workspaceBodyClass),t=e||TYPO3.lang["Workspaces.workspaceTitle"]):a.default("body").removeClass(c.workspaceBodyClass),l.updateTopBar(t)}constructor(){s.Topbar.Toolbar.registerEvent(()=>{this.initializeEvents(),l.updateBackendContext()})}performWorkspaceSwitch(e,t){top.TYPO3.Backend.workspaceTitle=t,top.TYPO3.configuration.inWorkspace=0!==e,l.updateBackendContext(t);a.default(n.activeMenuItemLinkSelector+" i",n.containerSelector).removeClass("fa fa-check").addClass("fa fa-empty-empty"),a.default(n.activeMenuItemLinkSelector,n.containerSelector).removeClass("selected");const o=a.default(n.menuItemLinkSelector+"[data-workspaceid="+e+"]",n.containerSelector).closest(n.menuItemSelector);o.find("i").removeClass("fa fa-empty-empty").addClass("fa fa-check"),o.addClass("selected")}initializeEvents(){a.default(n.containerSelector).on("click",n.workspaceModuleLinkSelector,e=>{e.preventDefault(),r.App.showModule(e.currentTarget.dataset.module)}),a.default(n.containerSelector).on("click",n.menuItemLinkSelector,e=>{e.preventDefault(),this.switchWorkspace(parseInt(e.currentTarget.dataset.workspaceid,10))})}switchWorkspace(e){new o(TYPO3.settings.ajaxUrls.workspace_switch).post({workspaceId:e,pageId:top.fsMod.recentIds.web}).then(async t=>{const a=await t.resolve();if(a.workspaceId||(a.workspaceId=0),this.performWorkspaceSwitch(parseInt(a.workspaceId,10),a.title),a.pageId){top.fsMod.recentIds.web=a.pageId;let e=TYPO3.Backend.ContentContainer.getUrl();e+=(e.includes("?")?"&":"?")+"&id="+a.pageId,l.refreshPageTree(),s.ContentContainer.setUrl(e)}else top.currentModuleLoaded.startsWith("web_")?(l.refreshPageTree(),"web_WorkspacesWorkspaces"===top.currentModuleLoaded?r.App.showModule(top.currentModuleLoaded,"workspace="+e):r.App.reloadFrames()):TYPO3.configuration.pageModule&&r.App.showModule(TYPO3.configuration.pageModule);r.App.refreshMenu()})}}const i=new l;return TYPO3.WorkspacesMenu=i,i})); \ No newline at end of file +var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","TYPO3/CMS/Backend/ModuleMenu","TYPO3/CMS/Backend/Viewport","TYPO3/CMS/Core/Event/RegularEvent"],(function(e,t,a,o,r,s,n){"use strict";var c,l;a=__importDefault(a),function(e){e.containerSelector="#typo3-cms-workspaces-backend-toolbaritems-workspaceselectortoolbaritem",e.activeMenuItemLinkSelector=".dropdown-menu .selected",e.menuItemSelector=".t3js-workspace-item",e.menuItemLinkSelector=".t3js-workspaces-switchlink",e.toolbarItemSelector=".dropdown-toggle",e.workspaceModuleLinkSelector=".t3js-workspaces-modulelink"}(c||(c={})),function(e){e.workspaceBodyClass="typo3-in-workspace",e.workspacesTitleInToolbarClass="toolbar-item-name"}(l||(l={}));class i{static refreshPageTree(){s.NavigationContainer&&s.NavigationContainer.PageTree&&s.NavigationContainer.PageTree.refreshTree()}static updateWorkspaceState(){const e=document.querySelector(c.containerSelector+" .t3js-workspace-item.selected .t3js-workspaces-switchlink");if(null!==e){const t=parseInt(e.dataset.workspaceid,10),a=e.innerText.trim();top.TYPO3.configuration.inWorkspace=0!==t,top.TYPO3.Backend.workspaceTitle=top.TYPO3.configuration.inWorkspace?a:""}}static updateTopBar(e){if(a.default("."+l.workspacesTitleInToolbarClass,c.containerSelector).remove(),e&&e.length){let t=a.default("",{class:l.workspacesTitleInToolbarClass}).text(e);a.default(c.toolbarItemSelector,c.containerSelector).append(t)}}static updateBackendContext(){let e="";i.updateWorkspaceState(),TYPO3.configuration.inWorkspace?(a.default("body").addClass(l.workspaceBodyClass),e=top.TYPO3.Backend.workspaceTitle||TYPO3.lang["Workspaces.workspaceTitle"]):a.default("body").removeClass(l.workspaceBodyClass),i.updateTopBar(e)}constructor(){s.Topbar.Toolbar.registerEvent(()=>{this.initializeEvents(),i.updateBackendContext()}),new n("typo3:datahandler:delete",e=>{const t=e.detail.payload;"sys_workspace"===t.table&&"delete"===t.action&&!1===t.hasErrors&&s.Topbar.refresh()}).bindTo(document)}performWorkspaceSwitch(e){a.default(c.activeMenuItemLinkSelector+" i",c.containerSelector).removeClass("fa fa-check").addClass("fa fa-empty-empty"),a.default(c.activeMenuItemLinkSelector,c.containerSelector).removeClass("selected");const t=a.default(c.menuItemLinkSelector+"[data-workspaceid="+e+"]",c.containerSelector).closest(c.menuItemSelector);t.find("i").removeClass("fa fa-empty-empty").addClass("fa fa-check"),t.addClass("selected"),i.updateBackendContext()}initializeEvents(){a.default(c.containerSelector).on("click",c.workspaceModuleLinkSelector,e=>{e.preventDefault(),r.App.showModule(e.currentTarget.dataset.module)}),a.default(c.containerSelector).on("click",c.menuItemLinkSelector,e=>{e.preventDefault(),this.switchWorkspace(parseInt(e.currentTarget.dataset.workspaceid,10))})}switchWorkspace(e){new o(TYPO3.settings.ajaxUrls.workspace_switch).post({workspaceId:e,pageId:top.fsMod.recentIds.web}).then(async t=>{const a=await t.resolve();if(a.workspaceId||(a.workspaceId=0),this.performWorkspaceSwitch(parseInt(a.workspaceId,10)),a.pageId){top.fsMod.recentIds.web=a.pageId;let e=TYPO3.Backend.ContentContainer.getUrl();e+=(e.includes("?")?"&":"?")+"&id="+a.pageId,i.refreshPageTree(),s.ContentContainer.setUrl(e)}else top.currentModuleLoaded.startsWith("web_")?(i.refreshPageTree(),"web_WorkspacesWorkspaces"===top.currentModuleLoaded?r.App.showModule(top.currentModuleLoaded,"workspace="+e):r.App.reloadFrames()):TYPO3.configuration.pageModule&&r.App.showModule(TYPO3.configuration.pageModule);r.App.refreshMenu()})}}const p=new i;return TYPO3.WorkspacesMenu=p,p})); \ No newline at end of file diff --git a/typo3/sysext/workspaces/ext_localconf.php b/typo3/sysext/workspaces/ext_localconf.php index 4c49e4612866..b7db372f5de6 100644 --- a/typo3/sysext/workspaces/ext_localconf.php +++ b/typo3/sysext/workspaces/ext_localconf.php @@ -13,6 +13,7 @@ // register the hook to actually do the work within DataHandler $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\DataHandlerHook::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\DataHandlerHook::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass']['version'] = \TYPO3\CMS\Workspaces\Hook\DataHandlerHook::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\BackendUtilityHook::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/alt_doc.php']['makeEditForm_accessCheck']['workspaces'] = \TYPO3\CMS\Workspaces\Hook\BackendUtilityHook::class . '->makeEditForm_accessCheck';