mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 22:43:33 +08:00
FEATURE: Initial version of experimental admin search (#31299)
This feature allows admins to find what they are looking for in the admin interface via a search modal. 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 search modal presents the following data for filtering: * A list of all admin pages, the same as the sidebar, except also showing "third level" pages like "Email > Skipped" * All site settings * Themes * Components * Reports Admins can also filter which types of items are shown in the modal, for example hiding Settings if they know they are looking for a Page. In this PR, I also have the following fixes: * Site setting filters now clear when moving between filtered site setting pages, previously it was super sticky from Ember * Many translations were moved around, instead of being in various namespaces for the sidebar links and the admin page titles and descriptions, now everything is under `admin.config` namespace, this makes it way easier to reuse this text for pages, search, and sidebar, and if you change it in one place then it is changed everywhere. --------- Co-authored-by: Ella <ella.estigoy@gmail.com>
This commit is contained in:
@ -16,7 +16,6 @@ export default class AdminAreaSettings extends Component {
|
||||
@service siteSettings;
|
||||
@service router;
|
||||
@tracked settings = [];
|
||||
@tracked filter = "";
|
||||
@tracked loading = false;
|
||||
@tracked showBreadcrumb = this.args.showBreadcrumb ?? true;
|
||||
|
||||
@ -37,7 +36,6 @@ export default class AdminAreaSettings extends Component {
|
||||
@bind
|
||||
async #loadSettings() {
|
||||
this.loading = true;
|
||||
this.filter = this.args.filter;
|
||||
try {
|
||||
const result = await ajax("/admin/config/site_settings.json", {
|
||||
data: {
|
||||
@ -63,6 +61,10 @@ export default class AdminAreaSettings extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
get filter() {
|
||||
return this.args.filter ?? "";
|
||||
}
|
||||
|
||||
@action
|
||||
adminSettingsFilterChangedCallback(filterData) {
|
||||
this.args.adminSettingsFilterChangedCallback(filterData.filter);
|
||||
|
@ -0,0 +1,27 @@
|
||||
import { concat, fn, get } from "@ember/helper";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
const AdminSearchFilters = <template>
|
||||
<div class="admin-search__filters">
|
||||
{{#each @types as |type|}}
|
||||
<span class={{concat "admin-search__filter --" type}}>
|
||||
<DButton
|
||||
class={{concatClass
|
||||
"btn-small admin-search__filter-item"
|
||||
(if (get @typeFilters type) "is-active")
|
||||
}}
|
||||
@translatedLabel={{i18n
|
||||
(concat "admin.search.result_types." type)
|
||||
count=2
|
||||
}}
|
||||
@icon={{if (get @typeFilters type) "check" "far-circle"}}
|
||||
@action={{fn @toggleTypeFilter type}}
|
||||
/>
|
||||
</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>;
|
||||
|
||||
export default AdminSearchFilters;
|
124
app/assets/javascripts/admin/addon/components/admin-search.gjs
Normal file
124
app/assets/javascripts/admin/addon/components/admin-search.gjs
Normal file
@ -0,0 +1,124 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { TrackedObject } from "@ember-compat/tracked-built-ins";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { INPUT_DELAY } from "discourse/lib/environment";
|
||||
import autoFocus from "discourse/modifiers/auto-focus";
|
||||
import AdminSearchFilters from "admin/components/admin-search-filters";
|
||||
import { RESULT_TYPES } from "admin/services/admin-search-data-source";
|
||||
|
||||
export default class AdminSearch extends Component {
|
||||
@service adminSearchDataSource;
|
||||
|
||||
@tracked filter = "";
|
||||
@tracked searchResults = [];
|
||||
@tracked showFilters = false;
|
||||
@tracked loading = false;
|
||||
typeFilters = new TrackedObject({
|
||||
page: true,
|
||||
setting: true,
|
||||
theme: true,
|
||||
component: true,
|
||||
report: true,
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.adminSearchDataSource.buildMap();
|
||||
}
|
||||
|
||||
get visibleTypes() {
|
||||
return Object.keys(this.typeFilters).filter(
|
||||
(type) => this.typeFilters[type]
|
||||
);
|
||||
}
|
||||
|
||||
get showLoadingSpinner() {
|
||||
return !this.adminSearchDataSource.isLoaded || this.loading;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleFilters() {
|
||||
this.showFilters = !this.showFilters;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleTypeFilter(type) {
|
||||
this.typeFilters[type] = !this.typeFilters[type];
|
||||
this.search();
|
||||
}
|
||||
|
||||
@action
|
||||
changeSearchTerm(event) {
|
||||
this.searchResults = [];
|
||||
this.filter = event.target.value;
|
||||
this.loading = true;
|
||||
this.search();
|
||||
}
|
||||
|
||||
@action
|
||||
search() {
|
||||
discourseDebounce(this, this.#search, INPUT_DELAY);
|
||||
}
|
||||
|
||||
#search() {
|
||||
this.searchResults = this.adminSearchDataSource.search(this.filter, {
|
||||
types: this.visibleTypes,
|
||||
});
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="admin-search__input-container">
|
||||
<div class="admin-search__input-group">
|
||||
{{icon "magnifying-glass" class="admin-search__input-icon"}}
|
||||
<input
|
||||
type="text"
|
||||
class="admin-search__input-field"
|
||||
{{autoFocus}}
|
||||
{{on "input" this.changeSearchTerm}}
|
||||
/>
|
||||
</div>
|
||||
<DButton class="btn-flat" @icon="filter" @action={{this.toggleFilters}} />
|
||||
</div>
|
||||
|
||||
{{#if this.showFilters}}
|
||||
<AdminSearchFilters
|
||||
@toggleTypeFilter={{this.toggleTypeFilter}}
|
||||
@typeFilters={{this.typeFilters}}
|
||||
@types={{RESULT_TYPES}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
<div class="admin-search__results">
|
||||
<ConditionalLoadingSpinner @condition={{this.showLoadingSpinner}}>
|
||||
{{#each this.searchResults as |result|}}
|
||||
<div class="admin-search__result">
|
||||
<a href={{result.url}}>
|
||||
<div class="admin-search__result-name">
|
||||
{{#if result.icon}}
|
||||
{{icon result.icon}}
|
||||
{{/if}}
|
||||
<span
|
||||
class="admin-search__result-name-label"
|
||||
>{{result.label}}</span>
|
||||
</div>
|
||||
{{#if result.description}}
|
||||
<div class="admin-search__result-description">{{htmlSafe
|
||||
result.description
|
||||
}}</div>
|
||||
{{/if}}
|
||||
</a>
|
||||
</div>
|
||||
{{/each}}
|
||||
</ConditionalLoadingSpinner>
|
||||
</div>
|
||||
</template>
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import DModal from "discourse/components/d-modal";
|
||||
import AdminSearch from "admin/components/admin-search";
|
||||
|
||||
export default class AdminSearchModal extends Component {
|
||||
@service currentUser;
|
||||
|
||||
<template>
|
||||
<DModal
|
||||
@closeModal={{@closeModal}}
|
||||
class="admin-search-modal"
|
||||
@title="admin.search.modal_title"
|
||||
@inline={{@inline}}
|
||||
@hideHeader={{true}}
|
||||
>
|
||||
<AdminSearch />
|
||||
</DModal>
|
||||
</template>
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Controller from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default class AdminAreaSettingsBaseController extends Controller {
|
||||
@tracked filter = "";
|
||||
queryParams = ["filter"];
|
||||
filter = "";
|
||||
queryParams = [
|
||||
{
|
||||
filter: { replace: true },
|
||||
},
|
||||
];
|
||||
|
||||
@action
|
||||
adminSettingsFilterChangedCallback(filter) {
|
||||
this.filter = filter;
|
||||
if (this.filter === filter) {
|
||||
return;
|
||||
}
|
||||
this.set("filter", filter);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ export default class AdminSiteSettingsController extends Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("filter", filterData.filter);
|
||||
|
||||
if (isEmpty(filterData.filter) && !filterData.onlyOverridden) {
|
||||
this.set("visibleSiteSettings", this.allSiteSettings);
|
||||
if (this.categoryNameKey === "all_results") {
|
||||
@ -31,8 +33,6 @@ export default class AdminSiteSettingsController extends Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("filter", filterData.filter);
|
||||
|
||||
const matchesGroupedByCategory = this.siteSettingFilter.filterSettings(
|
||||
filterData.filter,
|
||||
{ onlyOverridden: filterData.onlyOverridden }
|
||||
|
@ -11,6 +11,7 @@ import { ajax } from "discourse/lib/ajax";
|
||||
import { fmt, propertyNotEqual } from "discourse/lib/computed";
|
||||
import { SITE_SETTING_REQUIRES_CONFIRMATION_TYPES } from "discourse/lib/constants";
|
||||
import { deepEqual } from "discourse/lib/object";
|
||||
import { humanizedSettingName } from "discourse/lib/site-settings-utils";
|
||||
import { splitString } from "discourse/lib/utilities";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import SiteSettingDefaultCategoriesModal from "../components/modal/site-setting-default-categories";
|
||||
@ -82,79 +83,6 @@ const DEFAULT_USER_PREFERENCES = [
|
||||
"default_sidebar_show_count_of_new_items",
|
||||
];
|
||||
|
||||
const ACRONYMS = new Set([
|
||||
"acl",
|
||||
"ai",
|
||||
"api",
|
||||
"bg",
|
||||
"cdn",
|
||||
"cors",
|
||||
"cta",
|
||||
"dm",
|
||||
"eu",
|
||||
"faq",
|
||||
"fg",
|
||||
"ga",
|
||||
"gb",
|
||||
"gtm",
|
||||
"hd",
|
||||
"http",
|
||||
"https",
|
||||
"iam",
|
||||
"id",
|
||||
"imap",
|
||||
"ip",
|
||||
"jpg",
|
||||
"json",
|
||||
"kb",
|
||||
"mb",
|
||||
"oidc",
|
||||
"pm",
|
||||
"png",
|
||||
"pop3",
|
||||
"s3",
|
||||
"smtp",
|
||||
"svg",
|
||||
"tl",
|
||||
"tl0",
|
||||
"tl1",
|
||||
"tl2",
|
||||
"tl3",
|
||||
"tl4",
|
||||
"tld",
|
||||
"txt",
|
||||
"url",
|
||||
"ux",
|
||||
]);
|
||||
|
||||
const MIXED_CASE = [
|
||||
["adobe analytics", "Adobe Analytics"],
|
||||
["android", "Android"],
|
||||
["chinese", "Chinese"],
|
||||
["discord", "Discord"],
|
||||
["discourse", "Discourse"],
|
||||
["discourse connect", "Discourse Connect"],
|
||||
["discourse discover", "Discourse Discover"],
|
||||
["discourse narrative bot", "Discourse Narrative Bot"],
|
||||
["facebook", "Facebook"],
|
||||
["github", "GitHub"],
|
||||
["google", "Google"],
|
||||
["gravatar", "Gravatar"],
|
||||
["gravatars", "Gravatars"],
|
||||
["ios", "iOS"],
|
||||
["japanese", "Japanese"],
|
||||
["linkedin", "LinkedIn"],
|
||||
["oauth2", "OAuth2"],
|
||||
["opengraph", "OpenGraph"],
|
||||
["powered by discourse", "Powered by Discourse"],
|
||||
["tiktok", "TikTok"],
|
||||
["tos", "ToS"],
|
||||
["twitter", "Twitter"],
|
||||
["vimeo", "Vimeo"],
|
||||
["wordpress", "WordPress"],
|
||||
["youtube", "YouTube"],
|
||||
];
|
||||
|
||||
export default Mixin.create({
|
||||
modal: service(),
|
||||
router: service(),
|
||||
@ -215,29 +143,7 @@ export default Mixin.create({
|
||||
}),
|
||||
|
||||
settingName: computed("setting.setting", "setting.label", function () {
|
||||
const setting = this.setting?.setting;
|
||||
const label = this.setting?.label;
|
||||
const name = label || setting.replace(/\_/g, " ");
|
||||
|
||||
const formattedName = (name.charAt(0).toUpperCase() + name.slice(1))
|
||||
.split(" ")
|
||||
.map((word) =>
|
||||
ACRONYMS.has(word.toLowerCase()) ? word.toUpperCase() : word
|
||||
)
|
||||
.map((word) => {
|
||||
if (word.endsWith("s")) {
|
||||
const singular = word.slice(0, -1).toLowerCase();
|
||||
return ACRONYMS.has(singular) ? singular.toUpperCase() + "s" : word;
|
||||
}
|
||||
return word;
|
||||
})
|
||||
.join(" ");
|
||||
|
||||
return MIXED_CASE.reduce(
|
||||
(acc, [key, value]) =>
|
||||
acc.replaceAll(new RegExp(`\\b${key}\\b`, "gi"), value),
|
||||
formattedName
|
||||
);
|
||||
return humanizedSettingName(this.setting.setting, this.setting.label);
|
||||
}),
|
||||
|
||||
componentType: computed("type", function () {
|
||||
|
@ -4,7 +4,7 @@ import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class AdminConfigAboutRoute extends DiscourseRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.community.sidebar_link.about_your_site");
|
||||
return i18n("admin.config.about.title");
|
||||
}
|
||||
|
||||
model() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigDeveloperRoute extends DiscourseRoute {
|
||||
export default class AdminConfigDeveloperRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.developer");
|
||||
return i18n("admin.config.developer.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigExperimentalRoute extends DiscourseRoute {
|
||||
export default class AdminConfigExperimentalRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.experimental");
|
||||
return i18n("admin.config.experimental.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigFilesRoute extends DiscourseRoute {
|
||||
export default class AdminConfigFilesRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.files");
|
||||
return i18n("admin.config.files.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class AdminConfigFlagsIndexRoute extends DiscourseRoute {
|
||||
export default class AdminConfigFlagsSettingsRoute extends DiscourseRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.config_areas.flags.settings");
|
||||
}
|
||||
|
||||
resetController(controller, isExiting) {
|
||||
if (isExiting) {
|
||||
controller.set("filter", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigFontsRoute extends DiscourseRoute {
|
||||
export default class AdminConfigFontsRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.appearance.sidebar_link.font_style");
|
||||
return i18n("admin.config.font_style.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigGroupPermissionsRoute extends DiscourseRoute {
|
||||
export default class AdminConfigGroupPermissionsRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.community.sidebar_link.group_permissions");
|
||||
return i18n("admin.config.group_permissions.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigLegalRoute extends DiscourseRoute {
|
||||
export default class AdminConfigLegalRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.community.sidebar_link.legal");
|
||||
return i18n("admin.config.legal.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigLocalizationRoute extends DiscourseRoute {
|
||||
export default class AdminConfigLocalizationRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.community.sidebar_link.localization.title");
|
||||
return i18n("admin.config.localization.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigLoginAndAuthenticationRoute extends DiscourseRoute {
|
||||
export default class AdminConfigLoginAndAuthenticationRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.community.sidebar_link.login_and_authentication");
|
||||
return i18n("admin.config.login_and_authentication.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigLogoRoute extends DiscourseRoute {
|
||||
export default class AdminConfigLogoRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.appearance.sidebar_link.site_logo");
|
||||
return i18n("admin.config.logo.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigLookAndFeelIndexRoute extends DiscourseRoute {
|
||||
export default class AdminConfigLookAndFeelIndexRoute extends AdminConfigWithSettingsRoute {
|
||||
@service router;
|
||||
|
||||
beforeModel() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigNavigationRoute extends DiscourseRoute {
|
||||
export default class AdminConfigNavigationRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.appearance.sidebar_link.navigation");
|
||||
return i18n("admin.config.navigation.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigNotificationsRoute extends DiscourseRoute {
|
||||
export default class AdminConfigNotificationsRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.community.sidebar_link.notifications");
|
||||
return i18n("admin.config.notifications.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigOneboxRoute extends DiscourseRoute {
|
||||
export default class AdminConfigOneboxRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.onebox");
|
||||
return i18n("admin.config.onebox.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigOtherRoute extends DiscourseRoute {
|
||||
export default class AdminConfigOtherRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.other_options");
|
||||
return i18n("admin.config.other.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigRateLimitsRoute extends DiscourseRoute {
|
||||
export default class AdminConfigRateLimitsRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.rate_limits");
|
||||
return i18n("admin.config.rate_limits.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigSearchRoute extends DiscourseRoute {
|
||||
export default class AdminConfigSearchRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.search");
|
||||
return i18n("admin.config.search.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigSecurityRoute extends DiscourseRoute {
|
||||
export default class AdminConfigSecurityRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.security.sidebar_link.security");
|
||||
return i18n("admin.config.security.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigSpamRoute extends DiscourseRoute {
|
||||
export default class AdminConfigSpamRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.security.sidebar_link.spam");
|
||||
return i18n("admin.config.spam.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigTrustLevelsRoute extends DiscourseRoute {
|
||||
export default class AdminConfigTrustLevelsRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.community.sidebar_link.trust_levels");
|
||||
return i18n("admin.config.trust_levels.title");
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
|
||||
|
||||
export default class AdminConfigUserApiRoute extends DiscourseRoute {
|
||||
export default class AdminConfigUserApiRoute extends AdminConfigWithSettingsRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.advanced.sidebar_link.user_api");
|
||||
return i18n("admin.config.user_api.title");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class AdminConfigWithSettingsRoute extends DiscourseRoute {
|
||||
resetController(controller, isExiting) {
|
||||
// Have to do this because this is the parent route. We don't want to have
|
||||
// to make a controller for every single settings route when we can reset
|
||||
// the filter here.
|
||||
const settingsController = this.controllerFor(
|
||||
`${this.fullRouteName}.settings`
|
||||
);
|
||||
if (isExiting) {
|
||||
settingsController.set("filter", "");
|
||||
}
|
||||
}
|
||||
}
|
@ -20,4 +20,10 @@ export default class AdminPluginsShowSettingsRoute extends DiscourseRoute {
|
||||
titleToken() {
|
||||
return i18n("admin.plugins.change_settings_short");
|
||||
}
|
||||
|
||||
resetController(controller, isExiting) {
|
||||
if (isExiting) {
|
||||
controller.set("filter", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +25,10 @@ export default class AdminSiteSettingsRoute extends DiscourseRoute {
|
||||
this.controllerFor("adminSiteSettings").set("model", settings);
|
||||
});
|
||||
}
|
||||
|
||||
resetController(controller, isExiting) {
|
||||
if (isExiting) {
|
||||
controller.set("filter", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { service } from "@ember/service";
|
||||
import KeyboardShortcuts, {
|
||||
PLATFORM_KEY_MODIFIER,
|
||||
} from "discourse/lib/keyboard-shortcuts";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import AdminSearchModal from "admin/components/modal/admin-search";
|
||||
|
||||
export default class AdminRoute extends DiscourseRoute {
|
||||
@service sidebarState;
|
||||
@ -9,6 +13,7 @@ export default class AdminRoute extends DiscourseRoute {
|
||||
@service store;
|
||||
@service currentUser;
|
||||
@service adminSidebarStateManager;
|
||||
@service modal;
|
||||
@tracked initialSidebarState;
|
||||
|
||||
titleToken() {
|
||||
@ -16,6 +21,16 @@ export default class AdminRoute extends DiscourseRoute {
|
||||
}
|
||||
|
||||
activate() {
|
||||
if (this.currentUser.use_experimental_admin_search) {
|
||||
KeyboardShortcuts.addShortcut(
|
||||
`${PLATFORM_KEY_MODIFIER}+/`,
|
||||
() => this.showAdminSearchModal(),
|
||||
{
|
||||
global: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this.adminSidebarStateManager.maybeForceAdminSidebar({
|
||||
onlyIfAlreadyActive: false,
|
||||
});
|
||||
@ -28,10 +43,20 @@ export default class AdminRoute extends DiscourseRoute {
|
||||
deactivate(transition) {
|
||||
this.controllerFor("application").set("showTop", true);
|
||||
|
||||
if (this.currentUser.use_experimental_admin_search) {
|
||||
KeyboardShortcuts.unbind({
|
||||
[`${PLATFORM_KEY_MODIFIER}+/`]: this.showAdminSearchModal,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.adminSidebarStateManager.currentUserUsingAdminSidebar) {
|
||||
if (!transition?.to.name.startsWith("admin")) {
|
||||
this.adminSidebarStateManager.stopForcingAdminSidebar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showAdminSearchModal() {
|
||||
this.modal.show(AdminSearchModal);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,320 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Service, { service } from "@ember/service";
|
||||
import { adminRouteValid } from "discourse/lib/admin-utilities";
|
||||
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 { humanizedSettingName } from "discourse/lib/site-settings-utils";
|
||||
import I18n, { i18n } from "discourse-i18n";
|
||||
|
||||
// TODO (martin) Move this to javascript.rake constants, use on server too
|
||||
export const RESULT_TYPES = ["page", "setting", "theme", "component", "report"];
|
||||
const SEPARATOR = ">";
|
||||
const MIN_FILTER_LENGTH = 2;
|
||||
const MAX_TYPE_RESULT_COUNT_LOW = 15;
|
||||
const MAX_TYPE_RESULT_COUNT_HIGH = 50;
|
||||
|
||||
export default class AdminSearchDataSource extends Service {
|
||||
@service router;
|
||||
@service siteSettings;
|
||||
|
||||
plugins = {};
|
||||
pageMapItems = [];
|
||||
settingMapItems = [];
|
||||
themeMapItems = [];
|
||||
componentMapItems = [];
|
||||
reportMapItems = [];
|
||||
settingPageMap = {
|
||||
categories: {},
|
||||
areas: {},
|
||||
};
|
||||
@tracked _mapCached = false;
|
||||
|
||||
get isLoaded() {
|
||||
return this._mapCached;
|
||||
}
|
||||
|
||||
async buildMap() {
|
||||
if (this._mapCached) {
|
||||
return;
|
||||
}
|
||||
|
||||
ADMIN_NAV_MAP.forEach((mapItem) => {
|
||||
mapItem.links.forEach((link) => {
|
||||
let parentLabel = this.#addPageLink(mapItem, link);
|
||||
|
||||
link.links?.forEach((subLink) => {
|
||||
this.#addPageLink(mapItem, subLink, parentLabel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// TODO (martin) Handle plugin enabling/disabling via MessageBus for this
|
||||
// and the setting list?
|
||||
(PreloadStore.get("visiblePlugins") || {}).forEach((plugin) => {
|
||||
if (
|
||||
plugin.admin_route &&
|
||||
plugin.enabled &&
|
||||
adminRouteValid(this.router, plugin.admin_route)
|
||||
) {
|
||||
this.plugins[plugin.name] = plugin;
|
||||
}
|
||||
});
|
||||
|
||||
const allItems = await ajax("/admin/search/all.json");
|
||||
this.#processSettings(allItems.settings);
|
||||
this.#processThemesAndComponents(allItems.themes_and_components);
|
||||
|
||||
// TODO (martin) Move this to all.json after refactoring reports controller
|
||||
// into a service.
|
||||
const reportItems = await ajax("/admin/reports.json");
|
||||
this.#processReports(reportItems.reports);
|
||||
|
||||
this._mapCached = true;
|
||||
}
|
||||
|
||||
search(filter, opts = {}) {
|
||||
if (filter.length < MIN_FILTER_LENGTH) {
|
||||
return [];
|
||||
}
|
||||
|
||||
opts.types = opts.types || RESULT_TYPES;
|
||||
|
||||
const filteredResults = [];
|
||||
const escapedFilterRegExp = escapeRegExp(filter.toLowerCase());
|
||||
|
||||
// Pointless to render heaps of settings if the filter is quite low.
|
||||
const perTypeLimit =
|
||||
filter.length < MIN_FILTER_LENGTH + 1
|
||||
? MAX_TYPE_RESULT_COUNT_LOW
|
||||
: MAX_TYPE_RESULT_COUNT_HIGH;
|
||||
|
||||
opts.types.forEach((type) => {
|
||||
let typeItemCount = 0;
|
||||
this[`${type}MapItems`].forEach((mapItem) => {
|
||||
// TODO (martin) There is likely a much better way of doing this matching
|
||||
// that will support fuzzy searches, for now let's go with the most basic thing.
|
||||
if (
|
||||
mapItem.keywords.match(escapedFilterRegExp) &&
|
||||
typeItemCount <= perTypeLimit
|
||||
) {
|
||||
filteredResults.push(mapItem);
|
||||
typeItemCount++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return filteredResults;
|
||||
}
|
||||
|
||||
#addPageLink(mapItem, link, parentLabel = "") {
|
||||
let url;
|
||||
if (link.routeModels) {
|
||||
url = this.router.urlFor(link.route, ...link.routeModels);
|
||||
} else {
|
||||
url = this.router.urlFor(link.route);
|
||||
}
|
||||
|
||||
const mapItemLabel = this.#labelOrText(mapItem);
|
||||
const linkLabel = this.#labelOrText(link);
|
||||
|
||||
let label;
|
||||
if (parentLabel) {
|
||||
label = mapItemLabel;
|
||||
if (mapItemLabel) {
|
||||
label += ` ${SEPARATOR} `;
|
||||
}
|
||||
label += `${parentLabel} ${SEPARATOR} ${linkLabel}`;
|
||||
} else {
|
||||
label = mapItemLabel + (mapItemLabel ? ` ${SEPARATOR} ` : "") + linkLabel;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const linkKeywords = link.keywords
|
||||
? i18n(link.keywords).toLowerCase().replaceAll("|", " ")
|
||||
: "";
|
||||
const linkDescription = link.description
|
||||
? link.description.includes(" ")
|
||||
? link.description
|
||||
: i18n(link.description)
|
||||
: "";
|
||||
|
||||
this.pageMapItems.push({
|
||||
label,
|
||||
url,
|
||||
keywords: this.#buildKeywords(
|
||||
linkKeywords,
|
||||
url,
|
||||
label.replace(SEPARATOR, "").toLowerCase().replace(/ +/g, " "),
|
||||
linkDescription
|
||||
),
|
||||
type: "page",
|
||||
icon: link.icon,
|
||||
description: linkDescription,
|
||||
});
|
||||
|
||||
return linkLabel;
|
||||
}
|
||||
|
||||
#processSettings(settings) {
|
||||
const settingPluginNames = {};
|
||||
|
||||
settings.forEach((setting) => {
|
||||
let plugin;
|
||||
|
||||
let rootLabel;
|
||||
if (setting.plugin) {
|
||||
if (!settingPluginNames[setting.plugin]) {
|
||||
settingPluginNames[setting.plugin] = setting.plugin.replaceAll(
|
||||
"_",
|
||||
"-"
|
||||
);
|
||||
}
|
||||
|
||||
plugin = this.plugins[settingPluginNames[setting.plugin]];
|
||||
|
||||
if (plugin) {
|
||||
rootLabel = plugin.admin_route?.label
|
||||
? i18n(plugin.admin_route?.label)
|
||||
: i18n("admin.plugins.title");
|
||||
} else {
|
||||
rootLabel = 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} ${SEPARATOR} ${humanizedSettingName(
|
||||
setting.setting
|
||||
)}`;
|
||||
|
||||
// TODO (martin) These URLs will need to change eventually to anchors
|
||||
// to focus on a specific element on the page, for now though the filter is fine.
|
||||
let url;
|
||||
if (setting.plugin) {
|
||||
if (plugin) {
|
||||
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: this.#buildKeywords(
|
||||
setting.setting,
|
||||
humanizedSettingName(setting.setting),
|
||||
setting.description,
|
||||
setting.keywords,
|
||||
rootLabel
|
||||
),
|
||||
type: "setting",
|
||||
icon: "gear",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#processThemesAndComponents(themesAndComponents) {
|
||||
themesAndComponents.forEach((themeOrComponent) => {
|
||||
if (themeOrComponent.component) {
|
||||
this.componentMapItems.push({
|
||||
label: themeOrComponent.name,
|
||||
description: themeOrComponent.description,
|
||||
url: getURL(`/admin/customize/components/${themeOrComponent.id}`),
|
||||
keywords: this.#buildKeywords(
|
||||
"component",
|
||||
themeOrComponent.description,
|
||||
themeOrComponent.name
|
||||
),
|
||||
type: "component",
|
||||
icon: "puzzle-piece",
|
||||
});
|
||||
} else {
|
||||
this.themeMapItems.push({
|
||||
label: themeOrComponent.name,
|
||||
description: themeOrComponent.description,
|
||||
url: getURL(`/admin/customize/themes/${themeOrComponent.id}`),
|
||||
keywords: this.#buildKeywords(
|
||||
"theme",
|
||||
themeOrComponent.description,
|
||||
themeOrComponent.name
|
||||
),
|
||||
type: "theme",
|
||||
icon: "paintbrush",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#processReports(reports) {
|
||||
reports.forEach((report) => {
|
||||
this.reportMapItems.push({
|
||||
label: report.title,
|
||||
description: report.description,
|
||||
url: getURL(`/admin/reports/${report.type}`),
|
||||
icon: "chart-bar",
|
||||
keywords: this.#buildKeywords(
|
||||
report.title,
|
||||
report.description,
|
||||
report.type
|
||||
),
|
||||
type: "report",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#labelOrText(obj, fallback = "") {
|
||||
return obj.text || (obj.label ? i18n(obj.label) : fallback);
|
||||
}
|
||||
|
||||
#buildKeywords(...keywords) {
|
||||
return keywords
|
||||
.map((kw) => {
|
||||
if (Array.isArray(kw)) {
|
||||
return kw.join(" ");
|
||||
}
|
||||
return kw;
|
||||
})
|
||||
.join(" ")
|
||||
.toLowerCase();
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
<div class="badges">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.badges.title"}}
|
||||
@descriptionLabel={{i18n "admin.badges.page_description"}}
|
||||
@titleLabel={{i18n "admin.config.badges.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.badges.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/understanding-and-using-badges/32540"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/badges"
|
||||
@label={{i18n "admin.badges.title"}}
|
||||
@label={{i18n "admin.config.badges.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -7,15 +7,15 @@ import { i18n } from "discourse-i18n";
|
||||
export default RouteTemplate(<template>
|
||||
<PluginOutlet @name="admin-api-keys">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.api_keys.title"}}
|
||||
@descriptionLabel={{i18n "admin.api_keys.description"}}
|
||||
@titleLabel={{i18n "admin.config.api_keys.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.api_keys.header_description"}}
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/api/keys"
|
||||
@label={{i18n "admin.api_keys.title"}}
|
||||
@label={{i18n "admin.config.api_keys.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,14 +1,14 @@
|
||||
<div class="admin-backups admin-config-page">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.backups.title"}}
|
||||
@descriptionLabel={{i18n "admin.backups.description"}}
|
||||
@titleLabel={{i18n "admin.config.backups.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.backups.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/create-download-and-restore-a-backup-of-your-discourse-database/122710"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/backups"
|
||||
@label={{i18n "admin.backups.title"}}
|
||||
@label={{i18n "admin.config.backups.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.config_areas.about.header"}}
|
||||
@titleLabel={{i18n "admin.config.about.title"}}
|
||||
@descriptionLabel={{i18n
|
||||
"admin.config_areas.about.description"
|
||||
"admin.config.about.header_description"
|
||||
(hash basePath=(base-path))
|
||||
}}
|
||||
@hideTabs={{true}}
|
||||
@ -11,7 +11,7 @@
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/about"
|
||||
@label={{i18n "admin.config_areas.about.header"}}
|
||||
@label={{i18n "admin.config.about.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.customize.colors.long_title"}}
|
||||
@descriptionLabel={{i18n "admin.customize.colors.description"}}
|
||||
@titleLabel={{i18n "admin.config.color_palettes.long_title"}}
|
||||
@descriptionLabel={{i18n "admin.config.color_palettes.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/allow-users-to-select-new-color-palettes/60857"
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
@ -8,7 +8,7 @@
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/colors"
|
||||
@label={{i18n "admin.customize.colors.long_title"}}
|
||||
@label={{i18n "admin.config.color_palettes.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.customize.email_style.heading"}}
|
||||
@descriptionLabel={{i18n "admin.customize.email_style.instructions"}}
|
||||
@titleLabel={{i18n "admin.config.email_appearance.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.email_appearance.header_description"}}
|
||||
@shouldDisplay={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{#if (eq this.currentTab "themes")}}
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.customize.theme.title"}}
|
||||
@descriptionLabel={{i18n "admin.customize.theme.description"}}
|
||||
@titleLabel={{i18n "admin.config.themes.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.themes.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/91966"
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
@ -9,21 +9,21 @@
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/themes"
|
||||
@label={{i18n "admin.customize.theme.title"}}
|
||||
@label={{i18n "admin.config.themes.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
{{else}}
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.customize.theme.components"}}
|
||||
@descriptionLabel={{i18n "admin.customize.theme.components_description"}}
|
||||
@titleLabel={{i18n "admin.config.components.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.components.header_description"}}
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/components"
|
||||
@label={{i18n "admin.customize.theme.components"}}
|
||||
@label={{i18n "admin.config.components.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
@ -1,4 +1,8 @@
|
||||
<p>{{i18n "admin.email.advanced_test.desc"}}</p>
|
||||
<DPageSubheader
|
||||
@descriptionLabel={{i18n
|
||||
"admin.config.email.sub_pages.advanced_test.header_description"
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="email-advanced-test">
|
||||
<label for="email">{{i18n "admin.email.advanced_test.email"}}</label>
|
||||
|
@ -1,4 +1,8 @@
|
||||
<p>{{i18n "admin.email.preview_digest_desc"}}</p>
|
||||
<DPageSubheader
|
||||
@descriptionLabel={{i18n
|
||||
"admin.config.email.sub_pages.preview_summary.header_description"
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="admin-controls email-preview">
|
||||
<div class="controls">
|
||||
|
@ -1,31 +1,49 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.email.title"}}
|
||||
@descriptionLabel={{i18n "admin.email.description"}}
|
||||
@titleLabel={{i18n "admin.config.email.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.email.header_description"}}
|
||||
@shouldDisplay={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/email"
|
||||
@label={{i18n "admin.email.title"}}
|
||||
@label={{i18n "admin.config.email.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:tabs>
|
||||
<NavItem @route="adminEmail.index" @label="admin.email.settings" />
|
||||
<NavItem @route="adminEmail.index" @label="settings" />
|
||||
<NavItem
|
||||
@route="adminEmail.previewDigest"
|
||||
@label="admin.email.preview_digest"
|
||||
@label="admin.config.email.sub_pages.preview_summary.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmail.advancedTest"
|
||||
@label="admin.email.advanced_test.title"
|
||||
@label="admin.config.email.sub_pages.advanced_test.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmailTemplates"
|
||||
@label="admin.config.email.sub_pages.templates.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmail.sent"
|
||||
@label="admin.config.email.sub_pages.sent.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmail.skipped"
|
||||
@label="admin.config.email.sub_pages.skipped.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmail.bounced"
|
||||
@label="admin.config.email.sub_pages.bounced.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmail.received"
|
||||
@label="admin.config.email.sub_pages.received.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminEmail.rejected"
|
||||
@label="admin.config.email.sub_pages.rejected.title"
|
||||
/>
|
||||
<NavItem @route="adminEmailTemplates" @label="admin.email.templates" />
|
||||
<NavItem @route="adminEmail.sent" @label="admin.email.sent" />
|
||||
<NavItem @route="adminEmail.skipped" @label="admin.email.skipped" />
|
||||
<NavItem @route="adminEmail.bounced" @label="admin.email.bounced" />
|
||||
<NavItem @route="adminEmail.received" @label="admin.email.received" />
|
||||
<NavItem @route="adminEmail.rejected" @label="admin.email.rejected" />
|
||||
</:tabs>
|
||||
</DPageHeader>
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
<div class="admin-embedding admin-config-page">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.embedding.title"}}
|
||||
@descriptionLabel={{i18n "admin.embedding.description"}}
|
||||
@titleLabel={{i18n "admin.config.embedding.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.embedding.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/embed-discourse-comments-on-another-website-via-javascript/31963"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/embedding"
|
||||
@label={{i18n "admin.embedding.title"}}
|
||||
@label={{i18n "admin.config.embedding.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,14 +1,14 @@
|
||||
<div class="admin-emoji admin-config-page">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.emoji.title"}}
|
||||
@descriptionLabel={{i18n "admin.emoji.description"}}
|
||||
@titleLabel={{i18n "admin.config.emoji.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.emoji.header_description"}}
|
||||
@hideTabs={{this.hideTabs}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/emoji"
|
||||
@label={{i18n "admin.emoji.title"}}
|
||||
@label={{i18n "admin.config.emoji.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,32 +1,38 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.logs.title"}}
|
||||
@descriptionLabel={{i18n "admin.logs.description"}}
|
||||
@titleLabel={{i18n "admin.config.staff_action_logs.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.staff_action_logs.header_description"}}
|
||||
@shouldDisplay={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem @path="/admin/logs" @label={{i18n "admin.logs.title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/logs"
|
||||
@label={{i18n "admin.config.staff_action_logs.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:tabs>
|
||||
<NavItem
|
||||
@route="adminLogs.staffActionLogs"
|
||||
@label="admin.logs.staff_actions.title"
|
||||
@label="admin.config.staff_action_logs.title"
|
||||
/>
|
||||
{{#if this.currentUser.can_see_emails}}
|
||||
<NavItem
|
||||
@route="adminLogs.screenedEmails"
|
||||
@label="admin.logs.screened_emails.title"
|
||||
@label="admin.config.staff_action_logs.sub_pages.screened_emails.title"
|
||||
/>
|
||||
{{/if}}
|
||||
<NavItem
|
||||
@route="adminLogs.screenedIpAddresses"
|
||||
@label="admin.logs.screened_ips.title"
|
||||
@label="admin.config.staff_action_logs.sub_pages.screened_ips.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminLogs.screenedUrls"
|
||||
@label="admin.logs.screened_urls.title"
|
||||
@label="admin.config.staff_action_logs.sub_pages.screened_urls.title"
|
||||
/>
|
||||
<NavItem
|
||||
@route="adminSearchLogs"
|
||||
@label="admin.config.staff_action_logs.sub_pages.search_logs.title"
|
||||
/>
|
||||
<NavItem @route="adminSearchLogs" @label="admin.logs.search_logs.title" />
|
||||
{{#if this.currentUser.admin}}
|
||||
<NavItem @path="/logs" @label="admin.logs.logster.title" />
|
||||
{{/if}}
|
||||
|
@ -1,6 +1,8 @@
|
||||
<p>
|
||||
{{i18n "admin.logs.screened_emails.description"}}
|
||||
</p>
|
||||
<DPageSubheader
|
||||
@descriptionLabel={{i18n
|
||||
"admin.config.staff_action_logs.sub_pages.screened_emails.header_description"
|
||||
}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@action={{this.exportScreenedEmailList}}
|
||||
|
@ -1,4 +1,8 @@
|
||||
<p>{{i18n "admin.logs.screened_ips.description"}}</p>
|
||||
<DPageSubheader
|
||||
@descriptionLabel={{i18n
|
||||
"admin.config.staff_action_logs.sub_pages.screened_ips.header_description"
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="screened-ip-controls">
|
||||
<div class="filter-screened-ip-address inline-form">
|
||||
|
@ -1,6 +1,9 @@
|
||||
<p>
|
||||
{{i18n "admin.logs.screened_urls.description"}}
|
||||
</p>
|
||||
<DPageSubheader
|
||||
@descriptionLabel={{i18n
|
||||
"admin.config.staff_action_logs.sub_pages.screened_urls.header_description"
|
||||
}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@action={{this.exportScreenedUrlList}}
|
||||
@title="admin.export_csv.button_title.screened_url"
|
||||
|
@ -1,14 +1,14 @@
|
||||
<div class="admin-permalinks admin-config-page">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.permalink.title"}}
|
||||
@descriptionLabel={{i18n "admin.permalink.description"}}
|
||||
@titleLabel={{i18n "admin.config.permalinks.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.permalinks.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/20930"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/permalinks"
|
||||
@label={{i18n "admin.permalink.title"}}
|
||||
@label={{i18n "admin.config.permalinks.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<div class="admin-plugins-list-container">
|
||||
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.plugins.installed"}}
|
||||
@descriptionLabel={{i18n "admin.plugins.description"}}
|
||||
@titleLabel={{i18n "admin.config.plugins.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.plugins.header_description"}}
|
||||
@learnMoreUrl="https://www.discourse.org/plugins"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/plugins"
|
||||
@label={{i18n "admin.plugins.title"}}
|
||||
@label={{i18n "admin.config.plugins.title"}}
|
||||
/>
|
||||
<div class="d-nav-submenu">
|
||||
<HorizontalOverflowNav class="main-nav nav plugin-nav">
|
||||
|
@ -1,14 +1,14 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.reports.title"}}
|
||||
@descriptionLabel={{i18n "admin.reports.meta_doc"}}
|
||||
@titleLabel={{i18n "admin.config.reports.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.reports.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/-/240233"
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/reports"
|
||||
@label={{i18n "admin.reports.sidebar_title"}}
|
||||
@path="/admin.config.reports"
|
||||
@label={{i18n "admin.config.reports.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.section_landing_pages.account.title"}}
|
||||
@titleLabel={{i18n "admin.config_sections.account.title"}}
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/section/account"
|
||||
@label={{i18n "admin.section_landing_pages.account.title"}}
|
||||
@label={{i18n "admin.config_sections.account.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
@ -14,14 +14,14 @@
|
||||
<AdminSectionLandingWrapper>
|
||||
<AdminSectionLandingItem
|
||||
@icon="box-archive"
|
||||
@titleLabel="admin.section_landing_pages.account.backups.title"
|
||||
@descriptionLabel="admin.section_landing_pages.account.backups.description"
|
||||
@titleLabel="admin.config.backups.title"
|
||||
@descriptionLabel="admin.config.backups.header_description"
|
||||
@titleRoute="admin.backups"
|
||||
/>
|
||||
<AdminSectionLandingItem
|
||||
@icon="gift"
|
||||
@titleLabel="admin.section_landing_pages.account.whats_new.title"
|
||||
@descriptionLabel="admin.section_landing_pages.account.whats_new.description"
|
||||
@titleLabel="admin.config.whats_new.title"
|
||||
@descriptionLabel="admin.config.whats_new.header_description"
|
||||
@titleRoute="admin.whatsNew"
|
||||
/>
|
||||
</AdminSectionLandingWrapper>
|
@ -1,13 +1,13 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.site_settings.title"}}
|
||||
@descriptionLabel={{i18n "admin.site_settings.description"}}
|
||||
@titleLabel={{i18n "admin.config.site_settings.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.site_settings.header_description"}}
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/site_settings"
|
||||
@label={{i18n "admin.site_settings.title"}}
|
||||
@label={{i18n "admin.config.site_settings.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
@ -1,13 +1,13 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.site_text.title"}}
|
||||
@descriptionLabel={{i18n "admin.site_text.description"}}
|
||||
@titleLabel={{i18n "admin.config.site_texts.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.site_texts.header_description"}}
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/site_texts"
|
||||
@label={{i18n "admin.site_text.title"}}
|
||||
@label={{i18n "admin.config.site_texts.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="admin-user_fields admin-config-page">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.user_fields.title"}}
|
||||
@descriptionLabel={{i18n "admin.user_fields.help"}}
|
||||
@titleLabel={{i18n "admin.config.user_fields.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.user_fields.header_description"}}
|
||||
@hideTabs={{true}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/creating-and-configuring-custom-user-fields/113192"
|
||||
>
|
||||
@ -9,7 +9,7 @@
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/config/user-fields"
|
||||
@label={{i18n "admin.user_fields.title"}}
|
||||
@label={{i18n "admin.config.user_fields.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,14 +1,14 @@
|
||||
<div class="admin-users admin-config-page">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.users.title"}}
|
||||
@descriptionLabel={{i18n "admin.users.description"}}
|
||||
@titleLabel={{i18n "admin.config.users.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.users.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/accessing-a-user-s-admin-page/311859"
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/users/list"
|
||||
@label={{i18n "admin.users.title"}}
|
||||
@label={{i18n "admin.config.users.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.watched_words.title"}}
|
||||
@descriptionLabel={{i18n "admin.watched_words.description"}}
|
||||
@titleLabel={{i18n "admin.config.watched_words.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.watched_words.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/t/241735"
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
@ -8,7 +8,7 @@
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/customize/watched_words"
|
||||
@label={{i18n "admin.watched_words.title"}}
|
||||
@label={{i18n "admin.config.watched_words.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
</DPageHeader>
|
||||
|
@ -7,15 +7,15 @@ import { i18n } from "discourse-i18n";
|
||||
export default RouteTemplate(<template>
|
||||
<div class="admin-webhooks admin-config-page">
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.web_hooks.title"}}
|
||||
@descriptionLabel={{i18n "admin.web_hooks.instruction"}}
|
||||
@titleLabel={{i18n "admin.config.webhooks.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.webhooks.header_description"}}
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/api/web_hooks"
|
||||
@label={{i18n "admin.web_hooks.title"}}
|
||||
@label={{i18n "admin.config.webhooks.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<DPageHeader
|
||||
@titleLabel={{i18n "admin.dashboard.new_features.title"}}
|
||||
@descriptionLabel={{i18n "admin.dashboard.new_features.subtitle"}}
|
||||
@titleLabel={{i18n "admin.config.whats_new.title"}}
|
||||
@descriptionLabel={{i18n "admin.config.whats_new.header_description"}}
|
||||
@learnMoreUrl="https://meta.discourse.org/tags/c/announcements/67/release-notes"
|
||||
@hideTabs={{true}}
|
||||
>
|
||||
@ -8,7 +8,7 @@
|
||||
<DBreadcrumbsItem @path="/admin" @label={{i18n "admin_title"}} />
|
||||
<DBreadcrumbsItem
|
||||
@path="/admin/whats-new"
|
||||
@label={{i18n "admin.dashboard.new_features.title"}}
|
||||
@label={{i18n "admin.config.whats_new.title"}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:actions as |actions|>
|
||||
|
@ -21,6 +21,7 @@ export default class DPageSubheader extends Component {
|
||||
<template>
|
||||
<div class="d-page-subheader">
|
||||
<div class="d-page-subheader__title-row">
|
||||
{{#if @titleLabel}}
|
||||
<h2 class="d-page-subheader__title">
|
||||
{{#if @titleUrl}}
|
||||
<a href={{@titleUrl}} class="d-page-subheader__title-link">
|
||||
@ -30,6 +31,7 @@ export default class DPageSubheader extends Component {
|
||||
{{@titleLabel}}
|
||||
{{/if}}
|
||||
</h2>
|
||||
{{/if}}
|
||||
{{#if (has-block "actions")}}
|
||||
<div class="d-page-subheader__actions">
|
||||
{{#if this.site.mobileView}}
|
||||
|
@ -496,8 +496,11 @@ export default {
|
||||
|
||||
if (filterInput) {
|
||||
this._scrollTo(0);
|
||||
|
||||
if (!this.currentUser.use_experimental_admin_search) {
|
||||
filterInput.focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fullscreenComposer() {
|
||||
|
@ -1,24 +1,84 @@
|
||||
export const ADMIN_NAV_MAP = [
|
||||
{
|
||||
text: "",
|
||||
name: "root",
|
||||
hideSectionHeader: true,
|
||||
links: [
|
||||
{
|
||||
name: "admin_home",
|
||||
route: "admin.dashboard.general",
|
||||
label: "admin.config.dashboard.title",
|
||||
description: "admin.config.dashboard.header_description",
|
||||
icon: "house",
|
||||
moderator: true,
|
||||
},
|
||||
{
|
||||
name: "admin_users",
|
||||
route: "adminUsers",
|
||||
label: "admin.config.users.title",
|
||||
description: "admin.config.users.header_description",
|
||||
icon: "users",
|
||||
moderator: true,
|
||||
},
|
||||
{
|
||||
name: "admin_groups",
|
||||
route: "groups",
|
||||
label: "admin.config.groups.title",
|
||||
description: "admin.config.groups.header_description",
|
||||
icon: "user-group",
|
||||
moderator: true,
|
||||
},
|
||||
{
|
||||
name: "admin_all_site_settings",
|
||||
route: "adminSiteSettings",
|
||||
label: "admin.config.site_settings.title",
|
||||
description: "admin.config.site_settings.header_description",
|
||||
icon: "gear",
|
||||
},
|
||||
{
|
||||
name: "admin_whats_new",
|
||||
route: "admin.whatsNew",
|
||||
label: "admin.config.whats_new.title",
|
||||
description: "admin.config.whats_new.header_description",
|
||||
icon: "gift",
|
||||
keywords: "admin.config.whats_new.keywords",
|
||||
moderator: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "account",
|
||||
label: "admin.account.title",
|
||||
label: "admin.config_sections.account.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_backups",
|
||||
route: "admin.backups",
|
||||
label: "admin.account.sidebar_link.backups",
|
||||
label: "admin.config.backups.title",
|
||||
description: "admin.config.backups.header_description",
|
||||
icon: "box-archive",
|
||||
settings_category: "backups",
|
||||
multi_tabbed: true,
|
||||
links: [
|
||||
{
|
||||
name: "admin_backups_logs",
|
||||
route: "admin.backups.logs",
|
||||
label: "admin.config.backups.sub_pages.logs.title",
|
||||
description:
|
||||
"admin.config.backups.sub_pages.logs.header_description",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "reports",
|
||||
label: "admin.reports.sidebar_title",
|
||||
label: "admin.config_sections.reports.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_all_reports",
|
||||
route: "adminReports.index",
|
||||
label: "admin.reports.sidebar_link.all",
|
||||
label: "admin.config.reports.title",
|
||||
description: "admin.config.reports.header_description",
|
||||
icon: "chart-bar",
|
||||
moderator: true,
|
||||
},
|
||||
@ -26,269 +86,425 @@ export const ADMIN_NAV_MAP = [
|
||||
},
|
||||
{
|
||||
name: "community",
|
||||
label: "admin.community.title",
|
||||
label: "admin.config_sections.community.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_about_your_site",
|
||||
route: "adminConfig.about",
|
||||
label: "admin.community.sidebar_link.about_your_site",
|
||||
label: "admin.config.about.title",
|
||||
description: "admin.config.about.header_description",
|
||||
icon: "gear",
|
||||
settings_area: "about",
|
||||
},
|
||||
{
|
||||
name: "admin_badges",
|
||||
route: "adminBadges",
|
||||
label: "admin.community.sidebar_link.badges",
|
||||
label: "admin.config.badges.title",
|
||||
description: "admin.config.badges.header_description",
|
||||
icon: "certificate",
|
||||
},
|
||||
{
|
||||
name: "admin_login_and_authentication",
|
||||
route: "adminConfig.loginAndAuthentication.settings",
|
||||
label: "admin.community.sidebar_link.login_and_authentication",
|
||||
label: "admin.config.login_and_authentication.title",
|
||||
description: "admin.config.login_and_authentication.header_description",
|
||||
icon: "unlock",
|
||||
settings_category: "login",
|
||||
},
|
||||
{
|
||||
name: "admin_notifications",
|
||||
route: "adminConfig.notifications.settings",
|
||||
label: "admin.community.sidebar_link.notifications",
|
||||
label: "admin.config.notifications.title",
|
||||
description: "admin.config.notifications.header_description",
|
||||
icon: "bell",
|
||||
settings_area: "notifications",
|
||||
},
|
||||
{
|
||||
name: "admin_localization",
|
||||
route: "adminConfig.localization.settings",
|
||||
label: "admin.community.sidebar_link.localization.title",
|
||||
label: "admin.config.localization.title",
|
||||
description: "admin.config.localization.header_description",
|
||||
keywords: "admin.config.localization.keywords",
|
||||
icon: "globe",
|
||||
settings_area: "localization",
|
||||
},
|
||||
{
|
||||
name: "admin_permalinks",
|
||||
route: "adminPermalinks",
|
||||
label: "admin.community.sidebar_link.permalinks",
|
||||
label: "admin.config.permalinks.title",
|
||||
description: "admin.config.permalinks.header_description",
|
||||
icon: "link",
|
||||
settings_area: "permalinks",
|
||||
multi_tabbed: true,
|
||||
},
|
||||
{
|
||||
name: "admin_trust_levels",
|
||||
route: "adminConfig.trustLevels.settings",
|
||||
label: "admin.community.sidebar_link.trust_levels",
|
||||
label: "admin.config.trust_levels.title",
|
||||
description: "admin.config.trust_levels.header_description",
|
||||
icon: "user-shield",
|
||||
settings_area: "trust_levels",
|
||||
},
|
||||
{
|
||||
name: "admin_group_permissions",
|
||||
route: "adminConfig.groupPermissions.settings",
|
||||
label: "admin.community.sidebar_link.group_permissions",
|
||||
label: "admin.config.group_permissions.title",
|
||||
description: "admin.config.group_permissions.header_description",
|
||||
icon: "user-gear",
|
||||
settings_area: "group_permissions",
|
||||
},
|
||||
{
|
||||
name: "admin_user_fields",
|
||||
route: "adminUserFields",
|
||||
label: "admin.community.sidebar_link.user_fields",
|
||||
label: "admin.config.user_fields.title",
|
||||
description: "admin.config.user_fields.header_description",
|
||||
icon: "user-pen",
|
||||
},
|
||||
{
|
||||
name: "admin_watched_words",
|
||||
route: "adminWatchedWords",
|
||||
label: "admin.community.sidebar_link.watched_words",
|
||||
label: "admin.config.watched_words.title",
|
||||
description: "admin.config.watched_words.header_description",
|
||||
icon: "eye",
|
||||
moderator: true,
|
||||
},
|
||||
{
|
||||
name: "admin_legal",
|
||||
route: "adminConfig.legal.settings",
|
||||
label: "admin.community.sidebar_link.legal",
|
||||
label: "admin.config.legal.title",
|
||||
description: "admin.config.legal.header_description",
|
||||
icon: "gavel",
|
||||
},
|
||||
{
|
||||
name: "admin_moderation_flags",
|
||||
route: "adminConfig.flags",
|
||||
label: "admin.community.sidebar_link.moderation_flags.title",
|
||||
keywords: "admin.community.sidebar_link.moderation_flags.keywords",
|
||||
label: "admin.config.flags.title",
|
||||
description: "admin.config.flags.header_description",
|
||||
keywords: "admin.config.flags.keywords",
|
||||
icon: "flag",
|
||||
settings_area: "flags",
|
||||
multi_tabbed: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "appearance",
|
||||
label: "admin.appearance.title",
|
||||
label: "admin.config_sections.appearance.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_font_style",
|
||||
route: "adminConfig.fonts.settings",
|
||||
label: "admin.appearance.sidebar_link.font_style",
|
||||
label: "admin.config.font_style.title",
|
||||
description: "admin.config.font_style.header_description",
|
||||
icon: "italic",
|
||||
settings_area: "fonts",
|
||||
},
|
||||
{
|
||||
name: "admin_site_logo",
|
||||
route: "adminConfig.logo.settings",
|
||||
label: "admin.appearance.sidebar_link.site_logo",
|
||||
label: "admin.config.logo.title",
|
||||
description: "admin.config.logo.header_description",
|
||||
icon: "fab-discourse",
|
||||
settings_category: "branding",
|
||||
},
|
||||
{
|
||||
name: "admin_color_schemes",
|
||||
name: "admin_color_palettes",
|
||||
route: "adminCustomize.colors",
|
||||
label: "admin.appearance.sidebar_link.color_schemes",
|
||||
label: "admin.config.color_palettes.title",
|
||||
description: "admin.config.color_palettes.header_description",
|
||||
icon: "palette",
|
||||
},
|
||||
{
|
||||
name: "admin_emoji",
|
||||
route: "adminEmojis",
|
||||
label: "admin.appearance.sidebar_link.emoji",
|
||||
label: "admin.config.emoji.title",
|
||||
description: "admin.config.emoji.header_description",
|
||||
icon: "discourse-emojis",
|
||||
settings_area: "emojis",
|
||||
multi_tabbed: true,
|
||||
},
|
||||
{
|
||||
name: "admin_navigation",
|
||||
route: "adminConfig.navigation.settings",
|
||||
label: "admin.appearance.sidebar_link.navigation",
|
||||
label: "admin.config.navigation.title",
|
||||
description: "admin.config.navigation.header_description",
|
||||
icon: "diagram-project",
|
||||
settings_area: "navigation",
|
||||
},
|
||||
{
|
||||
name: "admin_themes",
|
||||
route: "adminCustomizeThemes",
|
||||
routeModels: ["themes"],
|
||||
model: "themes",
|
||||
label: "admin.appearance.sidebar_link.themes",
|
||||
label: "admin.config.themes.title",
|
||||
description: "admin.config.themes.header_description",
|
||||
icon: "paintbrush",
|
||||
},
|
||||
{
|
||||
name: "admin_components",
|
||||
route: "adminCustomizeThemes",
|
||||
routeModels: ["components"],
|
||||
label: "admin.appearance.sidebar_link.components.title",
|
||||
label: "admin.config.components.title",
|
||||
description: "admin.config.components.header_description",
|
||||
icon: "puzzle-piece",
|
||||
keywords: "admin.appearance.sidebar_link.components.keywords",
|
||||
keywords: "admin.config.components.keywords",
|
||||
},
|
||||
{
|
||||
name: "admin_customize_site_texts",
|
||||
route: "adminSiteText",
|
||||
label: "admin.appearance.sidebar_link.site_texts",
|
||||
label: "admin.config.site_texts.title",
|
||||
description: "admin.config.site_texts.header_description",
|
||||
icon: "language",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "email_settings",
|
||||
label: "admin.email_settings.title",
|
||||
label: "admin.config_sections.email.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_server_setup",
|
||||
route: "adminEmail",
|
||||
label: "admin.email_settings.sidebar_link.server_setup.title",
|
||||
label: "admin.config.email.title",
|
||||
description: "admin.config.email.header_description",
|
||||
keywords: "admin.config.email.keywords",
|
||||
icon: "gear",
|
||||
keywords: "admin.email_settings.sidebar_link.server_setup.keywords",
|
||||
links: [
|
||||
{
|
||||
name: "admin_email_preview_summary",
|
||||
route: "adminEmail.previewDigest",
|
||||
label: "admin.config.email.sub_pages.preview_summary.title",
|
||||
description:
|
||||
"admin.config.email.sub_pages.preview_summary.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_email_advanced_test",
|
||||
route: "adminEmail.advancedTest",
|
||||
label: "admin.config.email.sub_pages.advanced_test.title",
|
||||
description:
|
||||
"admin.config.email.sub_pages.advanced_test.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_email_templates",
|
||||
route: "adminEmailTemplates",
|
||||
label: "admin.config.email.sub_pages.templates.title",
|
||||
description:
|
||||
"admin.config.email.sub_pages.templates.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_email_sent",
|
||||
route: "adminEmail.sent",
|
||||
label: "admin.config.email.sub_pages.sent.title",
|
||||
description: "admin.config.email.sub_pages.sent.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_email_skipped",
|
||||
route: "adminEmail.skipped",
|
||||
label: "admin.config.email.sub_pages.skipped.title",
|
||||
description:
|
||||
"admin.config.email.sub_pages.skipped.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_email_bounced",
|
||||
route: "adminEmail.bounced",
|
||||
label: "admin.config.email.sub_pages.bounced.title",
|
||||
description:
|
||||
"admin.config.email.sub_pages.bounced.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_email_received",
|
||||
route: "adminEmail.received",
|
||||
label: "admin.config.email.sub_pages.received.title",
|
||||
description:
|
||||
"admin.config.email.sub_pages.received.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_email_rejected",
|
||||
route: "adminEmail.rejected",
|
||||
label: "admin.config.email.sub_pages.rejected.title",
|
||||
description:
|
||||
"admin.config.email.sub_pages.rejected.header_description",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "admin_appearance",
|
||||
route: "adminCustomizeEmailStyle",
|
||||
label: "admin.email_settings.sidebar_link.appearance",
|
||||
label: "admin.config.email_appearance.title",
|
||||
description: "admin.config.email_appearance.header_description",
|
||||
icon: "envelope",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "security",
|
||||
label: "admin.security.title",
|
||||
label: "admin.config_sections.security.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_security",
|
||||
route: "adminConfig.security.settings",
|
||||
label: "admin.security.sidebar_link.security",
|
||||
label: "admin.config.security.title",
|
||||
description: "admin.config.security.header_description",
|
||||
icon: "lock",
|
||||
settings_category: "security",
|
||||
},
|
||||
{
|
||||
name: "admin_spam",
|
||||
route: "adminConfig.spam.settings",
|
||||
label: "admin.security.sidebar_link.spam",
|
||||
label: "admin.config.spam.title",
|
||||
description: "admin.config.spam.header_description",
|
||||
icon: "robot",
|
||||
settings_category: "spam",
|
||||
},
|
||||
{
|
||||
name: "admin_logs_staff_action_logs",
|
||||
route: "adminLogs",
|
||||
label: "admin.security.sidebar_link.staff_action_logs.title",
|
||||
keywords: "admin.security.sidebar_link.staff_action_logs.keywords",
|
||||
label: "admin.config.staff_action_logs.title",
|
||||
description: "admin.config.staff_action_logs.header_description",
|
||||
keywords: "admin.config.staff_action_logs.keywords",
|
||||
icon: "user-shield",
|
||||
moderator: true,
|
||||
links: [
|
||||
{
|
||||
name: "admin_logs_screened_emails",
|
||||
route: "adminLogs.screenedEmails",
|
||||
label:
|
||||
"admin.config.staff_action_logs.sub_pages.screened_emails.title",
|
||||
description:
|
||||
"admin.config.staff_action_logs.sub_pages.screened_emails.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_logs_screened_ip_addresses",
|
||||
route: "adminLogs.screenedIpAddresses",
|
||||
label:
|
||||
"admin.config.staff_action_logs.sub_pages.screened_ips.title",
|
||||
description:
|
||||
"admin.config.staff_action_logs.sub_pages.screened_ips.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_logs_screened_urls",
|
||||
route: "adminLogs.screenedUrls",
|
||||
label:
|
||||
"admin.config.staff_action_logs.sub_pages.screened_urls.title",
|
||||
description:
|
||||
"admin.config.staff_action_logs.sub_pages.screened_urls.header_description",
|
||||
},
|
||||
{
|
||||
name: "admin_logs_search_logs",
|
||||
route: "adminSearchLogs",
|
||||
label: "admin.config.staff_action_logs.sub_pages.search_logs.title",
|
||||
description:
|
||||
"admin.config.staff_action_logs.sub_pages.search_logs.header_description",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "plugins",
|
||||
label: "admin.plugins.title",
|
||||
label: "admin.config_sections.plugins.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_installed_plugins",
|
||||
route: "adminPlugins.index",
|
||||
label: "admin.plugins.sidebar_link.installed",
|
||||
label: "admin.config.plugins.title",
|
||||
description: "admin.config.plugins.header_description",
|
||||
icon: "puzzle-piece",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "advanced",
|
||||
label: "admin.advanced.title",
|
||||
label: "admin.config_sections.advanced.title",
|
||||
links: [
|
||||
{
|
||||
name: "admin_api_keys",
|
||||
route: "adminApiKeys",
|
||||
icon: "key",
|
||||
label: "admin.advanced.sidebar_link.api_keys.title",
|
||||
keywords: "admin.advanced.sidebar_link.api_keys.keywords",
|
||||
label: "admin.config.api_keys.title",
|
||||
description: "admin.config.api_keys.header_description",
|
||||
keywords: "admin.config.api_keys.keywords",
|
||||
},
|
||||
{
|
||||
name: "admin_webhooks",
|
||||
route: "adminWebHooks",
|
||||
icon: "arrows-rotate",
|
||||
label: "admin.advanced.sidebar_link.webhooks",
|
||||
label: "admin.config.webhooks.title",
|
||||
description: "admin.config.webhooks.header_description",
|
||||
keywords: "admin.config.webhooks.keywords",
|
||||
},
|
||||
{
|
||||
name: "admin_developer",
|
||||
route: "adminConfig.developer.settings",
|
||||
label: "admin.advanced.sidebar_link.developer",
|
||||
label: "admin.config.developer.title",
|
||||
description: "admin.config.developer.header_description",
|
||||
icon: "keyboard",
|
||||
settings_category: "developer",
|
||||
},
|
||||
{
|
||||
name: "admin_embedding",
|
||||
route: "adminEmbedding",
|
||||
label: "admin.advanced.sidebar_link.embedding",
|
||||
label: "admin.config.embedding.title",
|
||||
description: "admin.config.embedding.header_description",
|
||||
icon: "code",
|
||||
settings_area: "embedding",
|
||||
},
|
||||
{
|
||||
name: "admin_rate_limits",
|
||||
route: "adminConfig.rate-limits.settings",
|
||||
label: "admin.advanced.sidebar_link.rate_limits",
|
||||
label: "admin.config.rate_limits.title",
|
||||
description: "admin.config.rate_limits.header_description",
|
||||
icon: "rocket",
|
||||
settings_category: "rate_limits",
|
||||
},
|
||||
{
|
||||
name: "admin_user_api",
|
||||
route: "adminConfig.user-api.settings",
|
||||
label: "admin.advanced.sidebar_link.user_api",
|
||||
label: "admin.config.user_api.title",
|
||||
description: "admin.config.user_api.header_description",
|
||||
icon: "shuffle",
|
||||
settings_category: "user_api",
|
||||
},
|
||||
{
|
||||
name: "admin_onebox",
|
||||
route: "adminConfig.onebox.settings",
|
||||
label: "admin.advanced.sidebar_link.onebox",
|
||||
label: "admin.config.onebox.title",
|
||||
description: "admin.config.onebox.header_description",
|
||||
icon: "far-square",
|
||||
settings_category: "onebox",
|
||||
},
|
||||
{
|
||||
name: "admin_files",
|
||||
route: "adminConfig.files.settings",
|
||||
label: "admin.advanced.sidebar_link.files",
|
||||
label: "admin.config.files.title",
|
||||
description: "admin.config.files.header_description",
|
||||
icon: "file",
|
||||
settings_category: "files",
|
||||
},
|
||||
{
|
||||
name: "admin_other_options",
|
||||
route: "adminConfig.other.settings",
|
||||
label: "admin.advanced.sidebar_link.other_options",
|
||||
label: "admin.config.other.title",
|
||||
description: "admin.config.other.header_description",
|
||||
icon: "discourse-other-tab",
|
||||
|
||||
settings_category: "uncategorized",
|
||||
},
|
||||
{
|
||||
name: "admin_search",
|
||||
route: "adminConfig.search.settings",
|
||||
label: "admin.advanced.sidebar_link.search",
|
||||
label: "admin.config.search.title",
|
||||
description: "admin.config.search.header_description",
|
||||
icon: "magnifying-glass",
|
||||
settings_category: "search",
|
||||
},
|
||||
{
|
||||
name: "admin_experimental",
|
||||
route: "adminConfig.experimental.settings",
|
||||
label: "admin.advanced.sidebar_link.experimental",
|
||||
label: "admin.config.experimental.title",
|
||||
description: "admin.config.experimental.header_description",
|
||||
icon: "discourse-sparkles",
|
||||
settings_category: "experimental",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -84,9 +84,8 @@ class SidebarAdminSectionLink extends BaseCustomSidebarSectionLink {
|
||||
// for the plugin ID has its own nested routes defined in the plugin.
|
||||
if (this.router.currentRoute.name === "adminPlugins.show.settings") {
|
||||
if (
|
||||
this.adminSidebarNavLink.route?.includes(
|
||||
this.adminSidebarNavLink.route?.split(".").last ===
|
||||
this.router.currentRoute.parent.params.plugin_id
|
||||
)
|
||||
) {
|
||||
return this.router.currentRoute.name;
|
||||
}
|
||||
@ -184,53 +183,6 @@ function defineAdminSection(
|
||||
}
|
||||
|
||||
export function useAdminNavConfig(navMap) {
|
||||
const adminNavSections = [
|
||||
{
|
||||
text: "",
|
||||
name: "root",
|
||||
hideSectionHeader: true,
|
||||
links: [
|
||||
{
|
||||
name: "admin_home",
|
||||
route: "admin.dashboard.general",
|
||||
label: "admin.dashboard.title",
|
||||
icon: "house",
|
||||
moderator: true,
|
||||
},
|
||||
{
|
||||
name: "admin_users",
|
||||
route: "adminUsers",
|
||||
label: "admin.community.sidebar_link.users",
|
||||
icon: "users",
|
||||
moderator: true,
|
||||
},
|
||||
{
|
||||
name: "admin_groups",
|
||||
route: "groups",
|
||||
label: "admin.community.sidebar_link.groups",
|
||||
icon: "user-group",
|
||||
moderator: true,
|
||||
},
|
||||
{
|
||||
name: "admin_all_site_settings",
|
||||
route: "adminSiteSettings",
|
||||
label: "admin.advanced.sidebar_link.all_site_settings",
|
||||
icon: "gear",
|
||||
},
|
||||
{
|
||||
name: "admin_whats_new",
|
||||
route: "admin.whatsNew",
|
||||
label: "admin.account.sidebar_link.whats_new.title",
|
||||
icon: "gift",
|
||||
keywords: "admin.account.sidebar_link.whats_new.keywords",
|
||||
moderator: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
navMap = adminNavSections.concat(navMap);
|
||||
|
||||
for (const [sectionName, additionalLinks] of Object.entries(
|
||||
additionalAdminSidebarSectionLinks
|
||||
)) {
|
||||
@ -320,6 +272,7 @@ function pluginAdminRouteLinks(router) {
|
||||
label: plugin.admin_route.label,
|
||||
text: plugin.humanized_name,
|
||||
icon: "gear",
|
||||
description: plugin.description,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,96 @@
|
||||
const ACRONYMS = new Set([
|
||||
"acl",
|
||||
"ai",
|
||||
"api",
|
||||
"bg",
|
||||
"cdn",
|
||||
"cors",
|
||||
"cta",
|
||||
"dm",
|
||||
"eu",
|
||||
"faq",
|
||||
"fg",
|
||||
"ga",
|
||||
"gb",
|
||||
"gtm",
|
||||
"hd",
|
||||
"http",
|
||||
"https",
|
||||
"iam",
|
||||
"id",
|
||||
"imap",
|
||||
"ip",
|
||||
"jpg",
|
||||
"json",
|
||||
"kb",
|
||||
"mb",
|
||||
"oidc",
|
||||
"pm",
|
||||
"png",
|
||||
"pop3",
|
||||
"s3",
|
||||
"smtp",
|
||||
"svg",
|
||||
"tl",
|
||||
"tl0",
|
||||
"tl1",
|
||||
"tl2",
|
||||
"tl3",
|
||||
"tl4",
|
||||
"tld",
|
||||
"txt",
|
||||
"url",
|
||||
"ux",
|
||||
]);
|
||||
|
||||
const MIXED_CASE = [
|
||||
["adobe analytics", "Adobe Analytics"],
|
||||
["android", "Android"],
|
||||
["chinese", "Chinese"],
|
||||
["discord", "Discord"],
|
||||
["discourse", "Discourse"],
|
||||
["discourse connect", "Discourse Connect"],
|
||||
["discourse discover", "Discourse Discover"],
|
||||
["discourse narrative bot", "Discourse Narrative Bot"],
|
||||
["facebook", "Facebook"],
|
||||
["github", "GitHub"],
|
||||
["google", "Google"],
|
||||
["gravatar", "Gravatar"],
|
||||
["gravatars", "Gravatars"],
|
||||
["ios", "iOS"],
|
||||
["japanese", "Japanese"],
|
||||
["linkedin", "LinkedIn"],
|
||||
["oauth2", "OAuth2"],
|
||||
["opengraph", "OpenGraph"],
|
||||
["powered by discourse", "Powered by Discourse"],
|
||||
["tiktok", "TikTok"],
|
||||
["tos", "ToS"],
|
||||
["twitter", "Twitter"],
|
||||
["vimeo", "Vimeo"],
|
||||
["wordpress", "WordPress"],
|
||||
["youtube", "YouTube"],
|
||||
];
|
||||
|
||||
export function humanizedSettingName(settingName, settingLabel) {
|
||||
const name = settingLabel || settingName.replace(/\_/g, " ");
|
||||
|
||||
const formattedName = (name.charAt(0).toUpperCase() + name.slice(1))
|
||||
.split(" ")
|
||||
.map((word) =>
|
||||
ACRONYMS.has(word.toLowerCase()) ? word.toUpperCase() : word
|
||||
)
|
||||
.map((word) => {
|
||||
if (word.endsWith("s")) {
|
||||
const singular = word.slice(0, -1).toLowerCase();
|
||||
return ACRONYMS.has(singular) ? singular.toUpperCase() + "s" : word;
|
||||
}
|
||||
return word;
|
||||
})
|
||||
.join(" ");
|
||||
|
||||
return MIXED_CASE.reduce(
|
||||
(acc, [key, value]) =>
|
||||
acc.replaceAll(new RegExp(`\\b${key}\\b`, "gi"), value),
|
||||
formattedName
|
||||
);
|
||||
}
|
@ -112,9 +112,10 @@ acceptance("Admin - Site Settings", function (needs) {
|
||||
await click(".nav.nav-pills li:nth-child(1) a");
|
||||
assert.dom(".row.setting").exists({ count: 0 });
|
||||
|
||||
// navigate back to the "Settings" page
|
||||
// navigate back to the "Settings" page, the title filter
|
||||
// has been removed from navigation
|
||||
await click(".nav.nav-pills li:nth-child(2) a");
|
||||
assert.dom(".row.setting").exists({ count: 1 });
|
||||
assert.dom(".row.setting").exists({ count: 4 });
|
||||
});
|
||||
|
||||
test("filtering overridden settings", async function (assert) {
|
||||
|
@ -1225,6 +1225,7 @@ a.inline-editable-field {
|
||||
@import "common/admin/plugins";
|
||||
@import "common/admin/site-settings";
|
||||
@import "common/admin/admin_config_area";
|
||||
@import "common/admin/search";
|
||||
@import "common/admin/admin_table";
|
||||
@import "common/admin/admin_filter";
|
||||
@import "common/admin/admin_reports";
|
||||
|
129
app/assets/stylesheets/common/admin/search.scss
Normal file
129
app/assets/stylesheets/common/admin/search.scss
Normal file
@ -0,0 +1,129 @@
|
||||
.admin-search-modal .d-modal__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-4);
|
||||
}
|
||||
|
||||
// Search field
|
||||
.admin-search {
|
||||
&__input-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--primary-400);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 42px;
|
||||
padding: 0 var(--space-2);
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--tertiary);
|
||||
outline: 2px solid var(--tertiary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
&__input-icon {
|
||||
background: none !important;
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
|
||||
&__input-field {
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
appearance: none !important;
|
||||
outline: none !important;
|
||||
background: none !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Fitler
|
||||
.admin-search__filters {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.admin-search__filter {
|
||||
&-item {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: var(--space-1) var(--space-2);
|
||||
background-color: var(--primary-low);
|
||||
color: var(--primary-medium);
|
||||
border-radius: var(--d-border-radius);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-low-mid);
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--tertiary);
|
||||
color: var(--secondary);
|
||||
|
||||
.d-icon {
|
||||
color: var(--tertiary-low);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-active) {
|
||||
.d-icon {
|
||||
color: var(--primary-low-mid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Results
|
||||
.admin-search__result {
|
||||
border-top: 1px solid var(--primary-low);
|
||||
padding: var(--space-3) var(--space-2);
|
||||
display: block;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--tertiary-very-low);
|
||||
}
|
||||
|
||||
&-name {
|
||||
color: var(--primary);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: var(--space-3);
|
||||
|
||||
.d-icon {
|
||||
color: var(--primary-high);
|
||||
font-size: var(--font-down-1);
|
||||
}
|
||||
}
|
||||
|
||||
&-name-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-description {
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-high);
|
||||
margin-top: var(--space-1);
|
||||
}
|
||||
}
|
21
app/controllers/admin/search_controller.rb
Normal file
21
app/controllers/admin/search_controller.rb
Normal file
@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SearchController < Admin::AdminController
|
||||
def index
|
||||
# TODO (martin) Include reports here too, need to refact
|
||||
# the reports controller into a reusable lookup service first.
|
||||
render_json_dump(
|
||||
settings:
|
||||
SiteSetting.all_settings(
|
||||
filter_names: params[:filter_names],
|
||||
filter_area: params[:filter_area],
|
||||
filter_plugin: params[:plugin],
|
||||
filter_categories: Array.wrap(params[:categories]),
|
||||
include_locale_setting: params[:filter_area] == "localization",
|
||||
basic_attributes: true,
|
||||
),
|
||||
themes_and_components:
|
||||
serialize_data(Theme.include_relations.order(:name), BasicThemeSerializer),
|
||||
)
|
||||
end
|
||||
end
|
@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BasicThemeSerializer < ApplicationSerializer
|
||||
attributes :id, :name, :created_at, :updated_at, :default, :component
|
||||
attributes :id, :name, :description, :created_at, :updated_at, :default, :component
|
||||
|
||||
def include_default?
|
||||
object.id == SiteSetting.default_theme_id
|
||||
@ -10,4 +10,8 @@ class BasicThemeSerializer < ApplicationSerializer
|
||||
def default
|
||||
true
|
||||
end
|
||||
|
||||
def description
|
||||
object.internal_translations.find { |t| t.key == "theme_metadata.description" }&.value
|
||||
end
|
||||
end
|
||||
|
@ -73,6 +73,7 @@ class CurrentUserSerializer < BasicUserSerializer
|
||||
:sidebar_sections,
|
||||
:new_new_view_enabled?,
|
||||
:use_admin_sidebar,
|
||||
:use_experimental_admin_search,
|
||||
:can_view_raw_email,
|
||||
:login_method,
|
||||
:has_unseen_features,
|
||||
@ -137,6 +138,10 @@ class CurrentUserSerializer < BasicUserSerializer
|
||||
object.staff? && object.in_any_groups?(SiteSetting.admin_sidebar_enabled_groups_map)
|
||||
end
|
||||
|
||||
def use_experimental_admin_search
|
||||
object.staff? && object.in_any_groups?(SiteSetting.experimental_admin_search_enabled_groups_map)
|
||||
end
|
||||
|
||||
def include_use_admin_sidebar?
|
||||
object.staff?
|
||||
end
|
||||
|
@ -10,7 +10,6 @@ class ThemeSerializer < BasicThemeSerializer
|
||||
:settings,
|
||||
:errors,
|
||||
:supported?,
|
||||
:description,
|
||||
:enabled?,
|
||||
:disabled_at,
|
||||
:theme_fields,
|
||||
@ -84,10 +83,6 @@ class ThemeSerializer < BasicThemeSerializer
|
||||
@errors.present?
|
||||
end
|
||||
|
||||
def description
|
||||
object.internal_translations.find { |t| t.key == "theme_metadata.description" }&.value
|
||||
end
|
||||
|
||||
def include_disabled_at?
|
||||
object.component? && !object.enabled?
|
||||
end
|
||||
|
@ -5160,16 +5160,42 @@ en:
|
||||
meta_doc: "Reports are a powerful tool to help you understand what’s happening on your site. They can help you identify trends, spot problems, and make decisions based on data."
|
||||
sidebar_title: "Reports"
|
||||
back: "Back to all reports"
|
||||
sidebar_link:
|
||||
all: "All reports"
|
||||
|
||||
config_sections:
|
||||
account:
|
||||
title: "Account"
|
||||
reports:
|
||||
title: "Reports"
|
||||
community:
|
||||
title: "Community"
|
||||
appearance:
|
||||
title: "Appearance"
|
||||
email:
|
||||
title: "Email"
|
||||
security:
|
||||
title: "Security"
|
||||
plugins:
|
||||
title: "Plugins"
|
||||
advanced:
|
||||
title: "Advanced"
|
||||
|
||||
config:
|
||||
about:
|
||||
title: "About your site"
|
||||
header_description: "Provide information here about this site and your team so that people can learn what your community is about, who is behind it, and how to reach you in case there is an issue. Displayed on your site's <a href='%{basePath}/about'>About page</a>."
|
||||
developer:
|
||||
title: "Developer"
|
||||
header_description: "Developer settings to control rate limits, multipliers and calculations, safe mode, and other advanced features"
|
||||
experimental:
|
||||
title: "Experimental"
|
||||
header_description: "Toggle experimental features on or off for your site, most of these can be controlled on a group basis"
|
||||
emojis:
|
||||
title: "Emoji"
|
||||
header_description: "Add new emoji that will be available to everyone. Select multiple files to create emojis using their file names. The selected group will be used for all files that are added at the same time"
|
||||
flags:
|
||||
title: "Moderation"
|
||||
header_description: "The flagging system in Discourse helps you and your moderator team manage content and user behavior, keeping your community respectful and healthy. The defaults are suitable for most communities and you don’t have to change them. However, if your site has particular requirements you can disable flags you don’t need and add your own custom flags."
|
||||
keywords: "flag|review|spam|illegal"
|
||||
font_style:
|
||||
title: "Font style"
|
||||
header_description: "Customize the font styles used by your themes"
|
||||
@ -5182,6 +5208,7 @@ en:
|
||||
localization:
|
||||
title: "Localization"
|
||||
header_description: "Configure your community’s interface language and other localization options for your members"
|
||||
keywords: "locale|language|timezone|unicode|ltr"
|
||||
login_and_authentication:
|
||||
title: "Login & authentication"
|
||||
header_description: "Configure how users log in and authenticate, secrets and keys, OAuth2 providers, and more"
|
||||
@ -5221,6 +5248,142 @@ en:
|
||||
group_permissions:
|
||||
title: "Group permissions"
|
||||
header_description: "All group-based app permissions are managed here, which control access to various features within Discourse"
|
||||
dashboard:
|
||||
title: "Dashboard"
|
||||
header_description: "The dashboard provides a snapshot of your community’s health, including traffic, user activity, and other key metrics"
|
||||
users:
|
||||
title: "Users"
|
||||
header_description: "View and manage users, send invites, and export user data"
|
||||
groups:
|
||||
title: "Groups"
|
||||
header_description: "Create and manage groups, set group permissions, and view group activity and inboxes"
|
||||
site_settings:
|
||||
title: "All site settings"
|
||||
header_description: "Configure all settings for your Discourse site to customize its appearance, functionality, and user experience"
|
||||
whats_new:
|
||||
title: "What's new?"
|
||||
header_description: "Stay up-to-date with the latest features and improvements in Discourse"
|
||||
keywords: "changelog|feature|release"
|
||||
backups:
|
||||
title: "Backups"
|
||||
header_description: "Discourse backups include the full site database, which contains everything on the site: topics, posts, users, groups, settings, themes, etc. Depending on how the backup file is created, it may or may not include uploads."
|
||||
sub_pages:
|
||||
logs:
|
||||
title: "Logs"
|
||||
header_description: "View and manage logs for backups, restores, and uploads"
|
||||
reports:
|
||||
title: "Reports"
|
||||
header_description: "Reports are a powerful tool to help you understand what’s happening on your site. They can help you identify trends, spot problems, and make decisions based on data."
|
||||
badges:
|
||||
title: "Badges"
|
||||
header_description: "Badges reward users for their activities, contributions, and achievements to recognize, validate, and encourage positive behavior and engagement within the community"
|
||||
permalinks:
|
||||
title: "Permalinks"
|
||||
header_description: "Redirections to apply for URLs not known by the forum"
|
||||
user_fields:
|
||||
title: "User fields"
|
||||
header_description: "Create custom user fields to collect extra details about your community members. You can choose what information is required during sign-up, what shows on profiles, and what users can update."
|
||||
watched_words:
|
||||
title: "Watched words"
|
||||
header_description: "Watched words are moderation tools that can perform multiple different actions including blocking, censoring, linking, or flagging posts containing certain words"
|
||||
color_palettes:
|
||||
title: "Color palettes"
|
||||
header_description: "Color palettes define the core colors used across your site’s interface, while themes can provide additional styling, layouts, and components. Both can work together to create your site’s unique look and feel, and both can be made available for users to select their preference"
|
||||
emoji:
|
||||
title: "Emoji"
|
||||
header_description: "Add new emoji that will be available to everyone. Select multiple files to create emojis using their file names. The selected group will be used for all files that are added at the same time."
|
||||
themes:
|
||||
title: "Themes"
|
||||
header_description: "Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features"
|
||||
components:
|
||||
title: "Components"
|
||||
header_description: "Components are smaller customizations that can be added to themes in order to change specific elements of the style of your forum design"
|
||||
keywords: "theme|component|extension"
|
||||
site_texts:
|
||||
title: "Site texts"
|
||||
header_description: "Customize any text used in Discourse to match your community’s voice and tone"
|
||||
email:
|
||||
title: "Server setup & logs"
|
||||
header_description: "Customize the templates used to create emails, preview summary emails that will be sent out, and view email logs"
|
||||
keywords: "email|smtp|mailgun|sendgrid|sent|skipped|bounced|received|rejected|email logs|preview summary"
|
||||
sub_pages:
|
||||
preview_summary:
|
||||
title: "Preview summary"
|
||||
header_description: "Preview the content of the summary emails sent to inactive users"
|
||||
advanced_test:
|
||||
title: "Advanced test"
|
||||
header_description: "See how Discourse processes received emails"
|
||||
templates:
|
||||
title: "Templates"
|
||||
header_description: "Customize the templates used to create emails"
|
||||
sent:
|
||||
title: "Sent"
|
||||
header_description: "View a log of emails that have been sent"
|
||||
skipped:
|
||||
title: "Skipped"
|
||||
header_description: "View a log of emails that have been skipped"
|
||||
bounced:
|
||||
title: "Bounced"
|
||||
header_description: "View a log of emails that have bounced"
|
||||
received:
|
||||
title: "Received"
|
||||
header_description: "View a log of emails that have been received"
|
||||
rejected:
|
||||
title: "Rejected"
|
||||
header_description: "View a log of emails that have been rejected"
|
||||
email_appearance:
|
||||
title: "Appearance"
|
||||
header_description: "Customize the template in which all html emails are rendered, and style using CSS"
|
||||
staff_action_logs:
|
||||
title: "Logs & screening"
|
||||
header_description: "Logs and screening allow you to monitor and manage your community, ensuring that it remains safe and respectful. You can view logs of all actions taken by staff members, search logs, and user screening configuration"
|
||||
keywords: "error logs|staff action|screened emails|screened ips|screened urls|search logs"
|
||||
sub_pages:
|
||||
screened_emails:
|
||||
title: "Screened emails"
|
||||
header_description: "When someone tries to create a new account, the following email addresses will be checked and the registration will be blocked, or some other action performed"
|
||||
screened_ips:
|
||||
title: "Screened IPs"
|
||||
header_description: "IP addresses that are being watched. Individual IP addresses can be allowlisted."
|
||||
screened_urls:
|
||||
title: "Screened URLs"
|
||||
header_description: "The URLs listed here were used in posts by users who have been identified as spammers"
|
||||
search_logs:
|
||||
title: "Search logs"
|
||||
header_description: "View a log of searches that have been performed"
|
||||
plugins:
|
||||
title: "Installed plugins"
|
||||
header_description: "Any Discourse plugins that you have installed, or plugins that come preinstalled with Discourse hosting, will appear in this list"
|
||||
api_keys:
|
||||
title: "API keys"
|
||||
header_description: "The API keys feature lets you securely integrate Discourse with external systems and automate actions. Admins can create keys with specific scopes to control access to resources and sensitive data. Scopes limit functionality, ensuring enhanced security."
|
||||
keywords: "token|scope"
|
||||
webhooks:
|
||||
title: "Webhooks"
|
||||
header_description: "Webhooks allows Discourse to notify external services when certain events happen in your site. When the webhook is triggered, a POST request will send to URLs provided."
|
||||
keywords: "event|payload|url"
|
||||
embedding:
|
||||
title: "Embedding"
|
||||
header_description: "Discourse has the ability to embed the comments from a topic in a remote site using a Javascript API that creates an IFRAME"
|
||||
|
||||
search:
|
||||
modal_title: "Search anything in admin"
|
||||
result_types:
|
||||
page:
|
||||
one: "Page"
|
||||
other: "Pages"
|
||||
report:
|
||||
one: "Report"
|
||||
other: "Reports"
|
||||
setting:
|
||||
one: "Setting"
|
||||
other: "Settings"
|
||||
theme:
|
||||
one: "Theme"
|
||||
other: "Themes"
|
||||
component:
|
||||
one: "Component"
|
||||
other: "Components"
|
||||
|
||||
new_features:
|
||||
title: "What's new?"
|
||||
@ -5702,81 +5865,9 @@ en:
|
||||
failed: "Failed"
|
||||
home:
|
||||
title: "Home"
|
||||
account:
|
||||
title: "Account"
|
||||
sidebar_link:
|
||||
backups: "Backups"
|
||||
whats_new:
|
||||
title: "What's new?"
|
||||
keywords: "changelog|feature|release"
|
||||
|
||||
community:
|
||||
title: "Community"
|
||||
sidebar_link:
|
||||
about_your_site: "About your site"
|
||||
badges: "Badges"
|
||||
login_and_authentication: "Login & authentication"
|
||||
notifications: "Notifications"
|
||||
permalinks: "Permalinks"
|
||||
trust_levels: "Trust levels"
|
||||
group_permissions: "Group permissions"
|
||||
users: "Users"
|
||||
groups: "Groups"
|
||||
localization:
|
||||
title: "Localization"
|
||||
keywords: "locale|language|timezone|unicode|ltr"
|
||||
user_fields: "User fields"
|
||||
watched_words: "Watched words"
|
||||
legal: "Legal"
|
||||
moderation_flags:
|
||||
title: "Moderation"
|
||||
keywords: "flag|review"
|
||||
|
||||
appearance:
|
||||
title: "Appearance"
|
||||
sidebar_link:
|
||||
font_style: "Font style"
|
||||
site_logo: "Site logo"
|
||||
color_schemes: "Color palettes"
|
||||
emoji: "Emoji"
|
||||
navigation: "Navigation"
|
||||
themes: "Themes"
|
||||
components:
|
||||
title: "Components"
|
||||
keywords: "theme|extension"
|
||||
site_texts: "Site texts"
|
||||
|
||||
email_settings:
|
||||
title: "Email Settings"
|
||||
sidebar_link:
|
||||
appearance: "Appearance"
|
||||
server_setup:
|
||||
title: "Server setup & logs"
|
||||
keywords: "email|smtp|mailgun|sendgrid|sent|skipped|bounced|received|rejected|email logs|preview summary"
|
||||
|
||||
security:
|
||||
title: "Security"
|
||||
sidebar_link:
|
||||
security: "Security settings"
|
||||
spam: "Spam settings"
|
||||
staff_action_logs:
|
||||
title: "Logs & screening"
|
||||
keywords: "error logs|staff action|screened emails|screened ips|screened urls|search logs"
|
||||
|
||||
section_landing_pages:
|
||||
account:
|
||||
title: "Account"
|
||||
backups:
|
||||
title: "Backups"
|
||||
description: "Take a backup of your site's data"
|
||||
whats_new:
|
||||
title: "What's new?"
|
||||
description: "Discover new releases and improvements to Discourse"
|
||||
|
||||
config_areas:
|
||||
about:
|
||||
header: "About your site"
|
||||
description: "Provide information here about this site and your team so that people can learn what your community is about, who is behind it, and how to reach you in case there is an issue. Displayed on your site's <a href='%{basePath}/about'>About page</a>."
|
||||
general_settings: "General settings"
|
||||
community_name: "Community name"
|
||||
community_name_placeholder: "Example Community"
|
||||
@ -5828,9 +5919,7 @@ en:
|
||||
your_organization_saved: "Your organization saved"
|
||||
saved: "saved!"
|
||||
flags:
|
||||
header: "Moderation"
|
||||
edit_header: "Edit Flag"
|
||||
subheader: "The flagging system in Discourse helps you and your moderator team manage content and user behavior, keeping your community respectful and healthy. The defaults are suitable for most communities and you don’t have to change them. However, if your site has particular requirements you can disable flags you don’t need and add your own custom flags."
|
||||
description: "Description"
|
||||
enabled: "Enabled?"
|
||||
add: "Add flag"
|
||||
@ -5873,8 +5962,6 @@ en:
|
||||
more_options:
|
||||
title: "More options"
|
||||
look_and_feel:
|
||||
title: "Look and feel"
|
||||
description: "Customize and brand your Discourse site, giving it a distinctive style."
|
||||
themes:
|
||||
title: "Themes"
|
||||
themes_intro: "Install a new theme to get started, or create your own from scratch using these resources."
|
||||
@ -5896,8 +5983,6 @@ en:
|
||||
save_successful: "User field saved."
|
||||
plugins:
|
||||
title: "Plugins"
|
||||
installed: "Installed plugins"
|
||||
description: "Any Discourse plugins that you have installed, or plugins that come preinstalled with Discourse hosting, will appear in this list."
|
||||
name: "Name"
|
||||
none_installed: "You don't have any plugins installed."
|
||||
version: "Version"
|
||||
@ -5912,26 +5997,6 @@ en:
|
||||
author: "By %{author}"
|
||||
experimental_badge: "experimental"
|
||||
learn_more: "Learn more"
|
||||
sidebar_link:
|
||||
installed: "Installed"
|
||||
|
||||
advanced:
|
||||
title: "Advanced"
|
||||
sidebar_link:
|
||||
api_keys:
|
||||
title: "API keys"
|
||||
keywords: "token"
|
||||
webhooks: "Webhooks"
|
||||
developer: "Developer"
|
||||
embedding: "Embedding"
|
||||
rate_limits: "Rate limits"
|
||||
user_api: "User API"
|
||||
onebox: "Onebox"
|
||||
files: "Files"
|
||||
other_options: "Other"
|
||||
search: "Search"
|
||||
experimental: "Experimental"
|
||||
all_site_settings: "All site settings"
|
||||
|
||||
navigation_menu:
|
||||
sidebar: "Sidebar"
|
||||
@ -5941,7 +6006,6 @@ en:
|
||||
backups:
|
||||
title: "Backups"
|
||||
files_title: "Backup files"
|
||||
description: "Discourse backups include the full site database, which contains everything on the site: topics, posts, users, groups, settings, themes, etc. Depending on how the backup file is created, it may or may not include uploads."
|
||||
learn_more_url: ""
|
||||
menu:
|
||||
backups: "Backups"
|
||||
|
@ -386,9 +386,11 @@ Discourse::Application.routes.draw do
|
||||
post "preview" => "badges#preview"
|
||||
end
|
||||
end
|
||||
|
||||
get "search/all" => "search#index"
|
||||
|
||||
namespace :config, constraints: StaffConstraint.new do
|
||||
resources :site_settings, only: %i[index]
|
||||
|
||||
get "developer" => "site_settings#index"
|
||||
get "fonts" => "site_settings#index"
|
||||
get "files" => "site_settings#index"
|
||||
|
@ -3532,6 +3532,15 @@ experimental:
|
||||
allow_any: false
|
||||
refresh: true
|
||||
area: "group_permissions|navigation"
|
||||
experimental_admin_search_enabled_groups:
|
||||
hidden: true
|
||||
client: true
|
||||
type: group_list
|
||||
list_type: compact
|
||||
default: ""
|
||||
allow_any: false
|
||||
refresh: true
|
||||
area: "group_permissions"
|
||||
glimmer_topic_list_mode:
|
||||
client: true
|
||||
type: enum
|
||||
|
@ -101,6 +101,7 @@ class ApplicationLayoutPreloader
|
||||
humanized_name: plugin.humanized_name,
|
||||
admin_route: plugin.full_admin_route,
|
||||
enabled: plugin.enabled?,
|
||||
description: plugin.metadata.about,
|
||||
}
|
||||
end,
|
||||
)
|
||||
|
@ -1451,7 +1451,12 @@ class Plugin::Instance
|
||||
|
||||
def setting_category_name
|
||||
return if setting_category.blank? || setting_category == "plugins"
|
||||
I18n.t("admin_js.admin.site_settings.categories.#{setting_category}")
|
||||
I18n.t("admin_js.#{setting_category_label}")
|
||||
end
|
||||
|
||||
def setting_category_label
|
||||
return if setting_category.blank? || setting_category == "plugins"
|
||||
"admin.site_settings.categories.#{setting_category}"
|
||||
end
|
||||
|
||||
def validate_directory_column_name(column_name)
|
||||
@ -1500,7 +1505,7 @@ class Plugin::Instance
|
||||
|
||||
def default_admin_route
|
||||
{
|
||||
label: "#{name.underscore}.title",
|
||||
label: setting_category_label || "#{name.underscore}.title",
|
||||
location: name,
|
||||
use_new_show_route: true,
|
||||
auto_generated: true,
|
||||
|
@ -201,6 +201,7 @@ module SiteSettingExtension
|
||||
include_hidden: false,
|
||||
include_locale_setting: true,
|
||||
only_overridden: false,
|
||||
basic_attributes: false,
|
||||
filter_categories: nil,
|
||||
filter_plugin: nil,
|
||||
filter_names: nil,
|
||||
@ -271,15 +272,22 @@ module SiteSettingExtension
|
||||
setting: s,
|
||||
description: description(s),
|
||||
keywords: keywords(s),
|
||||
category: categories[s],
|
||||
primary_area: areas[s]&.first,
|
||||
}
|
||||
|
||||
if !basic_attributes
|
||||
opts.merge!(
|
||||
default: default,
|
||||
value: value.to_s,
|
||||
category: categories[s],
|
||||
preview: previews[s],
|
||||
secret: secret_settings.include?(s),
|
||||
placeholder: placeholder(s),
|
||||
mandatory_values: mandatory_values[s],
|
||||
requires_confirmation: requires_confirmation_settings[s],
|
||||
}.merge!(type_hash)
|
||||
)
|
||||
opts.merge!(type_hash)
|
||||
end
|
||||
|
||||
opts[:plugin] = plugins[s] if plugins[s]
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# name: chat
|
||||
# about: Adds chat functionality to your site so it can natively support both long-form and short-form communication needs of your online community.
|
||||
# about: Adds chat functionality to your site so it can natively support both long-form and short-form communication needs of your online community
|
||||
# meta_topic_id: 230881
|
||||
# version: 0.4
|
||||
# authors: Kane York, Mark VanLandingham, Martin Brennan, Joffrey Jaffeux
|
||||
|
@ -31,6 +31,8 @@ RSpec.describe ApplicationController do
|
||||
"auto_generated" => false,
|
||||
},
|
||||
"enabled" => true,
|
||||
"description" =>
|
||||
"Adds chat functionality to your site so it can natively support both long-form and short-form communication needs of your online community",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
@ -1,4 +1,9 @@
|
||||
en:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_details: "Discourse Details"
|
||||
js:
|
||||
details:
|
||||
title: Hide Details
|
||||
|
@ -1,4 +1,9 @@
|
||||
en:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_local_dates: "Discourse Local Dates"
|
||||
js:
|
||||
discourse_local_dates:
|
||||
relative_dates:
|
||||
|
@ -4,3 +4,8 @@ en:
|
||||
welcome_post_type:
|
||||
new_user_track: "Start the new user tutorial for all new users"
|
||||
welcome_message: "Send all new users a welcome message with a quick start guide"
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
discourse_narrative_bot: "Discourse Narrative Bot"
|
||||
|
@ -1,4 +1,9 @@
|
||||
en:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
footnote: "Discourse Footnotes"
|
||||
js:
|
||||
footnote:
|
||||
title: "Footnotes"
|
||||
|
@ -1,4 +1,9 @@
|
||||
en:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
poll: "Discourse Poll"
|
||||
js:
|
||||
poll:
|
||||
voters:
|
||||
|
@ -1,4 +1,9 @@
|
||||
en:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
spoiler_alert: "Discourse Spoiler Alert"
|
||||
js:
|
||||
spoiler:
|
||||
title: Blur Spoiler
|
||||
|
@ -1,4 +1,9 @@
|
||||
en:
|
||||
admin_js:
|
||||
admin:
|
||||
site_settings:
|
||||
categories:
|
||||
styleguide: "Discourse Styleguide"
|
||||
js:
|
||||
styleguide:
|
||||
title: "Styleguide"
|
||||
|
@ -55,10 +55,10 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
expect(links.map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.dashboard.title"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.users"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.groups"),
|
||||
I18n.t("admin_js.admin.advanced.sidebar_link.all_site_settings"),
|
||||
I18n.t("admin_js.admin.account.sidebar_link.whats_new.title"),
|
||||
I18n.t("admin_js.admin.config.users.title"),
|
||||
I18n.t("admin_js.admin.config.groups.title"),
|
||||
I18n.t("admin_js.admin.config.site_settings.title"),
|
||||
I18n.t("admin_js.admin.config.whats_new.title"),
|
||||
],
|
||||
)
|
||||
end
|
||||
@ -107,9 +107,9 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
links = page.all(".sidebar-section-link-content-text")
|
||||
expect(links.map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.community.sidebar_link.user_fields"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.moderation_flags.title"),
|
||||
I18n.t("admin_js.admin.email_settings.sidebar_link.server_setup.title"),
|
||||
I18n.t("admin_js.admin.config.user_fields.title"),
|
||||
I18n.t("admin_js.admin.config.flags.title"),
|
||||
I18n.t("admin_js.admin.config.email.title"),
|
||||
],
|
||||
)
|
||||
expect(page).to have_no_css(".sidebar-no-results")
|
||||
@ -125,12 +125,13 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
expect(page).to have_css(".sidebar-sections__back-to-forum")
|
||||
|
||||
# When match section title, display all links
|
||||
filter.filter("Email Sett")
|
||||
filter.filter("Email")
|
||||
links = page.all(".sidebar-section-link-content-text")
|
||||
expect(links.map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.email_settings.sidebar_link.server_setup.title"),
|
||||
I18n.t("admin_js.admin.email_settings.sidebar_link.appearance"),
|
||||
I18n.t("admin_js.admin.config.email.title"),
|
||||
I18n.t("admin_js.admin.config.email_appearance.title"),
|
||||
I18n.t("admin_js.admin.config.staff_action_logs.title"),
|
||||
],
|
||||
)
|
||||
end
|
||||
@ -192,10 +193,10 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
expect(links.map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.dashboard.title"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.users"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.groups"),
|
||||
I18n.t("admin_js.admin.advanced.sidebar_link.all_site_settings"),
|
||||
I18n.t("admin_js.admin.account.sidebar_link.whats_new.title"),
|
||||
I18n.t("admin_js.admin.config.users.title"),
|
||||
I18n.t("admin_js.admin.config.groups.title"),
|
||||
I18n.t("admin_js.admin.config.site_settings.title"),
|
||||
I18n.t("admin_js.admin.config.whats_new.title"),
|
||||
],
|
||||
)
|
||||
|
||||
@ -203,9 +204,9 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
links = page.all(".sidebar-section-link-content-text")
|
||||
expect(links.map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.community.sidebar_link.user_fields"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.moderation_flags.title"),
|
||||
I18n.t("admin_js.admin.email_settings.sidebar_link.server_setup.title"),
|
||||
I18n.t("admin_js.admin.config.user_fields.title"),
|
||||
I18n.t("admin_js.admin.config.flags.title"),
|
||||
I18n.t("admin_js.admin.config.email.title"),
|
||||
],
|
||||
)
|
||||
|
||||
@ -214,10 +215,10 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
expect(links.map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.dashboard.title"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.users"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.groups"),
|
||||
I18n.t("admin_js.admin.advanced.sidebar_link.all_site_settings"),
|
||||
I18n.t("admin_js.admin.account.sidebar_link.whats_new.title"),
|
||||
I18n.t("admin_js.admin.config.users.title"),
|
||||
I18n.t("admin_js.admin.config.groups.title"),
|
||||
I18n.t("admin_js.admin.config.site_settings.title"),
|
||||
I18n.t("admin_js.admin.config.whats_new.title"),
|
||||
],
|
||||
)
|
||||
end
|
||||
@ -265,10 +266,10 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
expect(all(".sidebar-section-link-content-text").map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.dashboard.title"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.users"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.groups"),
|
||||
I18n.t("admin_js.admin.advanced.sidebar_link.all_site_settings"),
|
||||
I18n.t("admin_js.admin.account.sidebar_link.whats_new.title"),
|
||||
I18n.t("admin_js.admin.config.users.title"),
|
||||
I18n.t("admin_js.admin.config.groups.title"),
|
||||
I18n.t("admin_js.admin.config.site_settings.title"),
|
||||
I18n.t("admin_js.admin.config.whats_new.title"),
|
||||
],
|
||||
)
|
||||
|
||||
@ -290,7 +291,7 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
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"])
|
||||
expect(links.map(&:text)).to eq([I18n.t("admin_js.admin.config.plugins.title")])
|
||||
end
|
||||
|
||||
it "accepts components and themes keywords for filter" do
|
||||
@ -328,12 +329,12 @@ describe "Admin | Sidebar Navigation", type: :system do
|
||||
expect(links.map(&:text)).to eq(
|
||||
[
|
||||
I18n.t("admin_js.admin.dashboard.title"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.users"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.groups"),
|
||||
I18n.t("admin_js.admin.account.sidebar_link.whats_new.title"),
|
||||
I18n.t("admin_js.admin.reports.sidebar_link.all"),
|
||||
I18n.t("admin_js.admin.community.sidebar_link.watched_words"),
|
||||
I18n.t("admin_js.admin.security.sidebar_link.staff_action_logs.title"),
|
||||
I18n.t("admin_js.admin.config.users.title"),
|
||||
I18n.t("admin_js.admin.config.groups.title"),
|
||||
I18n.t("admin_js.admin.config.whats_new.title"),
|
||||
I18n.t("admin_js.admin.config.reports.title"),
|
||||
I18n.t("admin_js.admin.config.watched_words.title"),
|
||||
I18n.t("admin_js.admin.config.staff_action_logs.title"),
|
||||
],
|
||||
)
|
||||
end
|
||||
|
Reference in New Issue
Block a user