diff --git a/pkg/services/navtree/models.go b/pkg/services/navtree/models.go
index 8f521be5a46..728dc6cda51 100644
--- a/pkg/services/navtree/models.go
+++ b/pkg/services/navtree/models.go
@@ -41,6 +41,9 @@ const (
NavIDDashboardsBrowse = "dashboards/browse"
NavIDCfg = "cfg" // NavIDCfg is the id for org configuration navigation node
NavIDAdmin = "admin"
+ NavIDAdminGeneral = "admin/general"
+ NavIDAdminPlugins = "admin/plugins"
+ NavIDAdminAccess = "admin/access"
NavIDAlertsAndIncidents = "alerts-and-incidents"
NavIDAlerting = "alerting"
NavIDMonitoring = "monitoring"
@@ -111,22 +114,7 @@ func (root *NavTreeRoot) RemoveEmptySectionsAndApplyNewInformationArchitecture(t
}
if topNavEnabled {
- orgAdminNode := root.FindById(NavIDCfg)
-
- if orgAdminNode != nil {
- orgAdminNode.Url = "/admin"
- orgAdminNode.Text = "Administration"
- }
-
- if serverAdminNode := root.FindById(NavIDAdmin); serverAdminNode != nil {
- serverAdminNode.Url = "/admin/server"
- serverAdminNode.SortWeight = 0
-
- if orgAdminNode != nil {
- orgAdminNode.Children = append(orgAdminNode.Children, serverAdminNode)
- root.RemoveSection(serverAdminNode)
- }
- }
+ ApplyAdminIA(root)
// Move reports into dashboards
if reports := root.FindById(NavIDReporting); reports != nil {
@@ -181,6 +169,99 @@ func Sort(nodes []*NavLink) {
}
}
+func ApplyAdminIA(root *NavTreeRoot) {
+ orgAdminNode := root.FindById(NavIDCfg)
+
+ if orgAdminNode != nil {
+ orgAdminNode.Url = "/admin"
+ orgAdminNode.Text = "Administration"
+
+ generalNodeLinks := []*NavLink{}
+ pluginsNodeLinks := []*NavLink{}
+ accessNodeLinks := []*NavLink{}
+
+ generalNodeLinks = AppendIfNotNil(generalNodeLinks, root.FindById("upgrading"))
+ if orgSettings := root.FindById("org-settings"); orgSettings != nil {
+ orgSettings.Text = "Default preferences"
+ generalNodeLinks = append(generalNodeLinks, orgSettings)
+ }
+ generalNodeLinks = AppendIfNotNil(generalNodeLinks, root.FindById("global-orgs"))
+ generalNodeLinks = AppendIfNotNil(generalNodeLinks, root.FindById("server-settings"))
+
+ pluginsNodeLinks = AppendIfNotNil(pluginsNodeLinks, root.FindById("plugins"))
+ pluginsNodeLinks = AppendIfNotNil(pluginsNodeLinks, root.FindById("datasources"))
+ pluginsNodeLinks = AppendIfNotNil(pluginsNodeLinks, root.FindById("correlations"))
+ pluginsNodeLinks = AppendIfNotNil(pluginsNodeLinks, root.FindById("plugin-page-grafana-cloud-link-app"))
+ pluginsNodeLinks = AppendIfNotNil(pluginsNodeLinks, root.FindById("recordedQueries")) // enterprise only
+
+ accessNodeLinks = AppendIfNotNil(accessNodeLinks, root.FindById("users"))
+ if globalUsers := root.FindById("global-users"); globalUsers != nil {
+ globalUsers.Text = "Users (All orgs)"
+ accessNodeLinks = append(accessNodeLinks, globalUsers)
+ }
+ accessNodeLinks = AppendIfNotNil(accessNodeLinks, root.FindById("teams"))
+ accessNodeLinks = AppendIfNotNil(accessNodeLinks, root.FindById("serviceaccounts"))
+ accessNodeLinks = AppendIfNotNil(accessNodeLinks, root.FindById("apikeys"))
+ accessNodeLinks = AppendIfNotNil(accessNodeLinks, root.FindById("plugin-page-grafana-auth-app")) // Cloud Access Policies
+
+ generalNode := &NavLink{
+ Text: "General",
+ Id: NavIDAdminGeneral,
+ Url: "/admin/general",
+ Icon: "shield",
+ Children: generalNodeLinks,
+ }
+
+ pluginsNode := &NavLink{
+ Text: "Plugins and data",
+ Id: NavIDAdminPlugins,
+ Url: "/admin/plugins",
+ Icon: "shield",
+ Children: pluginsNodeLinks,
+ }
+
+ accessNode := &NavLink{
+ Text: "Users and access",
+ Id: NavIDAdminAccess,
+ Url: "/admin/access",
+ Icon: "shield",
+ Children: accessNodeLinks,
+ }
+
+ adminNodeLinks := []*NavLink{}
+
+ if len(generalNode.Children) > 0 {
+ adminNodeLinks = append(adminNodeLinks, generalNode)
+ }
+
+ if len(pluginsNode.Children) > 0 {
+ adminNodeLinks = append(adminNodeLinks, pluginsNode)
+ }
+
+ if len(accessNode.Children) > 0 {
+ adminNodeLinks = append(adminNodeLinks, accessNode)
+ }
+
+ if len(adminNodeLinks) > 0 {
+ orgAdminNode.Children = adminNodeLinks
+ } else {
+ root.RemoveSection(orgAdminNode)
+ }
+ }
+
+ if serverAdminNode := root.FindById(NavIDAdmin); serverAdminNode != nil {
+ root.RemoveSection(serverAdminNode)
+ }
+}
+
+func AppendIfNotNil(children []*NavLink, newChild *NavLink) []*NavLink {
+ if newChild != nil {
+ return append(children, newChild)
+ }
+
+ return children
+}
+
func FindById(nodes []*NavLink, id string) *NavLink {
for _, child := range nodes {
if child.Id == id {
diff --git a/pkg/services/navtree/models_test.go b/pkg/services/navtree/models_test.go
index 21cd52a8dce..66f8a01ab55 100644
--- a/pkg/services/navtree/models_test.go
+++ b/pkg/services/navtree/models_test.go
@@ -33,18 +33,20 @@ func TestNavTreeRoot(t *testing.T) {
require.Equal(t, 2, len(treeRoot.Children))
})
- t.Run("Should move admin section into cfg and rename when topnav is enabled", func(t *testing.T) {
+ t.Run("Should create 3 new sections in the Admin node when topnav is enabled", func(t *testing.T) {
treeRoot := NavTreeRoot{
Children: []*NavLink{
{Id: NavIDCfg},
- {Id: NavIDAdmin, Children: []*NavLink{{Id: "child"}}},
+ {Id: NavIDAdmin, Children: []*NavLink{{Id: "upgrading"}, {Id: "plugins"}, {Id: "teams"}}},
},
}
treeRoot.RemoveEmptySectionsAndApplyNewInformationArchitecture(true)
require.Equal(t, "Administration", treeRoot.Children[0].Text)
- require.Equal(t, NavIDAdmin, treeRoot.Children[0].Children[0].Id)
+ require.Equal(t, NavIDAdminGeneral, treeRoot.Children[0].Children[0].Id)
+ require.Equal(t, NavIDAdminPlugins, treeRoot.Children[0].Children[1].Id)
+ require.Equal(t, NavIDAdminAccess, treeRoot.Children[0].Children[2].Id)
})
t.Run("Should move reports into Dashboards", func(t *testing.T) {
diff --git a/pkg/services/navtree/navtreeimpl/admin.go b/pkg/services/navtree/navtreeimpl/admin.go
index c024aeb7f97..7bf7a95ec46 100644
--- a/pkg/services/navtree/navtreeimpl/admin.go
+++ b/pkg/services/navtree/navtreeimpl/admin.go
@@ -166,16 +166,8 @@ func (s *ServiceImpl) getServerAdminNode(c *models.ReqContext) *navtree.NavLink
Children: adminNavLinks,
}
- if s.cfg.IsFeatureToggleEnabled(featuremgmt.FlagTopnav) {
- adminNode.SubTitle = "Manage server-wide settings and access to resources such as organizations, users, and licenses"
- }
-
if len(adminNavLinks) > 0 {
- if s.cfg.IsFeatureToggleEnabled(featuremgmt.FlagTopnav) {
- adminNode.Url = s.cfg.AppSubURL + "/admin/server"
- } else {
- adminNode.Url = adminNavLinks[0].Url
- }
+ adminNode.Url = adminNavLinks[0].Url
}
return adminNode
diff --git a/pkg/services/navtree/navtreeimpl/applinks.go b/pkg/services/navtree/navtreeimpl/applinks.go
index e59a4d6bd09..189315b8e1f 100644
--- a/pkg/services/navtree/navtreeimpl/applinks.go
+++ b/pkg/services/navtree/navtreeimpl/applinks.go
@@ -262,6 +262,7 @@ func (s *ServiceImpl) readNavigationSettings() {
"grafana-incident-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 2, Text: "Incident"},
"grafana-ml-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 3, Text: "Machine Learning"},
"grafana-cloud-link-app": {SectionID: navtree.NavIDCfg},
+ "grafana-auth-app": {SectionID: navtree.NavIDCfg},
"grafana-easystart-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightSavedItems + 1, Text: "Connections"},
}
diff --git a/public/app/core/components/NavBar/navBarItem-translations.ts b/public/app/core/components/NavBar/navBarItem-translations.ts
index db6a018ebb4..ea674073cf7 100644
--- a/public/app/core/components/NavBar/navBarItem-translations.ts
+++ b/public/app/core/components/NavBar/navBarItem-translations.ts
@@ -69,6 +69,12 @@ export function getNavTitle(navId: string | undefined) {
return config.featureToggles.topnav
? t('nav.config.title', 'Administration')
: t('nav.config.titleBeforeTopnav', 'Configuration');
+ case 'admin/general':
+ return t('nav.admin-general.title', 'General');
+ case 'admin/plugins':
+ return t('nav.admin-plugins.title', 'Plugins and data');
+ case 'admin/access':
+ return t('nav.admin-access.title', 'Users and access');
case 'datasources':
return t('nav.datasources.title', 'Data sources');
case 'correlations':
@@ -80,7 +86,9 @@ export function getNavTitle(navId: string | undefined) {
case 'plugins':
return t('nav.plugins.title', 'Plugins');
case 'org-settings':
- return t('nav.org-settings.title', 'Preferences');
+ return config.featureToggles.topnav
+ ? t('nav.org-settings.title', 'Default preferences')
+ : t('nav.org-settings.titleBeforeTopnav', 'Preferences');
case 'apikeys':
return t('nav.api-keys.title', 'API keys');
case 'serviceaccounts':
@@ -88,7 +96,9 @@ export function getNavTitle(navId: string | undefined) {
case 'admin':
return t('nav.admin.title', 'Server admin');
case 'global-users':
- return t('nav.global-users.title', 'Users');
+ return config.featureToggles.topnav
+ ? t('nav.global-users.title', 'Users (All orgs)')
+ : t('nav.global-users.titleBeforeTopnav', 'Users');
case 'global-orgs':
return t('nav.global-orgs.title', 'Organizations');
case 'server-settings':
diff --git a/public/app/routes/routes.tsx b/public/app/routes/routes.tsx
index 17b5d38a063..9fda8c87041 100644
--- a/public/app/routes/routes.tsx
+++ b/public/app/routes/routes.tsx
@@ -38,6 +38,18 @@ export function getAppRoutes(): RouteDescriptor[] {
path: '/monitoring',
component: () => ,
},
+ {
+ path: '/admin/general',
+ component: () => ,
+ },
+ {
+ path: '/admin/plugins',
+ component: () => ,
+ },
+ {
+ path: '/admin/access',
+ component: () => ,
+ },
]
: [];
@@ -294,9 +306,14 @@ export function getAppRoutes(): RouteDescriptor[] {
component: () => (config.featureToggles.topnav ? : ),
},
{
- path: '/admin/server',
+ path: '/admin/access',
component: () =>
- config.featureToggles.topnav ? : ,
+ config.featureToggles.topnav ? : ,
+ },
+ {
+ path: '/admin/config',
+ component: () =>
+ config.featureToggles.topnav ? : ,
},
{
path: '/admin/settings',
diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json
index 940eb77aa34..e3d9a9abaab 100644
--- a/public/locales/de-DE/grafana.json
+++ b/public/locales/de-DE/grafana.json
@@ -99,6 +99,15 @@
"subtitle": "Serverweite Einstellungen und Zugriff auf Ressourcen wie Organisationen, Benutzer und Lizenzen verwalten",
"title": "Server-Administrator"
},
+ "admin-access": {
+ "title": ""
+ },
+ "admin-general": {
+ "title": ""
+ },
+ "admin-plugins": {
+ "title": ""
+ },
"alerting": {
"subtitle": "Informiere dich über Probleme in deinen Systemen kurz nach deren Auftreten",
"title": "Meldungen"
@@ -184,7 +193,8 @@
},
"global-users": {
"subtitle": "Erstelle und verwalte Benutzer auf dem gesamten Grafana-Server",
- "title": "Benutzer"
+ "title": "Benutzer",
+ "titleBeforeTopnav": ""
},
"help": {
"title": "Hilfe"
@@ -227,7 +237,8 @@
},
"org-settings": {
"subtitle": "Verwalte Einstellungen in der gesamten Organisation",
- "title": "Einstellungen"
+ "title": "Einstellungen",
+ "titleBeforeTopnav": ""
},
"playlists": {
"subtitle": "Gruppen von Dashboards, die in einer bestimmten Reihenfolge angezeigt werden",
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index c3d7bab249e..2a68c15a74c 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -99,6 +99,15 @@
"subtitle": "Manage server-wide settings and access to resources such as organizations, users, and licenses",
"title": "Server admin"
},
+ "admin-access": {
+ "title": "Users and access"
+ },
+ "admin-general": {
+ "title": "General"
+ },
+ "admin-plugins": {
+ "title": "Plugins and data"
+ },
"alerting": {
"subtitle": "Learn about problems in your systems moments after they occur",
"title": "Alerting"
@@ -184,7 +193,8 @@
},
"global-users": {
"subtitle": "Manage and create users across the whole Grafana server",
- "title": "Users"
+ "title": "Users (All orgs)",
+ "titleBeforeTopnav": "Users"
},
"help": {
"title": "Help"
@@ -227,7 +237,8 @@
},
"org-settings": {
"subtitle": "Manage preferences across an organization",
- "title": "Preferences"
+ "title": "Default preferences",
+ "titleBeforeTopnav": "Preferences"
},
"playlists": {
"subtitle": "Groups of dashboards that are displayed in a sequence",
diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json
index b6020ca1118..f3bd4e0cc7b 100644
--- a/public/locales/es-ES/grafana.json
+++ b/public/locales/es-ES/grafana.json
@@ -99,6 +99,15 @@
"subtitle": "Administrar la configuración de todo el servidor y el acceso a recursos como organizaciones, usuarios y licencias",
"title": "Administrador del servidor"
},
+ "admin-access": {
+ "title": ""
+ },
+ "admin-general": {
+ "title": ""
+ },
+ "admin-plugins": {
+ "title": ""
+ },
"alerting": {
"subtitle": "Conozca los problemas de sus sistemas justo después de que se produzcan",
"title": "Alertas"
@@ -184,7 +193,8 @@
},
"global-users": {
"subtitle": "Gestione y cree usuarios en todo el servidor Grafana",
- "title": "Usuarios"
+ "title": "Usuarios",
+ "titleBeforeTopnav": ""
},
"help": {
"title": "Ayuda"
@@ -227,7 +237,8 @@
},
"org-settings": {
"subtitle": "Gestionar las preferencias en una organización",
- "title": "Preferencias"
+ "title": "Preferencias",
+ "titleBeforeTopnav": ""
},
"playlists": {
"subtitle": "Grupos de paneles de control que se muestran en una secuencia",
diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json
index 50ee9d5efa8..1bcdb9254ca 100644
--- a/public/locales/fr-FR/grafana.json
+++ b/public/locales/fr-FR/grafana.json
@@ -99,6 +99,15 @@
"subtitle": "Gérer les paramètres à l'échelle du serveur et l'accès aux ressources telles que les organisations, les utilisateurs et les licences",
"title": "Administrateur de serveur"
},
+ "admin-access": {
+ "title": ""
+ },
+ "admin-general": {
+ "title": ""
+ },
+ "admin-plugins": {
+ "title": ""
+ },
"alerting": {
"subtitle": "En savoir plus sur les problèmes dans vos systèmes quelques instants après qu'ils se produisent",
"title": "Alertes"
@@ -184,7 +193,8 @@
},
"global-users": {
"subtitle": "Gérer et créer des utilisateurs sur l'ensemble du serveur Grafana",
- "title": "Utilisateurs"
+ "title": "Utilisateurs",
+ "titleBeforeTopnav": ""
},
"help": {
"title": "Aide"
@@ -227,7 +237,8 @@
},
"org-settings": {
"subtitle": "Gérer les préférences au sein d'une organisation",
- "title": "Préférences"
+ "title": "Préférences",
+ "titleBeforeTopnav": ""
},
"playlists": {
"subtitle": "Groupes de tableaux de bord affichés dans une séquence",
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index cef669c3987..1ec222378bf 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -99,6 +99,15 @@
"subtitle": "Mäʼnäģę şęřvęř-ŵįđę şęŧŧįʼnģş äʼnđ äččęşş ŧő řęşőūřčęş şūčĥ äş őřģäʼnįžäŧįőʼnş, ūşęřş, äʼnđ ľįčęʼnşęş",
"title": "Ŝęřvęř äđmįʼn"
},
+ "admin-access": {
+ "title": "Ůşęřş äʼnđ äččęşş"
+ },
+ "admin-general": {
+ "title": "Ğęʼnęřäľ"
+ },
+ "admin-plugins": {
+ "title": "Pľūģįʼnş äʼnđ đäŧä"
+ },
"alerting": {
"subtitle": "Ŀęäřʼn äþőūŧ přőþľęmş įʼn yőūř şyşŧęmş mőmęʼnŧş äƒŧęř ŧĥęy őččūř",
"title": "Åľęřŧįʼnģ"
@@ -184,7 +193,8 @@
},
"global-users": {
"subtitle": "Mäʼnäģę äʼnđ čřęäŧę ūşęřş äčřőşş ŧĥę ŵĥőľę Ğřäƒäʼnä şęřvęř",
- "title": "Ůşęřş"
+ "title": "Ůşęřş (Åľľ őřģş)",
+ "titleBeforeTopnav": "Ůşęřş"
},
"help": {
"title": "Ħęľp"
@@ -227,7 +237,8 @@
},
"org-settings": {
"subtitle": "Mäʼnäģę přęƒęřęʼnčęş äčřőşş äʼn őřģäʼnįžäŧįőʼn",
- "title": "Přęƒęřęʼnčęş"
+ "title": "Đęƒäūľŧ přęƒęřęʼnčęş",
+ "titleBeforeTopnav": "Přęƒęřęʼnčęş"
},
"playlists": {
"subtitle": "Ğřőūpş őƒ đäşĥþőäřđş ŧĥäŧ äřę đįşpľäyęđ įʼn ä şęqūęʼnčę",
diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json
index c1623c71043..4122d706209 100644
--- a/public/locales/zh-Hans/grafana.json
+++ b/public/locales/zh-Hans/grafana.json
@@ -99,6 +99,15 @@
"subtitle": "管理整个服务器范围的设置,以及对组织、用户和许可证等资源的访问权限",
"title": "服务器管理员"
},
+ "admin-access": {
+ "title": ""
+ },
+ "admin-general": {
+ "title": ""
+ },
+ "admin-plugins": {
+ "title": ""
+ },
"alerting": {
"subtitle": "在系统发生问题后立即获悉",
"title": "警报"
@@ -184,7 +193,8 @@
},
"global-users": {
"subtitle": "在整个 Grafana 服务器上管理和创建用户",
- "title": "用户"
+ "title": "用户",
+ "titleBeforeTopnav": ""
},
"help": {
"title": "帮助"
@@ -227,7 +237,8 @@
},
"org-settings": {
"subtitle": "管理整个组织的首选项",
- "title": "首选项"
+ "title": "首选项",
+ "titleBeforeTopnav": ""
},
"playlists": {
"subtitle": "以序列显示的仪表板组",