mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 22:50:45 +08:00
FEATURE: filter additional keywords for the sidebar (#26148)
With the new admin sidebar restructure, we have a link to "Installed plugins". We would like to ensure that when the admin is searching for a plugin name like "akismet" or "automation" this link will be visible. Also when entering the plugins page, related plugins should be highlighted.
This commit is contained in:
parent
bbb18fa2ce
commit
9afb0b29f8
|
@ -12,7 +12,7 @@ import { ADMIN_PANEL } from "discourse/lib/sidebar/panels";
|
|||
|
||||
// TODO (martin) (2024-02-01) Remove this experimental UI.
|
||||
export default class AdminConfigAreaSidebarExperiment extends Component {
|
||||
@service adminSidebarExperimentStateManager;
|
||||
@service adminSidebarStateManager;
|
||||
@service toasts;
|
||||
@service router;
|
||||
@tracked editedNavConfig;
|
||||
|
@ -46,7 +46,7 @@ export default class AdminConfigAreaSidebarExperiment extends Component {
|
|||
|
||||
@action
|
||||
loadDefaultNavConfig() {
|
||||
const savedConfig = this.adminSidebarExperimentStateManager.navConfig;
|
||||
const savedConfig = this.adminSidebarStateManager.navConfig;
|
||||
this.editedNavConfig = savedConfig
|
||||
? JSON.stringify(savedConfig, null, 2)
|
||||
: this.defaultAdminNav;
|
||||
|
@ -116,7 +116,7 @@ export default class AdminConfigAreaSidebarExperiment extends Component {
|
|||
}
|
||||
|
||||
#saveConfig(config) {
|
||||
this.adminSidebarExperimentStateManager.navConfig = config;
|
||||
this.adminSidebarStateManager.navConfig = config;
|
||||
resetPanelSections(
|
||||
ADMIN_PANEL,
|
||||
useAdminNavConfig(config),
|
||||
|
|
|
@ -14,6 +14,7 @@ import PluginCommitHash from "./plugin-commit-hash";
|
|||
export default class AdminPluginsListItem extends Component {
|
||||
@service session;
|
||||
@service currentUser;
|
||||
@service sidebarState;
|
||||
|
||||
@action
|
||||
async togglePluginEnabled(plugin) {
|
||||
|
@ -30,9 +31,22 @@ export default class AdminPluginsListItem extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
get isAdminSearchFiltered() {
|
||||
if (!this.sidebarState.filter) {
|
||||
return false;
|
||||
}
|
||||
return this.args.plugin.nameTitleizedLower.match(this.sidebarState.filter);
|
||||
}
|
||||
|
||||
<template>
|
||||
<tr data-plugin-name={{@plugin.name}}>
|
||||
<td class="admin-plugins-list__row">
|
||||
<tr
|
||||
data-plugin-name={{@plugin.name}}
|
||||
class={{concat
|
||||
"admin-plugins-list__row"
|
||||
(if this.isAdminSearchFiltered "-admin-search-filtered")
|
||||
}}
|
||||
>
|
||||
<td class="admin-plugins-list__name-details">
|
||||
<div class="admin-plugins-list__name-with-badges">
|
||||
<div class="admin-plugins-list__name">
|
||||
{{#if @plugin.linkUrl}}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import { cached, tracked } from "@glimmer/tracking";
|
||||
import { capitalize } from "@ember/string";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
|
@ -53,6 +53,7 @@ export default class AdminPlugin {
|
|||
return "plugins";
|
||||
}
|
||||
|
||||
@cached
|
||||
get nameTitleized() {
|
||||
// The category name is better in a lot of cases, as it's a human-inputted
|
||||
// translation, and we can handle things like SAML instead of showing them
|
||||
|
@ -79,6 +80,11 @@ export default class AdminPlugin {
|
|||
return name;
|
||||
}
|
||||
|
||||
@cached
|
||||
get nameTitleizedLower() {
|
||||
return this.nameTitleized.toLowerCase();
|
||||
}
|
||||
|
||||
get author() {
|
||||
if (this.isOfficial || this.isDiscourseOwned) {
|
||||
return I18n.t("admin.plugins.author", { author: "Discourse" });
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import { service } from "@ember/service";
|
||||
import PreloadStore from "discourse/lib/preload-store";
|
||||
import { ADMIN_PANEL, MAIN_PANEL } from "discourse/lib/sidebar/panels";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import I18n from "discourse-i18n";
|
||||
|
@ -7,7 +8,9 @@ import I18n from "discourse-i18n";
|
|||
export default class AdminRoute extends DiscourseRoute {
|
||||
@service sidebarState;
|
||||
@service siteSettings;
|
||||
@service store;
|
||||
@service currentUser;
|
||||
@service adminSidebarStateManager;
|
||||
@tracked initialSidebarState;
|
||||
|
||||
titleToken() {
|
||||
|
@ -24,6 +27,13 @@ export default class AdminRoute extends DiscourseRoute {
|
|||
this.controllerFor("application").setProperties({
|
||||
showTop: false,
|
||||
});
|
||||
|
||||
const visiblePlugins = PreloadStore.get("visiblePlugins");
|
||||
if (visiblePlugins) {
|
||||
this.adminSidebarStateManager.keywords.admin_installed_plugins = {
|
||||
navigation: visiblePlugins.mapBy("name"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
deactivate(transition) {
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import Service from "@ember/service";
|
||||
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
||||
import KeyValueStore from "discourse/lib/key-value-store";
|
||||
|
||||
export default class AdminSidebarExperimentStateManager extends Service {
|
||||
export default class AdminSidebarStateManager extends Service {
|
||||
@tracked keywords = new TrackedObject();
|
||||
STORE_NAMESPACE = "discourse_admin_sidebar_experiment_";
|
||||
|
||||
store = new KeyValueStore(this.STORE_NAMESPACE);
|
|
@ -27,8 +27,14 @@ export default class SidebarApiSection extends Component {
|
|||
if (this.section.text.toLowerCase().match(this.sidebarState.filter)) {
|
||||
return this.section.links;
|
||||
}
|
||||
|
||||
return this.section.links.filter((link) => {
|
||||
return link.text.toString().toLowerCase().match(this.sidebarState.filter);
|
||||
return (
|
||||
link.text.toString().toLowerCase().match(this.sidebarState.filter) ||
|
||||
link.keywords.navigation.some((keyword) =>
|
||||
keyword.match(this.sidebarState.filter)
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,11 @@ export function clearAdditionalAdminSidebarSectionLinks() {
|
|||
}
|
||||
|
||||
class SidebarAdminSectionLink extends BaseCustomSidebarSectionLink {
|
||||
constructor({ adminSidebarNavLink, router }) {
|
||||
constructor({ adminSidebarNavLink, adminSidebarStateManager, router }) {
|
||||
super(...arguments);
|
||||
this.router = router;
|
||||
this.adminSidebarNavLink = adminSidebarNavLink;
|
||||
this.adminSidebarStateManager = adminSidebarStateManager;
|
||||
}
|
||||
|
||||
get name() {
|
||||
|
@ -80,14 +81,26 @@ class SidebarAdminSectionLink extends BaseCustomSidebarSectionLink {
|
|||
|
||||
return this.adminSidebarNavLink.route;
|
||||
}
|
||||
get keywords() {
|
||||
return (
|
||||
this.adminSidebarStateManager.keywords[this.adminSidebarNavLink.name] || {
|
||||
navigation: [],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function defineAdminSection(adminNavSectionData, router) {
|
||||
function defineAdminSection(
|
||||
adminNavSectionData,
|
||||
adminSidebarStateManager,
|
||||
router
|
||||
) {
|
||||
const AdminNavSection = class extends BaseCustomSidebarSection {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.adminNavSectionData = adminNavSectionData;
|
||||
this.hideSectionHeader = adminNavSectionData.hideSectionHeader;
|
||||
this.adminSidebarStateManager = adminSidebarStateManager;
|
||||
}
|
||||
|
||||
get sectionLinks() {
|
||||
|
@ -113,6 +126,7 @@ function defineAdminSection(adminNavSectionData, router) {
|
|||
(sectionLinkData) =>
|
||||
new SidebarAdminSectionLink({
|
||||
adminSidebarNavLink: sectionLinkData,
|
||||
adminSidebarStateManager: this.adminSidebarStateManager,
|
||||
router,
|
||||
})
|
||||
);
|
||||
|
@ -198,21 +212,21 @@ export function addAdminSidebarSectionLink(sectionName, link) {
|
|||
}
|
||||
|
||||
function pluginAdminRouteLinks() {
|
||||
return (PreloadStore.get("enabledPluginAdminRoutes") || []).map(
|
||||
(pluginAdminRoute) => {
|
||||
return (PreloadStore.get("visiblePlugins") || [])
|
||||
.filter((plugin) => plugin.admin_route && plugin.enabled)
|
||||
.map((plugin) => {
|
||||
return {
|
||||
name: `admin_plugin_${pluginAdminRoute.location}`,
|
||||
route: pluginAdminRoute.use_new_show_route
|
||||
? `adminPlugins.show.${pluginAdminRoute.location}`
|
||||
: `adminPlugins.${pluginAdminRoute.location}`,
|
||||
routeModels: pluginAdminRoute.use_new_show_route
|
||||
? [pluginAdminRoute.location]
|
||||
name: `admin_plugin_${plugin.admin_route.location}`,
|
||||
route: plugin.admin_route.use_new_show_route
|
||||
? `adminPlugins.show.${plugin.admin_route.location}`
|
||||
: `adminPlugins.${plugin.admin_route.location}`,
|
||||
routeModels: plugin.admin_route.use_new_show_route
|
||||
? [plugin.admin_route.location]
|
||||
: [],
|
||||
label: pluginAdminRoute.label,
|
||||
label: plugin.admin_route.label,
|
||||
icon: "cog",
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
|
||||
|
@ -233,11 +247,11 @@ export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
|
|||
return [];
|
||||
}
|
||||
|
||||
this.adminSidebarExperimentStateManager = getOwnerWithFallback(this).lookup(
|
||||
"service:admin-sidebar-experiment-state-manager"
|
||||
this.adminSidebarStateManager = getOwnerWithFallback(this).lookup(
|
||||
"service:admin-sidebar-state-manager"
|
||||
);
|
||||
|
||||
const savedConfig = this.adminSidebarExperimentStateManager.navConfig;
|
||||
const savedConfig = this.adminSidebarStateManager.navConfig;
|
||||
const navMap = savedConfig || ADMIN_NAV_MAP;
|
||||
|
||||
if (!session.get("safe_mode")) {
|
||||
|
@ -253,10 +267,22 @@ export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
|
|||
});
|
||||
}
|
||||
|
||||
navMap.forEach((section) =>
|
||||
section.links.forEach((link) => {
|
||||
if (link.keywords) {
|
||||
this.adminSidebarStateManager.keywords[link.name] = link.keywords;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const navConfig = useAdminNavConfig(navMap);
|
||||
|
||||
return navConfig.map((adminNavSectionData) => {
|
||||
return defineAdminSection(adminNavSectionData, router);
|
||||
return defineAdminSection(
|
||||
adminNavSectionData,
|
||||
this.adminSidebarStateManager,
|
||||
router
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ acceptance("Admin - Plugins", function (needs) {
|
|||
|
||||
assert
|
||||
.dom(
|
||||
"table.admin-plugins-list tr .admin-plugins-list__row .admin-plugins-list__name-with-badges .admin-plugins-list__name"
|
||||
"table.admin-plugins-list .admin-plugins-list__row .admin-plugins-list__name-details .admin-plugins-list__name-with-badges .admin-plugins-list__name"
|
||||
)
|
||||
.hasText("Some Test Plugin", "displays the plugin in the table");
|
||||
|
||||
|
|
|
@ -13,10 +13,14 @@ acceptance("Admin Sidebar - Sections", function (needs) {
|
|||
});
|
||||
|
||||
needs.hooks.beforeEach(() => {
|
||||
PreloadStore.store("enabledPluginAdminRoutes", [
|
||||
PreloadStore.store("visiblePlugins", [
|
||||
{
|
||||
location: "index",
|
||||
label: "admin.plugins.title",
|
||||
name: "plugin title",
|
||||
admin_route: {
|
||||
location: "index",
|
||||
label: "admin.plugins.title",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
@ -83,7 +87,7 @@ acceptance("Admin Sidebar - Sections", function (needs) {
|
|||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='admin-nav-section-plugins'] .sidebar-section-link-wrapper[data-list-item-name=\"admin_plugin_index\"]"
|
||||
".sidebar-section[data-section-name='admin-nav-section-plugins'] .sidebar-section-link-wrapper[data-list-item-name=\"admin_installed_plugins\"]"
|
||||
),
|
||||
"the admin plugin route is added to the plugins section"
|
||||
);
|
||||
|
|
|
@ -9,16 +9,16 @@
|
|||
|
||||
.admin-plugins-list {
|
||||
@media screen and (min-width: 550px) {
|
||||
tr {
|
||||
.admin-plugins-list__row {
|
||||
grid-template-columns: 0.25fr repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
@include breakpoint(mobile-extra-large) {
|
||||
tr {
|
||||
.admin-plugins-list__row {
|
||||
grid-template-columns: 0.25fr repeat(3, 1fr);
|
||||
}
|
||||
.admin-plugins-list {
|
||||
&__row {
|
||||
&__name-details {
|
||||
grid-column-start: 2;
|
||||
grid-column-end: -1;
|
||||
}
|
||||
|
@ -41,6 +41,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.admin-plugins-list__row-admin-search-filtered {
|
||||
background-color: var(--primary-low);
|
||||
}
|
||||
|
||||
&__author {
|
||||
font-size: var(--font-down-2);
|
||||
padding: 0 0 0.25em 0;
|
||||
|
|
|
@ -673,8 +673,18 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
# Used to show plugin-specific admin routes in the sidebar.
|
||||
store_preloaded(
|
||||
"enabledPluginAdminRoutes",
|
||||
MultiJson.dump(Discourse.plugins_sorted_by_name.filter_map(&:admin_route)),
|
||||
"visiblePlugins",
|
||||
MultiJson.dump(
|
||||
Discourse
|
||||
.plugins_sorted_by_name(enabled_only: false)
|
||||
.map do |plugin|
|
||||
{
|
||||
name: plugin.name.downcase,
|
||||
admin_route: plugin.admin_route,
|
||||
enabled: plugin.enabled?,
|
||||
}
|
||||
end,
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,20 +18,28 @@ RSpec.describe ApplicationController do
|
|||
end
|
||||
|
||||
context "when user is admin" do
|
||||
it "has correctly loaded preloaded data for enabledPluginAdminRoutes" do
|
||||
it "has correctly loaded preloaded data for visiblePlugins" do
|
||||
sign_in(admin)
|
||||
get "/latest"
|
||||
expect(JSON.parse(preloaded_json["enabledPluginAdminRoutes"])).to include(
|
||||
{ "label" => "chat.admin.title", "location" => "chat", "use_new_show_route" => false },
|
||||
expect(JSON.parse(preloaded_json["visiblePlugins"])).to include(
|
||||
{
|
||||
"name" => "chat",
|
||||
"admin_route" => {
|
||||
"label" => "chat.admin.title",
|
||||
"location" => "chat",
|
||||
"use_new_show_route" => false,
|
||||
},
|
||||
"enabled" => true,
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user is not admin" do
|
||||
it "does not include preloaded data for enabledPluginAdminRoutes" do
|
||||
it "does not include preloaded data for visiblePlugins" do
|
||||
sign_in(user)
|
||||
get "/latest"
|
||||
expect(preloaded_json["enabledPluginAdminRoutes"]).to eq(nil)
|
||||
expect(preloaded_json["visiblePlugins"]).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1295,7 +1295,7 @@ RSpec.describe ApplicationController do
|
|||
"topicTrackingStates",
|
||||
"topicTrackingStateMeta",
|
||||
"fontMap",
|
||||
"enabledPluginAdminRoutes",
|
||||
"visiblePlugins",
|
||||
],
|
||||
)
|
||||
end
|
||||
|
@ -1309,9 +1309,9 @@ RSpec.describe ApplicationController do
|
|||
)
|
||||
end
|
||||
|
||||
it "has correctly loaded enabledPluginAdminRoutes" do
|
||||
it "has correctly loaded visiblePlugins" do
|
||||
get "/latest"
|
||||
expect(JSON.parse(preloaded_json["enabledPluginAdminRoutes"])).to eq([])
|
||||
expect(JSON.parse(preloaded_json["visiblePlugins"])).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,9 @@ describe "Admin Plugins List", type: :system, js: true do
|
|||
visit "/admin/plugins"
|
||||
|
||||
plugin_row =
|
||||
find(".admin-plugins-list tr[data-plugin-name=\"spoiler-alert\"] td.admin-plugins-list__row")
|
||||
find(
|
||||
".admin-plugins-list tr[data-plugin-name=\"spoiler-alert\"] td.admin-plugins-list__name-details",
|
||||
)
|
||||
expect(plugin_row).to have_css(
|
||||
".admin-plugins-list__name-with-badges .admin-plugins-list__name",
|
||||
text: "Spoiler Alert",
|
||||
|
|
|
@ -92,4 +92,17 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do
|
|||
expect(links.count).to eq(3)
|
||||
expect(links.map(&:text)).to eq(["Appearance", "Preview Summary", "Server Setup"])
|
||||
end
|
||||
|
||||
it "accepts hidden keywords like installed plugin names for filter" do
|
||||
Discourse.instance_variable_set(
|
||||
"@plugins",
|
||||
Plugin::Instance.find_all("#{Rails.root}/spec/fixtures/plugins"),
|
||||
)
|
||||
|
||||
visit("/admin")
|
||||
filter.filter("csp_extension")
|
||||
links = page.all(".sidebar-section-link-content-text")
|
||||
expect(links.count).to eq(1)
|
||||
expect(links.map(&:text)).to eq(["Installed"])
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user