DEV: Move admin sidebar out of initializer (#25396)

Having the admin sidebar code in an instance initializer is not
ideal because:

* It runs during app boot which may not even be necessary based on site settings
* It makes it hard for plugins to register additional links in time without resorting
to before/after initializer gymnastics

This PR moves the admin sidebar into a lib and creates the panel
in custom-sections.js, then the sections and links are loaded when
the main sidebar component is rendered, which leaves plugins enough
time to add additional links in an initializer.

---------

Co-authored-by: David Taylor <david@taylorhq.com>
This commit is contained in:
Martin Brennan 2024-01-25 10:45:14 +10:00 committed by GitHub
parent 6ad34a0152
commit 57ededb770
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 83 additions and 116 deletions

View File

@ -2,13 +2,13 @@ import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
import {
buildAdminSidebar,
useAdminNavConfig,
} from "discourse/instance-initializers/admin-sidebar";
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
} from "discourse/lib/sidebar/admin-sidebar";
import { resetPanelSections } from "discourse/lib/sidebar/custom-sections";
import { ADMIN_PANEL } from "discourse/services/sidebar-state";
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
// TODO (martin) (2024-02-01) Remove this experimental UI.
export default class AdminConfigAreaSidebarExperiment extends Component {

View File

@ -1,7 +1,7 @@
import { inject as service } from "@ember/service";
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/lib/sidebar/panels";
import DiscourseURL from "discourse/lib/url";
import DiscourseRoute from "discourse/routes/discourse";
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/services/sidebar-state";
import I18n from "discourse-i18n";
export default class AdminRoute extends DiscourseRoute {

View File

@ -1,6 +1,6 @@
import { inject as service } from "@ember/service";
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/lib/sidebar/panels";
import DiscourseRoute from "discourse/routes/discourse";
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/services/sidebar-state";
import I18n from "discourse-i18n";
export default class AdminRoute extends DiscourseRoute {

View File

@ -42,7 +42,6 @@ import {
} from "discourse/helpers/category-link";
import { addUsernameSelectorDecorator } from "discourse/helpers/decorate-username-selector";
import { registerCustomAvatarHelper } from "discourse/helpers/user-avatar";
import { addAdminSidebarSectionLink } from "discourse/instance-initializers/admin-sidebar";
import { addBeforeAuthCompleteCallback } from "discourse/instance-initializers/auth-complete";
import { addPopupMenuOption } from "discourse/lib/composer/custom-popup-menu-options";
import { registerDesktopNotificationHandler } from "discourse/lib/desktop-notifications";
@ -67,6 +66,7 @@ import { addTagsHtmlCallback } from "discourse/lib/render-tags";
import { addFeaturedLinkMetaDecorator } from "discourse/lib/render-topic-featured-link";
import { addSearchResultsCallback } from "discourse/lib/search";
import Sharing from "discourse/lib/sharing";
import { addAdminSidebarSectionLink } from "discourse/lib/sidebar/admin-sidebar";
import { addSectionLink as addCustomCommunitySectionLink } from "discourse/lib/sidebar/custom-community-section-links";
import {
addSidebarPanel,

View File

@ -1,10 +1,11 @@
import { isEmpty } from "@ember/utils";
import PreloadStore from "discourse/lib/preload-store";
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
import {
addSidebarPanel,
addSidebarSection,
} from "discourse/lib/sidebar/custom-sections";
import { ADMIN_PANEL } from "discourse/services/sidebar-state";
import BaseCustomSidebarPanel from "discourse/lib/sidebar/base-custom-sidebar-panel";
import BaseCustomSidebarSection from "discourse/lib/sidebar/base-custom-sidebar-section";
import BaseCustomSidebarSectionLink from "discourse/lib/sidebar/base-custom-sidebar-section-link";
import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
import I18n from "discourse-i18n";
let additionalAdminSidebarSectionLinks = {};
@ -14,60 +15,52 @@ export function clearAdditionalAdminSidebarSectionLinks() {
additionalAdminSidebarSectionLinks = {};
}
function defineAdminSectionLink(BaseCustomSidebarSectionLink) {
const SidebarAdminSectionLink = class extends BaseCustomSidebarSectionLink {
constructor({ adminSidebarNavLink }) {
super(...arguments);
this.adminSidebarNavLink = adminSidebarNavLink;
}
class SidebarAdminSectionLink extends BaseCustomSidebarSectionLink {
constructor({ adminSidebarNavLink }) {
super(...arguments);
this.adminSidebarNavLink = adminSidebarNavLink;
}
get name() {
return this.adminSidebarNavLink.name;
}
get name() {
return this.adminSidebarNavLink.name;
}
get classNames() {
return "admin-sidebar-nav-link";
}
get classNames() {
return "admin-sidebar-nav-link";
}
get route() {
return this.adminSidebarNavLink.route;
}
get route() {
return this.adminSidebarNavLink.route;
}
get href() {
return this.adminSidebarNavLink.href;
}
get href() {
return this.adminSidebarNavLink.href;
}
get models() {
return this.adminSidebarNavLink.routeModels;
}
get models() {
return this.adminSidebarNavLink.routeModels;
}
get text() {
return this.adminSidebarNavLink.label
? I18n.t(this.adminSidebarNavLink.label)
: this.adminSidebarNavLink.text;
}
get text() {
return this.adminSidebarNavLink.label
? I18n.t(this.adminSidebarNavLink.label)
: this.adminSidebarNavLink.text;
}
get prefixType() {
return "icon";
}
get prefixType() {
return "icon";
}
get prefixValue() {
return this.adminSidebarNavLink.icon;
}
get prefixValue() {
return this.adminSidebarNavLink.icon;
}
get title() {
return this.adminSidebarNavLink.text;
}
};
return SidebarAdminSectionLink;
get title() {
return this.adminSidebarNavLink.text;
}
}
function defineAdminSection(
adminNavSectionData,
BaseCustomSidebarSection,
adminSectionLinkClass
) {
function defineAdminSection(adminNavSectionData) {
const AdminNavSection = class extends BaseCustomSidebarSection {
constructor() {
super(...arguments);
@ -96,7 +89,7 @@ function defineAdminSection(
get links() {
return this.sectionLinks.map(
(sectionLinkData) =>
new adminSectionLinkClass({ adminSidebarNavLink: sectionLinkData })
new SidebarAdminSectionLink({ adminSidebarNavLink: sectionLinkData })
);
}
@ -163,27 +156,6 @@ export function useAdminNavConfig(navMap) {
return navMap;
}
let adminSectionLinkClass = null;
export function buildAdminSidebar(navConfig) {
navConfig.forEach((adminNavSectionData) => {
addSidebarSection(
(BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
// We only want to define the link class once even though we have many different sections.
adminSectionLinkClass =
adminSectionLinkClass ||
defineAdminSectionLink(BaseCustomSidebarSectionLink);
return defineAdminSection(
adminNavSectionData,
BaseCustomSidebarSection,
adminSectionLinkClass
);
},
ADMIN_PANEL
);
});
}
// This is used for a plugin API.
export function addAdminSidebarSectionLink(sectionName, link) {
if (!additionalAdminSidebarSectionLinks.hasOwnProperty(sectionName)) {
@ -237,41 +209,26 @@ function pluginAdminRouteLinks() {
);
}
export default {
name: "admin-sidebar-initializer",
export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
key = ADMIN_PANEL;
hidden = true;
initialize(owner) {
this.currentUser = owner.lookup("service:current-user");
this.siteSettings = owner.lookup("service:site-settings");
if (
!this.currentUser?.staff ||
!this.siteSettings.userInAnyGroups(
"admin_sidebar_enabled_groups",
this.currentUser
)
) {
return;
get sections() {
const siteSettings = getOwnerWithFallback().lookup("service:site-settings");
if (isEmpty(siteSettings.admin_sidebar_enabled_groups)) {
return [];
}
this.adminSidebarExperimentStateManager = owner.lookup(
this.adminSidebarExperimentStateManager = getOwnerWithFallback(this).lookup(
"service:admin-sidebar-experiment-state-manager"
);
addSidebarPanel(
(BaseCustomSidebarPanel) =>
class AdminSidebarPanel extends BaseCustomSidebarPanel {
key = ADMIN_PANEL;
hidden = true;
}
);
const savedConfig = this.adminSidebarExperimentStateManager.navConfig;
const navMap = savedConfig || ADMIN_NAV_MAP;
navMap.findBy("name", "plugins").links.push(...pluginAdminRouteLinks());
if (this.siteSettings.experimental_form_templates) {
if (siteSettings.experimental_form_templates) {
navMap.findBy("name", "customize").links.push({
name: "admin_customize_form_templates",
route: "adminCustomizeFormTemplates",
@ -280,6 +237,10 @@ export default {
});
}
buildAdminSidebar(useAdminNavConfig(navMap), adminSectionLinkClass);
},
};
const navConfig = useAdminNavConfig(navMap);
return navConfig.map((adminNavSectionData) => {
return defineAdminSection(adminNavSectionData);
});
}
}

View File

@ -1,7 +1,9 @@
import BaseCustomSidebarPanel from "discourse/lib/sidebar/base-custom-sidebar-panel";
import BaseCustomSidebarSection from "discourse/lib/sidebar/base-custom-sidebar-section";
import BaseCustomSidebarSectionLink from "discourse/lib/sidebar/base-custom-sidebar-section-link";
import { MAIN_PANEL } from "discourse/lib/sidebar/panels";
import I18n from "discourse-i18n";
import AdminSidebarPanel from "./admin-sidebar";
class MainSidebarPanel {
sections = [];
@ -23,9 +25,9 @@ class MainSidebarPanel {
}
}
export let customPanels = [new MainSidebarPanel()];
export let currentPanelKey = "main";
export let customPanels;
export let currentPanelKey;
resetSidebarPanels();
export function addSidebarPanel(func) {
const panelClass = func.call(this, BaseCustomSidebarPanel);
@ -60,6 +62,6 @@ export function resetPanelSections(
}
export function resetSidebarPanels() {
customPanels = [new MainSidebarPanel()];
currentPanelKey = "main";
customPanels = [new MainSidebarPanel(), new AdminSidebarPanel()];
currentPanelKey = MAIN_PANEL;
}

View File

@ -0,0 +1,4 @@
export const COMBINED_MODE = "combined";
export const SEPARATED_MODE = "separated";
export const MAIN_PANEL = "main";
export const ADMIN_PANEL = "admin";

View File

@ -5,11 +5,11 @@ import {
currentPanelKey,
customPanels as panels,
} from "discourse/lib/sidebar/custom-sections";
const COMBINED_MODE = "combined";
const SEPARATED_MODE = "separated";
export const MAIN_PANEL = "main";
export const ADMIN_PANEL = "admin";
import {
COMBINED_MODE,
MAIN_PANEL,
SEPARATED_MODE,
} from "discourse/lib/sidebar/panels";
@disableImplicitInjections
export default class SidebarState extends Service {

View File

@ -28,7 +28,6 @@ import { resetUserMenuProfileTabItems } from "discourse/components/user-menu/pro
import { resetCustomPostMessageCallbacks } from "discourse/controllers/topic";
import { clearHTMLCache } from "discourse/helpers/custom-html";
import { resetUsernameDecorators } from "discourse/helpers/decorate-username-selector";
import { clearAdditionalAdminSidebarSectionLinks } from "discourse/instance-initializers/admin-sidebar";
import { resetBeforeAuthCompleteCallbacks } from "discourse/instance-initializers/auth-complete";
import { clearPopupMenuOptions } from "discourse/lib/composer/custom-popup-menu-options";
import { clearDesktopNotificationHandlers } from "discourse/lib/desktop-notifications";
@ -50,6 +49,7 @@ import PreloadStore from "discourse/lib/preload-store";
import { clearTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
import { clearTopicFooterDropdowns } from "discourse/lib/register-topic-footer-dropdown";
import { clearTagsHtmlCallbacks } from "discourse/lib/render-tags";
import { clearAdditionalAdminSidebarSectionLinks } from "discourse/lib/sidebar/admin-sidebar";
import { resetDefaultSectionLinks as resetTopicsSectionLinks } from "discourse/lib/sidebar/custom-community-section-links";
import { resetSidebarPanels } from "discourse/lib/sidebar/custom-sections";
import {

View File

@ -1,4 +1,4 @@
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/services/sidebar-state";
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/lib/sidebar/panels";
import { getUserChatSeparateSidebarMode } from "discourse/plugins/chat/discourse/lib/get-user-chat-separate-sidebar-mode";
export const CHAT_PANEL = "chat";

View File

@ -2,9 +2,9 @@ import { tracked } from "@glimmer/tracking";
import Service, { inject as service } from "@ember/service";
import KeyValueStore from "discourse/lib/key-value-store";
import { withPluginApi } from "discourse/lib/plugin-api";
import { MAIN_PANEL } from "discourse/lib/sidebar/panels";
import { defaultHomepage } from "discourse/lib/utilities";
import Site from "discourse/models/site";
import { MAIN_PANEL } from "discourse/services/sidebar-state";
import getURL from "discourse-common/lib/get-url";
import { getUserChatSeparateSidebarMode } from "discourse/plugins/chat/discourse/lib/get-user-chat-separate-sidebar-mode";