From 1739f5a9e707dec523b0668326ef1364779faf6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 18 Oct 2023 10:37:11 -0400 Subject: [PATCH] fix: #10276, only move widgets if area doesn't exist when changing a theme save all locations that have widgets in them, after restart go through these locations and if they don't exist anymore move those widgets to drafts --- src/socket.io/admin/themes.js | 2 +- src/webserver.js | 3 ++ src/widgets/admin.js | 15 ++----- src/widgets/index.js | 76 ++++++++++++++++++++++++++++++++--- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/src/socket.io/admin/themes.js b/src/socket.io/admin/themes.js index 32e677bcf8..7c8ad7da74 100644 --- a/src/socket.io/admin/themes.js +++ b/src/socket.io/admin/themes.js @@ -14,7 +14,7 @@ Themes.set = async function (socket, data) { throw new Error('[[error:invalid-data]]'); } if (data.type === 'local') { - await widgets.reset(); + await widgets.saveLocationsOnThemeReset(); } data.ip = socket.ip; diff --git a/src/webserver.js b/src/webserver.js index 8885849c7a..ff5031ff41 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -106,6 +106,9 @@ async function initializeNodeBB() { await flags.init(); await analytics.init(); await topicEvents.init(); + if (nconf.get('runJobs')) { + await require('./widgets').moveMissingAreasToDrafts(); + } } function setupExpressApp(app) { diff --git a/src/widgets/admin.js b/src/widgets/admin.js index d6e67f6917..eed216efad 100644 --- a/src/widgets/admin.js +++ b/src/widgets/admin.js @@ -21,19 +21,12 @@ admin.get = async function () { }; admin.getAreas = async function () { - const defaultAreas = [ - { name: 'Global Sidebar', template: 'global', location: 'sidebar' }, - { name: 'Global Header', template: 'global', location: 'header' }, - { name: 'Global Footer', template: 'global', location: 'footer' }, - - { name: 'Group Page (Left)', template: 'groups/details.tpl', location: 'left' }, - { name: 'Group Page (Right)', template: 'groups/details.tpl', location: 'right' }, - ]; - - const areas = await plugins.hooks.fire('filter:widgets.getAreas', defaultAreas); + const areas = await index.getAvailableAreas(); areas.push({ name: 'Draft Zone', template: 'global', location: 'drafts' }); - const areaData = await Promise.all(areas.map(area => index.getArea(area.template, area.location))); + const areaData = await Promise.all( + areas.map(area => index.getArea(area.template, area.location)) + ); areas.forEach((area, i) => { area.data = areaData[i]; }); diff --git a/src/widgets/index.js b/src/widgets/index.js index 0b5c736527..4302ac74c0 100644 --- a/src/widgets/index.js +++ b/src/widgets/index.js @@ -188,15 +188,81 @@ widgets.setAreas = async function (areas) { ); }; -widgets.reset = async function () { +widgets.getAvailableAreas = async function () { const defaultAreas = [ - { name: 'Draft Zone', template: 'global', location: 'header' }, - { name: 'Draft Zone', template: 'global', location: 'footer' }, - { name: 'Draft Zone', template: 'global', location: 'sidebar' }, + { name: 'Global Header', template: 'global', location: 'header' }, + { name: 'Global Footer', template: 'global', location: 'footer' }, + { name: 'Global Sidebar', template: 'global', location: 'sidebar' }, + + { name: 'Group Page (Left)', template: 'groups/details.tpl', location: 'left' }, + { name: 'Group Page (Right)', template: 'groups/details.tpl', location: 'right' }, ]; + return await plugins.hooks.fire('filter:widgets.getAreas', defaultAreas); +}; + +widgets.saveLocationsOnThemeReset = async function () { + const locations = {}; + const available = await widgets.getAvailableAreas(); + for (const area of available) { + /* eslint-disable no-await-in-loop */ + const widgetsAtLocation = await widgets.getArea(area.template, area.location); + if (widgetsAtLocation.length) { + locations[area.template] = locations[area.template] || []; + if (!locations[area.template].includes(area.location)) { + locations[area.template].push(area.location); + } + } + } + + if (Object.keys(locations).length) { + await db.set('widgets:draft:locations', JSON.stringify(locations)); + } +}; + +widgets.moveMissingAreasToDrafts = async function () { + const locationsObj = await db.get('widgets:draft:locations'); + if (!locationsObj) { + return; + } + try { + const locations = JSON.parse(locationsObj); + const [available, draftWidgets] = await Promise.all([ + widgets.getAvailableAreas(), + widgets.getArea('global', 'drafts'), + ]); + let saveDraftWidgets = draftWidgets || []; + for (const [template, tplLocations] of Object.entries(locations)) { + for (const location of tplLocations) { + const locationExists = available.find( + area => area.template === template && area.location === location + ); + if (!locationExists) { + const widgetsAtLocation = await widgets.getArea(template, location); + saveDraftWidgets = saveDraftWidgets.concat(widgetsAtLocation); + await widgets.setArea({ + template, + location, + widgets: [], + }); + } + } + } + await widgets.setArea({ + template: 'global', + location: 'drafts', + widgets: saveDraftWidgets, + }); + } catch (err) { + winston.error(err.stack); + } finally { + await db.delete('widgets:draft:locations'); + } +}; + +widgets.reset = async function () { const [areas, drafts] = await Promise.all([ - plugins.hooks.fire('filter:widgets.getAreas', defaultAreas), + widgets.getAvailableAreas(), widgets.getArea('global', 'drafts'), ]);