diff --git a/app/assets/javascripts/admin/addon/controllers/admin-plugins.js b/app/assets/javascripts/admin/addon/controllers/admin-plugins.js
index be663cadb15..34ffe45783d 100644
--- a/app/assets/javascripts/admin/addon/controllers/admin-plugins.js
+++ b/app/assets/javascripts/admin/addon/controllers/admin-plugins.js
@@ -1,17 +1,27 @@
import { action } from "@ember/object";
import Controller from "@ember/controller";
-import discourseComputed from "discourse-common/utils/decorators";
+import { inject as service } from "@ember/service";
export default Controller.extend({
- @discourseComputed
- adminRoutes() {
+ router: service(),
+
+ get adminRoutes() {
+ return this.allAdminRoutes.filter((r) => this.routeExists(r.full_location));
+ },
+
+ get brokenAdminRoutes() {
+ return this.allAdminRoutes.filter(
+ (r) => !this.routeExists(r.full_location)
+ );
+ },
+
+ get allAdminRoutes() {
return this.model
+ .filter((p) => p?.enabled)
.map((p) => {
- if (p.get("enabled")) {
- return p.admin_route;
- }
+ return p.admin_route;
})
- .compact();
+ .filter(Boolean);
},
@action
@@ -21,4 +31,13 @@ export default Controller.extend({
adminDetail.classList.toggle(state);
});
},
+
+ routeExists(routeName) {
+ try {
+ this.router.urlFor(routeName);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ },
});
diff --git a/app/assets/javascripts/admin/addon/templates/plugins.hbs b/app/assets/javascripts/admin/addon/templates/plugins.hbs
index 1425cf479ef..1baaaddd2d7 100644
--- a/app/assets/javascripts/admin/addon/templates/plugins.hbs
+++ b/app/assets/javascripts/admin/addon/templates/plugins.hbs
@@ -19,5 +19,11 @@
+ {{#each this.brokenAdminRoutes as |route|}}
+
+ {{i18n "admin.plugins.broken_route" name=(i18n route.label)}}
+
+ {{/each}}
+
{{outlet}}
diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-plugins-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-plugins-test.js
new file mode 100644
index 00000000000..61ca9fafc3c
--- /dev/null
+++ b/app/assets/javascripts/discourse/tests/acceptance/admin-plugins-test.js
@@ -0,0 +1,51 @@
+import {
+ acceptance,
+ exists,
+ query,
+} from "discourse/tests/helpers/qunit-helpers";
+import { visit } from "@ember/test-helpers";
+import { test } from "qunit";
+
+acceptance("Admin - Plugins", function (needs) {
+ needs.user();
+
+ needs.pretender((server, helper) => {
+ server.get("/admin/plugins", () =>
+ helper.response({
+ plugins: [
+ {
+ id: "some-test-plugin",
+ name: "some-test-plugin",
+ about: "Plugin description",
+ version: "0.1",
+ url: "https://example.com",
+ admin_route: {
+ location: "testlocation",
+ label: "test.plugin.label",
+ full_location: "adminPlugins.testlocation",
+ },
+ enabled: true,
+ enabled_setting: "testplugin_enabled",
+ has_settings: true,
+ is_official: true,
+ },
+ ],
+ })
+ );
+ });
+
+ test("shows plugin list", async function (assert) {
+ await visit("/admin/plugins");
+ const table = query("table.admin-plugins");
+ assert.strictEqual(
+ table.querySelector("tr .plugin-name .name").innerText,
+ "some-test-plugin",
+ "displays the plugin in the table"
+ );
+
+ assert.true(
+ exists(".admin-plugins .admin-detail .alert-error"),
+ "displays an error for unknown routes"
+ );
+ });
+});
diff --git a/app/assets/stylesheets/testem.scss b/app/assets/stylesheets/testem.scss
index cb117a88252..a1e4c5dad37 100644
--- a/app/assets/stylesheets/testem.scss
+++ b/app/assets/stylesheets/testem.scss
@@ -19,6 +19,7 @@ $love: #fa6c8d !default;
@import "common/foundation/mixins";
@import "desktop";
@import "color_definitions";
+@import "admin";
#ember-testing-container {
box-sizing: border-box;
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index fb29c37f874..0cc78869985 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -4639,6 +4639,7 @@ en:
change_settings_short: "Settings"
howto: "How do I install plugins?"
official: "Official Plugin"
+ broken_route: "Unable to configure link to '%{name}'. Ensure ad-blockers are disabled and try reloading the page."
backups:
title: "Backups"