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}} {{#if this.commitHash}}
<a <a
href={{@plugin.commit_url}} href={{@plugin.commitUrl}}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="current commit-hash" class="current commit-hash"

View File

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

View File

@ -5,20 +5,22 @@ export default class AdminPluginsController extends Controller {
@service router; @service router;
get adminRoutes() { get adminRoutes() {
return this.allAdminRoutes.filter((r) => this.routeExists(r.full_location)); return this.allAdminRoutes.filter((route) =>
this.routeExists(route.full_location)
);
} }
get brokenAdminRoutes() { get brokenAdminRoutes() {
return this.allAdminRoutes.filter( return this.allAdminRoutes.filter(
(r) => !this.routeExists(r.full_location) (route) => !this.routeExists(route.full_location)
); );
} }
get allAdminRoutes() { get allAdminRoutes() {
return this.model return this.model
.filter((p) => p?.enabled) .filter((plugin) => plugin?.enabled)
.map((p) => { .map((plugin) => {
return p.admin_route; return plugin.adminRoute;
}) })
.filter(Boolean); .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 Route from "@ember/routing/route";
import AdminPlugin from "admin/models/admin-plugin";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
export default class AdminPluginsRoute extends Route { export default class AdminPluginsRoute extends Route {
@service router; @service router;
model() { model() {
return this.store.findAll("plugin"); return this.store
} .findAll("plugin")
.then((plugins) => plugins.map((plugin) => AdminPlugin.create(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");
}
});
} }
} }

View File

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