FIX: Improve admin plugin list and modernize code (#23256)

This commit contains a few improvements:

* Use LinkTo instead of a button with a weird action referencing the
  controller to navigate to the filtered settings for a plugin
* Add an AdminPlugin model with tracked properties and use that when
  toggling the setting on/off and in the templates
* Make it so the Settings button for a plugin navigates to the correct
  site setting category instead of always just going to the generic
  "plugins" one if possible
This commit is contained in:
Martin Brennan 2023-08-28 10:48:59 +10:00 committed by GitHub
parent 4b52269827
commit 5fc93b95cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 42 deletions

View File

@ -1,6 +1,6 @@
{{#if this.commitHash}}
<a
href={{@plugin.commit_url}}
href={{@plugin.commitUrl}}
target="_blank"
rel="noopener noreferrer"
class="current commit-hash"

View File

@ -6,6 +6,6 @@ export default class PluginCommitHash extends Component {
}
get commitHash() {
return this.args.plugin.commit_hash;
return this.args.plugin.commitHash;
}
}

View File

@ -1,5 +1,5 @@
import Controller from "@ember/controller";
import { action, set } from "@ember/object";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import SiteSetting from "admin/models/site-setting";
import { popupAjaxError } from "discourse/lib/ajax-error";
@ -9,16 +9,15 @@ export default class AdminPluginsIndexController extends Controller {
@action
async togglePluginEnabled(plugin) {
const enabledSettingName = plugin.enabled_setting;
const oldValue = plugin.enabled;
const newValue = !oldValue;
try {
set(plugin, "enabled", newValue);
await SiteSetting.update(enabledSettingName, newValue);
plugin.enabled = newValue;
await SiteSetting.update(plugin.enabledSetting, newValue);
this.session.requiresRefresh = true;
} catch (e) {
set(plugin, "enabled", oldValue);
plugin.enabled = oldValue;
popupAjaxError(e);
}
}

View File

@ -5,20 +5,22 @@ export default class AdminPluginsController extends Controller {
@service router;
get adminRoutes() {
return this.allAdminRoutes.filter((r) => this.routeExists(r.full_location));
return this.allAdminRoutes.filter((route) =>
this.routeExists(route.full_location)
);
}
get brokenAdminRoutes() {
return this.allAdminRoutes.filter(
(r) => !this.routeExists(r.full_location)
(route) => !this.routeExists(route.full_location)
);
}
get allAdminRoutes() {
return this.model
.filter((p) => p?.enabled)
.map((p) => {
return p.admin_route;
.filter((plugin) => plugin?.enabled)
.map((plugin) => {
return plugin.adminRoute;
})
.filter(Boolean);
}

View File

@ -0,0 +1,46 @@
import { tracked } from "@glimmer/tracking";
import I18n from "I18n";
export default class AdminPlugin {
static create(args = {}) {
return new AdminPlugin(args);
}
@tracked enabled;
constructor(args = {}) {
this.about = args.about;
this.adminRoute = args.admin_route;
this.commitHash = args.commit_hash;
this.commitUrl = args.commit_url;
this.enabled = args.enabled;
this.enabledSetting = args.enabled_setting;
this.hasSettings = args.has_settings;
this.id = args.id;
this.isOfficial = args.is_official;
this.name = args.name;
this.url = args.url;
this.version = args.version;
}
get settingCategoryName() {
const snakeCaseName = this.name.replaceAll("-", "_");
// We do this because the site setting list is grouped by category,
// with plugins that have their root site setting key defined as `plugins:`
// being grouped under the generic "plugins" category.
//
// If a site setting has defined a proper root key and translated category name,
// we can use that instead to go directly to the setting category.
//
// Over time, no plugins should be missing this data.
const translationAttempt = I18n.lookup(
`admin.site_settings.categories.${snakeCaseName}`
);
if (translationAttempt) {
return snakeCaseName;
}
return "plugins";
}
}

View File

@ -1,28 +1,13 @@
import { action } from "@ember/object";
import Route from "@ember/routing/route";
import AdminPlugin from "admin/models/admin-plugin";
import { inject as service } from "@ember/service";
export default class AdminPluginsRoute extends Route {
@service router;
model() {
return this.store.findAll("plugin");
}
@action
showSettings(plugin) {
const controller = this.controllerFor("adminSiteSettings");
this.router
.transitionTo("adminSiteSettingsCategory", "plugins")
.then(() => {
if (plugin) {
// filterContent() is normally on a debounce from typing.
// Because we don't want the default of "All Results", we tell it
// to skip the next debounce.
controller.set("filter", `plugin:${plugin.id}`);
controller.set("_skipBounce", true);
controller.filterContentNow("plugins");
}
});
return this.store
.findAll("plugin")
.then((plugins) => plugins.map((plugin) => AdminPlugin.create(plugin)));
}
}

View File

@ -15,7 +15,7 @@
{{#each this.model as |plugin|}}
<tr data-plugin-name={{plugin.name}}>
<td>
{{#if plugin.is_official}}
{{#if plugin.isOfficial}}
{{d-icon
"check-circle"
title="admin.plugins.official"
@ -47,7 +47,7 @@
</td>
<td class="col-enabled">
<div class="label">{{i18n "admin.plugins.enabled"}}</div>
{{#if plugin.enabled_setting}}
{{#if plugin.enabledSetting}}
<DToggleSwitch
@state={{plugin.enabled}}
{{on "click" (fn this.togglePluginEnabled plugin)}}
@ -58,14 +58,17 @@
</td>
<td class="settings">
{{#if this.currentUser.admin}}
{{#if plugin.has_settings}}
<DButton
@class="btn-default"
@action={{route-action "showSettings"}}
@actionParam={{plugin}}
@icon="cog"
@label="admin.plugins.change_settings_short"
/>
{{#if plugin.hasSettings}}
<LinkTo
class="btn-default btn btn-icon-text"
@route="adminSiteSettingsCategory"
@model={{plugin.settingCategoryName}}
@query={{hash filter=(concat "plugin:" plugin.name)}}
data-plugin-setting-button={{plugin.name}}
>
{{d-icon "cog"}}
{{i18n "admin.plugins.change_settings_short"}}
</LinkTo>
{{/if}}
{{/if}}
</td>