mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
This feature allows admins to find what they are looking for in the admin interface via a command palette. This replaces the admin sidebar filter as the focus of the Ctrl+/ command, but the sidebar filter can also still be used. Perhaps at some point we may remove it or change the shortcut. The palette presents the following data for filtering: * The admin nav map, which is also used for the sidebar * All site settings * Themes * Components Admins can also filter which items are shown in the palette. This is still **extremely** WIP -- the interface for the palette is not ideal, it's not keyboard accessible, the design needs to be refined, the code needs to be refined, and there are still numerous other considerations here, like: * Do we want to include reports? * We need to include the automatically generated plugin setting pages added by Ted * Do we want to show screenshots for themes and components if available, or stick to icons? * Site setting filters are janky when visitng the same setting page with a different filter, the page is not refreshed
210 lines
6.4 KiB
JavaScript
210 lines
6.4 KiB
JavaScript
import Service, { service } from "@ember/service";
|
|
import { ajax } from "discourse/lib/ajax";
|
|
import escapeRegExp from "discourse/lib/escape-regexp";
|
|
import getURL from "discourse/lib/get-url";
|
|
import PreloadStore from "discourse/lib/preload-store";
|
|
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
|
|
import I18n, { i18n } from "discourse-i18n";
|
|
|
|
const RESULT_TYPES = ["page", "setting", "theme", "component"];
|
|
|
|
export default class AdminPaletteDataSource extends Service {
|
|
@service router;
|
|
@service siteSettings;
|
|
|
|
pageMapItems = [];
|
|
settingMapItems = [];
|
|
themeMapItems = [];
|
|
componentMapItems = [];
|
|
settingPageMap = {
|
|
categories: {},
|
|
areas: {},
|
|
};
|
|
_mapCached = false;
|
|
|
|
buildMap() {
|
|
if (this._mapCached) {
|
|
return;
|
|
}
|
|
ADMIN_NAV_MAP.forEach((mapItem) => {
|
|
mapItem.links.forEach((link) => {
|
|
let url;
|
|
if (link.routeModels) {
|
|
url = this.router.urlFor(link.route, ...link.routeModels);
|
|
} else {
|
|
url = this.router.urlFor(link.route);
|
|
}
|
|
|
|
const mapItemLabel =
|
|
mapItem.text || (mapItem.label ? i18n(mapItem.label) : "");
|
|
const label =
|
|
mapItemLabel +
|
|
(mapItemLabel ? " > " : "") +
|
|
(link.text || (link.label ? i18n(link.label) : ""));
|
|
|
|
if (link.settings_area) {
|
|
this.settingPageMap.areas[link.settings_area] = link.multi_tabbed
|
|
? `${url}/settings`
|
|
: url;
|
|
}
|
|
|
|
if (link.settings_category) {
|
|
this.settingPageMap.categories[link.settings_category] =
|
|
link.multi_tabbed ? `${url}/settings` : url;
|
|
}
|
|
|
|
this.pageMapItems.push({
|
|
label,
|
|
url,
|
|
keywords:
|
|
(link.keywords ? i18n(link.keywords).toLowerCase() : "") +
|
|
" " +
|
|
url +
|
|
" " +
|
|
label.toLowerCase(),
|
|
type: "page",
|
|
icon: link.icon,
|
|
description: link.description ? i18n(link.description) : "",
|
|
});
|
|
});
|
|
});
|
|
|
|
// TODO (martin) Probably hash these with the plugin name as key
|
|
const visiblePlugins = PreloadStore.get("visiblePlugins") || [];
|
|
ajax("/admin/palette/settings.json").then((result) => {
|
|
result.forEach((setting) => {
|
|
// TODO: (martin) Might want to use the sidebar link name for this instead of the
|
|
// plugin category?
|
|
|
|
let rootLabel;
|
|
if (setting.plugin) {
|
|
rootLabel =
|
|
I18n.lookup(
|
|
`admin.site_settings.categories.${setting.plugin.replaceAll(
|
|
"-",
|
|
"_"
|
|
)}`
|
|
) || i18n("admin.plugins.title");
|
|
} else if (setting.primary_area) {
|
|
rootLabel =
|
|
I18n.lookup(`admin.config.${setting.primary_area}.title`) ||
|
|
i18n(`admin.site_settings.categories.${setting.category}`);
|
|
} else {
|
|
rootLabel = i18n(
|
|
`admin.site_settings.categories.${setting.category}`
|
|
);
|
|
}
|
|
const label = rootLabel + " > " + setting.setting;
|
|
|
|
let url;
|
|
if (setting.plugin) {
|
|
const plugin = visiblePlugins.find(
|
|
(visiblePlugin) => visiblePlugin.name === setting.plugin
|
|
);
|
|
if (plugin && plugin.admin_route) {
|
|
url = plugin.admin_route.use_new_show_route
|
|
? this.router.urlFor(
|
|
`adminPlugins.show.settings`,
|
|
plugin.admin_route.location,
|
|
{ queryParams: { filter: setting.setting } }
|
|
)
|
|
: this.router.urlFor(
|
|
`adminPlugins.${plugin.admin_route.location}`
|
|
);
|
|
} else {
|
|
url = getURL(
|
|
`/admin/site_settings/category/all_results?filter=${setting.setting}`
|
|
);
|
|
}
|
|
} else if (this.settingPageMap.areas[setting.primary_area]) {
|
|
url =
|
|
this.settingPageMap.areas[setting.primary_area] +
|
|
`?filter=${setting.setting}`;
|
|
} else if (this.settingPageMap.categories[setting.category]) {
|
|
url =
|
|
this.settingPageMap.categories[setting.category] +
|
|
`?filter=${setting.setting}`;
|
|
} else {
|
|
url = getURL(
|
|
`/admin/site_settings/category/all_results?filter=${setting.setting}`
|
|
);
|
|
}
|
|
|
|
this.settingMapItems.push({
|
|
label,
|
|
description: setting.description,
|
|
url,
|
|
keywords: (
|
|
setting.setting +
|
|
" " +
|
|
setting.setting.split("_").join(" ") +
|
|
" " +
|
|
setting.description +
|
|
" " +
|
|
setting.keywords.join(" ") +
|
|
" " +
|
|
rootLabel
|
|
).toLowerCase(),
|
|
type: "setting",
|
|
icon: "gear",
|
|
});
|
|
});
|
|
});
|
|
ajax("/admin/palette/themes-and-components.json").then((result) => {
|
|
result.forEach((themeOrComponent) => {
|
|
if (themeOrComponent.component) {
|
|
this.componentMapItems.push({
|
|
label: themeOrComponent.name,
|
|
description: themeOrComponent.description,
|
|
url: getURL(`/admin/customize/components/${themeOrComponent.id}`),
|
|
keywords: (
|
|
"component" +
|
|
" " +
|
|
themeOrComponent.description +
|
|
" " +
|
|
themeOrComponent.name
|
|
).toLowerCase(),
|
|
type: "component",
|
|
icon: "puzzle-piece",
|
|
});
|
|
} else {
|
|
this.themeMapItems.push({
|
|
label: themeOrComponent.name,
|
|
description: themeOrComponent.description,
|
|
url: getURL(`/admin/customize/themes/${themeOrComponent.id}`),
|
|
keywords: (
|
|
"theme" +
|
|
" " +
|
|
themeOrComponent.description +
|
|
" " +
|
|
themeOrComponent.name
|
|
).toLowerCase(),
|
|
type: "theme",
|
|
icon: "paintbrush",
|
|
});
|
|
}
|
|
});
|
|
});
|
|
this._mapCached = true;
|
|
}
|
|
|
|
search(filter, opts = {}) {
|
|
if (filter.length < 2) {
|
|
return [];
|
|
}
|
|
opts.types = opts.types || RESULT_TYPES;
|
|
const filteredResults = [];
|
|
const escapedFilterRegExp = escapeRegExp(filter.toLowerCase());
|
|
|
|
opts.types.forEach((type) => {
|
|
this[`${type}MapItems`].forEach((mapItem) => {
|
|
if (mapItem.keywords.match(escapedFilterRegExp)) {
|
|
filteredResults.push(mapItem);
|
|
}
|
|
});
|
|
});
|
|
|
|
return filteredResults;
|
|
}
|
|
}
|