diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
index ab9db38b18d..e657ac6a741 100644
--- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
+++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
@@ -217,6 +217,7 @@ Experimental features might be changed or removed without prior notice.
| `prometheusUsesCombobox` | Use new combobox component for Prometheus query editor |
| `dashboardSchemaV2` | Enables the new dashboard schema version 2, implementing changes necessary for dynamic dashboards and dashboards as code. |
| `playlistsWatcher` | Enables experimental watcher for playlists |
+| `enableExtensionsAdminPage` | Enables the extension admin page regardless of development mode |
## Development feature toggles
diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts
index e1dc98f8c4d..5659ac3d70a 100644
--- a/packages/grafana-data/src/types/featureToggles.gen.ts
+++ b/packages/grafana-data/src/types/featureToggles.gen.ts
@@ -228,4 +228,5 @@ export interface FeatureToggles {
azureMonitorDisableLogLimit?: boolean;
dashboardSchemaV2?: boolean;
playlistsWatcher?: boolean;
+ enableExtensionsAdminPage?: boolean;
}
diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go
index ef8f5f1c0c6..cfcc24edd7e 100644
--- a/pkg/services/featuremgmt/registry.go
+++ b/pkg/services/featuremgmt/registry.go
@@ -1570,6 +1570,13 @@ var (
Owner: grafanaAppPlatformSquad,
RequiresRestart: true,
},
+ {
+ Name: "enableExtensionsAdminPage",
+ Description: "Enables the extension admin page regardless of development mode",
+ Stage: FeatureStageExperimental,
+ Owner: grafanaPluginsPlatformSquad,
+ RequiresRestart: true,
+ },
}
)
diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv
index f4ce89233c3..d9b8866677b 100644
--- a/pkg/services/featuremgmt/toggles_gen.csv
+++ b/pkg/services/featuremgmt/toggles_gen.csv
@@ -209,3 +209,4 @@ prometheusUsesCombobox,experimental,@grafana/observability-metrics,false,false,f
azureMonitorDisableLogLimit,GA,@grafana/partner-datasources,false,false,false
dashboardSchemaV2,experimental,@grafana/dashboards-squad,false,false,true
playlistsWatcher,experimental,@grafana/grafana-app-platform-squad,false,true,false
+enableExtensionsAdminPage,experimental,@grafana/plugins-platform-backend,false,true,false
diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go
index 248b5c8004b..21a69174c78 100644
--- a/pkg/services/featuremgmt/toggles_gen.go
+++ b/pkg/services/featuremgmt/toggles_gen.go
@@ -846,4 +846,8 @@ const (
// FlagPlaylistsWatcher
// Enables experimental watcher for playlists
FlagPlaylistsWatcher = "playlistsWatcher"
+
+ // FlagEnableExtensionsAdminPage
+ // Enables the extension admin page regardless of development mode
+ FlagEnableExtensionsAdminPage = "enableExtensionsAdminPage"
)
diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json
index 09cf086e4a1..90efd2f74c8 100644
--- a/pkg/services/featuremgmt/toggles_gen.json
+++ b/pkg/services/featuremgmt/toggles_gen.json
@@ -1201,6 +1201,22 @@
"frontend": true
}
},
+ {
+ "metadata": {
+ "name": "enableExtensionsAdminPage",
+ "resourceVersion": "1730819353237",
+ "creationTimestamp": "2024-11-05T09:18:42Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2024-11-05 15:09:13.237578 +0000 UTC"
+ }
+ },
+ "spec": {
+ "description": "Enables the extension admin page regardless of development mode",
+ "stage": "experimental",
+ "codeowner": "@grafana/plugins-platform-backend",
+ "requiresRestart": true
+ }
+ },
{
"metadata": {
"name": "enableNativeHTTPHistogram",
diff --git a/pkg/services/navtree/navtreeimpl/admin.go b/pkg/services/navtree/navtreeimpl/admin.go
index 18a71f24dde..66f50147643 100644
--- a/pkg/services/navtree/navtreeimpl/admin.go
+++ b/pkg/services/navtree/navtreeimpl/admin.go
@@ -105,7 +105,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
})
}
- if s.cfg.Env == setting.Dev {
+ if (s.cfg.Env == setting.Dev) || s.features.IsEnabled(ctx, featuremgmt.FlagEnableExtensionsAdminPage) && hasAccess(pluginaccesscontrol.AdminAccessEvaluator) {
pluginsNodeLinks = append(pluginsNodeLinks, &navtree.NavLink{
Text: "Extensions",
Icon: "plug",
diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx
index dd1a0a4967a..a3a88ca83b8 100644
--- a/public/app/routes/routes.tsx
+++ b/public/app/routes/routes.tsx
@@ -203,11 +203,15 @@ export function getAppRoutes(): RouteDescriptor[] {
{
path: '/admin/extensions',
navId: 'extensions',
- component: isDevEnv
- ? SafeDynamicImport(
- () => import(/* webpackChunkName: "PluginExtensionsLog" */ 'app/features/plugins/extensions/logs/LogViewer')
- )
- : () => ,
+ roles: () =>
+ contextSrv.evaluatePermission([AccessControlAction.PluginsInstall, AccessControlAction.PluginsWrite]),
+ component:
+ isDevEnv || config.featureToggles.enableExtensionsAdminPage
+ ? SafeDynamicImport(
+ () =>
+ import(/* webpackChunkName: "PluginExtensionsLog" */ 'app/features/plugins/extensions/logs/LogViewer')
+ )
+ : () => ,
},
{
path: '/admin/access',