mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 09:42:02 +08:00
FIX: Admin sidebar fixes and custom link registration (#25200)
This commit adds some more links to the admin sidebar and removes some to give it more parity with the old nav structure. This also adds the `addAdminSidebarSectionLink` plugin API to replace the admin-menu plugin outlet, which is used by plugins like docker-manager to add links to the old admin nav.
This commit is contained in:
parent
52511a3260
commit
de88fc26df
|
@ -84,12 +84,16 @@ export default class AdminConfigAreaSidebarExperiment extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
// Using the private `_routerMicrolib` is not ideal, but Ember doesn't provide
|
||||
// any other way for us to easily check for route validity.
|
||||
try {
|
||||
this.router._router._routerMicrolib.recognizer.handlersFor(
|
||||
link.route
|
||||
);
|
||||
this.validRouteNames.add(link.route);
|
||||
} catch {
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug("[AdminSidebarExperiment]", err);
|
||||
invalidRoutes.push(link.route);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
{{/if}}
|
||||
<NavItem @route="adminPlugins" @label="admin.plugins.title" />
|
||||
{{/if}}
|
||||
{{! TODO: What do we do with this?? How many plugins are using? }}
|
||||
<PluginOutlet @name="admin-menu" />
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,13 @@ import {
|
|||
import { ADMIN_PANEL } from "discourse/services/sidebar-state";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
let additionalAdminSidebarSectionLinks = {};
|
||||
|
||||
// For testing.
|
||||
export function clearAdditionalAdminSidebarSectionLinks() {
|
||||
additionalAdminSidebarSectionLinks = {};
|
||||
}
|
||||
|
||||
function defineAdminSectionLink(BaseCustomSidebarSectionLink) {
|
||||
const SidebarAdminSectionLink = class extends BaseCustomSidebarSectionLink {
|
||||
constructor({ adminSidebarNavLink }) {
|
||||
|
@ -132,12 +139,6 @@ export function useAdminNavConfig(navMap) {
|
|||
label: "admin.users.title",
|
||||
icon: "users",
|
||||
},
|
||||
{
|
||||
name: "admin_reports",
|
||||
route: "adminReports",
|
||||
label: "admin.dashboard.reports_tab",
|
||||
icon: "chart-pie",
|
||||
},
|
||||
{
|
||||
name: "admin_badges",
|
||||
route: "adminBadges",
|
||||
|
@ -148,7 +149,18 @@ export function useAdminNavConfig(navMap) {
|
|||
},
|
||||
];
|
||||
|
||||
return adminNavSections.concat(navMap);
|
||||
navMap = adminNavSections.concat(navMap);
|
||||
|
||||
for (const [sectionName, additionalLinks] of Object.entries(
|
||||
additionalAdminSidebarSectionLinks
|
||||
)) {
|
||||
const section = navMap.findBy("name", sectionName);
|
||||
if (section && additionalLinks.length) {
|
||||
section.links.push(...additionalLinks);
|
||||
}
|
||||
}
|
||||
|
||||
return navMap;
|
||||
}
|
||||
|
||||
let adminSectionLinkClass = null;
|
||||
|
@ -172,16 +184,68 @@ export function buildAdminSidebar(navConfig) {
|
|||
});
|
||||
}
|
||||
|
||||
// This is used for a plugin API.
|
||||
export function addAdminSidebarSectionLink(sectionName, link) {
|
||||
if (!additionalAdminSidebarSectionLinks.hasOwnProperty(sectionName)) {
|
||||
additionalAdminSidebarSectionLinks[sectionName] = [];
|
||||
}
|
||||
|
||||
// make the name extra-unique
|
||||
link.name = `admin_additional_${sectionName}_${link.name}`;
|
||||
|
||||
if (!link.href && !link.route) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(
|
||||
"[AdminSidebar]",
|
||||
`Custom link ${sectionName}_${link.name} must have either href or route`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!link.label && !link.text) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(
|
||||
"[AdminSidebar]",
|
||||
`Custom link ${sectionName}_${link.name} must have either label (which is an I18n key) or text`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// label must be valid, don't want broken [XYZ translation missing]
|
||||
if (link.label && typeof I18n.lookup(link.label) !== "string") {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(
|
||||
"[AdminSidebar]",
|
||||
`Custom link ${sectionName}_${link.name} must have a valid I18n label, got ${link.label}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
additionalAdminSidebarSectionLinks[sectionName].push(link);
|
||||
}
|
||||
|
||||
function pluginAdminRouteLinks() {
|
||||
return (PreloadStore.get("enabledPluginAdminRoutes") || []).map(
|
||||
(pluginAdminRoute) => {
|
||||
return {
|
||||
name: `admin_plugin_${pluginAdminRoute.location}`,
|
||||
route: `adminPlugins.${pluginAdminRoute.location}`,
|
||||
label: pluginAdminRoute.label,
|
||||
icon: "cog",
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "admin-sidebar-initializer",
|
||||
|
||||
initialize(owner) {
|
||||
this.currentUser = owner.lookup("service:current-user");
|
||||
this.siteSettings = owner.lookup("service:site-settings");
|
||||
|
||||
if (!this.currentUser?.staff) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.currentUser?.staff ||
|
||||
!this.siteSettings.userInAnyGroups(
|
||||
"admin_sidebar_enabled_groups",
|
||||
this.currentUser
|
||||
|
@ -205,16 +269,7 @@ export default {
|
|||
const savedConfig = this.adminSidebarExperimentStateManager.navConfig;
|
||||
const navMap = savedConfig || ADMIN_NAV_MAP;
|
||||
|
||||
const enabledPluginAdminRoutes =
|
||||
PreloadStore.get("enabledPluginAdminRoutes") || [];
|
||||
enabledPluginAdminRoutes.forEach((pluginAdminRoute) => {
|
||||
navMap.findBy("name", "admin_plugins").links.push({
|
||||
name: `admin_plugin_${pluginAdminRoute.location}`,
|
||||
route: `adminPlugins.${pluginAdminRoute.location}`,
|
||||
label: pluginAdminRoute.label,
|
||||
icon: "cog",
|
||||
});
|
||||
});
|
||||
navMap.findBy("name", "plugins").links.push(...pluginAdminRouteLinks());
|
||||
|
||||
if (this.siteSettings.experimental_form_templates) {
|
||||
navMap.findBy("name", "customize").links.push({
|
||||
|
|
|
@ -41,6 +41,7 @@ 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";
|
||||
|
@ -145,7 +146,7 @@ import { modifySelectKit } from "select-kit/mixins/plugin-api";
|
|||
// docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version
|
||||
// using the format described at https://keepachangelog.com/en/1.0.0/.
|
||||
|
||||
export const PLUGIN_API_VERSION = "1.23.0";
|
||||
export const PLUGIN_API_VERSION = "1.24.0";
|
||||
|
||||
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
||||
function canModify(klass, type, resolverName, changes) {
|
||||
|
@ -2298,6 +2299,35 @@ class PluginApi {
|
|||
addSidebarPanel(func);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. Do not use.
|
||||
* Support for adding links to specific admin sidebar sections.
|
||||
*
|
||||
* This is intended to replace the admin-menu plugin outlet from
|
||||
* the old admin horizontal nav.
|
||||
*
|
||||
* ```
|
||||
* api.addAdminSidebarSectionLink("root", {
|
||||
* name: "unique_link_name",
|
||||
* label: "admin.some.i18n.label.key",
|
||||
* route: "(optional) emberRouteId",
|
||||
* href: "(optional) can be used instead of the route",
|
||||
* }
|
||||
* ```
|
||||
|
||||
* @param {String} sectionName - The name of the admin sidebar section to add the link to.
|
||||
* @param {Object} link - A link object representing a section link for the sidebar.
|
||||
* @param {string} link.name - The name of the link. Needs to be dasherized and lowercase.
|
||||
* @param {string} link.title - The title attribute for the link.
|
||||
* @param {string} link.text - The text to display for the link.
|
||||
* @param {string} [link.route] - The Ember route name to generate the href attribute for the link.
|
||||
* @param {string} [link.href] - The href attribute for the link.
|
||||
* @param {string} [link.icon] - The FontAwesome icon to display for the link.
|
||||
*/
|
||||
addAdminSidebarSectionLink(sectionName, link) {
|
||||
addAdminSidebarSectionLink(sectionName, link);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. Do not use.
|
||||
* Support for setting a Sidebar panel.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import getURL from "discourse-common/lib/get-url";
|
||||
|
||||
export const ADMIN_NAV_MAP = [
|
||||
{
|
||||
name: "admin_plugins",
|
||||
name: "plugins",
|
||||
route: "adminPlugins.index",
|
||||
label: "admin.plugins.title",
|
||||
links: [
|
||||
|
@ -14,7 +16,7 @@ export const ADMIN_NAV_MAP = [
|
|||
},
|
||||
{
|
||||
name: "email",
|
||||
text: "Email",
|
||||
text: "Emails",
|
||||
links: [
|
||||
{
|
||||
name: "admin_email",
|
||||
|
@ -52,6 +54,18 @@ export const ADMIN_NAV_MAP = [
|
|||
label: "admin.email.rejected",
|
||||
icon: "ban",
|
||||
},
|
||||
{
|
||||
name: "admin_email_preview_summary",
|
||||
route: "adminEmail.previewDigest",
|
||||
label: "admin.email.preview_digest",
|
||||
icon: "notification.private_message",
|
||||
},
|
||||
{
|
||||
name: "admin_email_advanced_test",
|
||||
route: "adminEmail.advancedTest",
|
||||
label: "admin.email.advanced_test.title",
|
||||
icon: "wrench",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -88,6 +102,12 @@ export const ADMIN_NAV_MAP = [
|
|||
label: "admin.logs.search_logs.title",
|
||||
icon: "search",
|
||||
},
|
||||
{
|
||||
name: "admin_logs_error_logs",
|
||||
href: getURL("/logs"),
|
||||
label: "admin.logs.logster.title",
|
||||
icon: "external-link-alt",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
import { visit } from "@ember/test-helpers";
|
||||
import { test } from "qunit";
|
||||
import { AUTO_GROUPS } from "discourse/lib/constants";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import PreloadStore from "discourse/lib/preload-store";
|
||||
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
acceptance("Admin Sidebar - Sections", function (needs) {
|
||||
needs.user({
|
||||
admin: true,
|
||||
groups: [AUTO_GROUPS.admins],
|
||||
});
|
||||
|
||||
needs.settings({
|
||||
admin_sidebar_enabled_groups: "1",
|
||||
});
|
||||
|
||||
needs.hooks.beforeEach(() => {
|
||||
PreloadStore.store("enabledPluginAdminRoutes", [
|
||||
{
|
||||
location: "index",
|
||||
label: "admin.plugins.title",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("default sections are loaded", async function (assert) {
|
||||
await visit("/admin");
|
||||
|
||||
assert.ok(
|
||||
exists(".sidebar-section[data-section-name='admin-nav-section-root']"),
|
||||
"root section is displayed"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".sidebar-section[data-section-name='admin-nav-section-plugins']"),
|
||||
"plugins section is displayed"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".sidebar-section[data-section-name='admin-nav-section-email']"),
|
||||
"email section is displayed"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".sidebar-section[data-section-name='admin-nav-section-logs']"),
|
||||
"logs section is displayed"
|
||||
);
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='admin-nav-section-customize']"
|
||||
),
|
||||
"customize section is displayed"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".sidebar-section[data-section-name='admin-nav-section-api']"),
|
||||
"api section is displayed"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".sidebar-section[data-section-name='admin-nav-section-backups']"),
|
||||
"backups section is displayed"
|
||||
);
|
||||
});
|
||||
|
||||
test("enabled plugin admin routes have links added", async function (assert) {
|
||||
await visit("/admin");
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='admin-nav-section-plugins'] .sidebar-section-link-wrapper[data-list-item-name=\"admin_plugin_index\"]"
|
||||
),
|
||||
"the admin plugin route is added to the plugins section"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Admin Sidebar - Sections - Plugin API", function (needs) {
|
||||
needs.user({
|
||||
admin: true,
|
||||
groups: [AUTO_GROUPS.admins],
|
||||
});
|
||||
|
||||
needs.settings({
|
||||
admin_sidebar_enabled_groups: "1",
|
||||
});
|
||||
|
||||
needs.hooks.beforeEach(() => {
|
||||
withPluginApi("1.24.0", (api) => {
|
||||
api.addAdminSidebarSectionLink("root", {
|
||||
name: "test_section_link",
|
||||
label: "admin.plugins.title",
|
||||
route: "adminPlugins.index",
|
||||
icon: "cog",
|
||||
});
|
||||
|
||||
api.addAdminSidebarSectionLink("root", {
|
||||
name: "test_section_link_no_route_or_href",
|
||||
label: "admin.plugins.title",
|
||||
icon: "cog",
|
||||
});
|
||||
|
||||
api.addAdminSidebarSectionLink("root", {
|
||||
name: "test_section_link_no_label_or_text",
|
||||
route: "adminPlugins.index",
|
||||
icon: "cog",
|
||||
});
|
||||
|
||||
api.addAdminSidebarSectionLink("root", {
|
||||
name: "test_section_link_invalid_label",
|
||||
label: "blahblah.i18n",
|
||||
route: "adminPlugins.index",
|
||||
icon: "cog",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("additional valid links can be added to a section with the plugin API", async function (assert) {
|
||||
await visit("/admin");
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='admin-nav-section-root'] .sidebar-section-link-wrapper[data-list-item-name=\"admin_additional_root_test_section_link\"]"
|
||||
),
|
||||
"link is appended to the root section"
|
||||
);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='admin-nav-section-root'] .sidebar-section-link-wrapper[data-list-item-name=\"admin_additional_root_test_section_link_no_route_or_href\"]"
|
||||
),
|
||||
"invalid link that has no route or href is not appended to the root section"
|
||||
);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='admin-nav-section-root'] .sidebar-section-link-wrapper[data-list-item-name=\"admin_additional_root_test_section_link_no_label_or_text\"]"
|
||||
),
|
||||
"invalid link that has no label or text is not appended to the root section"
|
||||
);
|
||||
|
||||
assert.notOk(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='admin-nav-section-root'] .sidebar-section-link-wrapper[data-list-item-name=\"admin_additional_root_test_section_link_invalid_label\"]"
|
||||
),
|
||||
"invalid link with an invalid I18n key is not appended to the root section"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -28,6 +28,7 @@ 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";
|
||||
|
@ -240,6 +241,7 @@ export function testCleanup(container, app) {
|
|||
clearBulkButtons();
|
||||
resetBeforeAuthCompleteCallbacks();
|
||||
clearPopupMenuOptions();
|
||||
clearAdditionalAdminSidebarSectionLinks();
|
||||
}
|
||||
|
||||
function cleanupCssGeneratorTags() {
|
||||
|
|
|
@ -7,7 +7,11 @@ in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.22.0] - 2024-01-03
|
||||
## [1.24.0] - 2024-01-08
|
||||
|
||||
- Added `addAdminSidebarSectionLink` which is used to add a link to a specific admin sidebar section, as a replacement for the `admin-menu` plugin outlet. This only has an effect if the `admin_sidebar_enabled_groups` site setting is in use, which enables the new admin nav sidebar.
|
||||
|
||||
## [1.23.0] - 2024-01-03
|
||||
|
||||
### Added
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user