2015-01-29 19:10:34 +08:00
package api
import (
2016-09-23 18:29:53 +08:00
"fmt"
2019-11-15 16:28:55 +08:00
"sort"
2016-07-05 23:59:43 +08:00
"strings"
2015-02-05 17:37:13 +08:00
"github.com/grafana/grafana/pkg/api/dtos"
2016-04-03 04:54:06 +08:00
"github.com/grafana/grafana/pkg/bus"
2015-11-20 16:43:10 +08:00
m "github.com/grafana/grafana/pkg/models"
2015-10-06 17:20:50 +08:00
"github.com/grafana/grafana/pkg/plugins"
2015-02-05 17:37:13 +08:00
"github.com/grafana/grafana/pkg/setting"
2015-01-29 19:10:34 +08:00
)
2018-09-22 16:50:00 +08:00
const (
// Themes
lightName = "light"
darkName = "dark"
)
2018-10-09 23:47:43 +08:00
func ( hs * HTTPServer ) setIndexViewData ( c * m . ReqContext ) ( * dtos . IndexViewData , error ) {
settings , err := hs . getFrontendSettingsMap ( c )
2015-01-29 19:10:34 +08:00
if err != nil {
2015-11-20 16:43:10 +08:00
return nil , err
2015-01-29 19:10:34 +08:00
}
2018-11-13 03:01:53 +08:00
prefsQuery := m . GetPreferencesWithDefaultsQuery { User : c . SignedInUser }
2016-04-03 04:54:06 +08:00
if err := bus . Dispatch ( & prefsQuery ) ; err != nil {
return nil , err
}
prefs := prefsQuery . Result
2016-07-05 23:59:43 +08:00
// Read locale from acccept-language
acceptLang := c . Req . Header . Get ( "Accept-Language" )
locale := "en-US"
if len ( acceptLang ) > 0 {
parts := strings . Split ( acceptLang , "," )
locale = parts [ 0 ]
}
2018-03-22 19:37:35 +08:00
appURL := setting . AppUrl
appSubURL := setting . AppSubUrl
2016-09-23 18:29:53 +08:00
// special case when doing localhost call from phantomjs
2020-01-06 15:12:18 +08:00
if c . IsRenderCall && ! hs . Cfg . ServeFromSubPath {
2018-03-22 19:37:35 +08:00
appURL = fmt . Sprintf ( "%s://localhost:%s" , setting . Protocol , setting . HttpPort )
appSubURL = ""
2016-09-23 18:29:53 +08:00
settings [ "appSubUrl" ] = ""
}
2018-04-30 21:34:31 +08:00
hasEditPermissionInFoldersQuery := m . HasEditPermissionInFoldersQuery { SignedInUser : c . SignedInUser }
if err := bus . Dispatch ( & hasEditPermissionInFoldersQuery ) ; err != nil {
return nil , err
}
2015-11-20 16:43:10 +08:00
var data = dtos . IndexViewData {
User : & dtos . CurrentUser {
2018-04-30 21:34:31 +08:00
Id : c . UserId ,
IsSignedIn : c . IsSignedIn ,
Login : c . Login ,
Email : c . Email ,
Name : c . Name ,
OrgCount : c . OrgCount ,
OrgId : c . OrgId ,
OrgName : c . OrgName ,
OrgRole : c . OrgRole ,
GravatarUrl : dtos . GetGravatarUrl ( c . Email ) ,
IsGrafanaAdmin : c . IsGrafanaAdmin ,
2018-09-22 16:50:00 +08:00
LightTheme : prefs . Theme == lightName ,
2018-04-30 21:34:31 +08:00
Timezone : prefs . Timezone ,
Locale : locale ,
HelpFlags1 : c . HelpFlags1 ,
HasEditPermissionInFolders : hasEditPermissionInFoldersQuery . Result ,
2015-11-20 16:43:10 +08:00
} ,
2016-05-03 15:00:58 +08:00
Settings : settings ,
2017-10-12 03:36:03 +08:00
Theme : prefs . Theme ,
2018-03-22 19:37:35 +08:00
AppUrl : appURL ,
AppSubUrl : appSubURL ,
2016-05-03 15:00:58 +08:00
GoogleAnalyticsId : setting . GoogleAnalyticsId ,
GoogleTagManagerId : setting . GoogleTagManagerId ,
BuildVersion : setting . BuildVersion ,
BuildCommit : setting . BuildCommit ,
NewGrafanaVersion : plugins . GrafanaLatestVersion ,
NewGrafanaVersionExists : plugins . GrafanaHasUpdate ,
2018-07-02 19:33:39 +08:00
AppName : setting . ApplicationName ,
2019-11-01 21:56:12 +08:00
AppNameBodyClass : getAppNameBodyClass ( hs . License . HasValidLicense ( ) ) ,
2015-01-29 19:10:34 +08:00
}
2015-05-01 14:40:13 +08:00
if setting . DisableGravatar {
2018-03-06 18:46:01 +08:00
data . User . GravatarUrl = setting . AppSubUrl + "/public/img/user_profile.png"
2015-05-01 14:40:13 +08:00
}
2015-11-20 16:43:10 +08:00
if len ( data . User . Name ) == 0 {
data . User . Name = data . User . Login
2015-02-03 17:46:52 +08:00
}
2018-03-22 19:37:35 +08:00
themeURLParam := c . Query ( "theme" )
2018-09-22 16:50:00 +08:00
if themeURLParam == lightName {
2015-11-20 16:43:10 +08:00
data . User . LightTheme = true
2018-09-22 16:50:00 +08:00
data . Theme = lightName
} else if themeURLParam == darkName {
2018-09-12 12:52:38 +08:00
data . User . LightTheme = false
2018-09-22 16:50:00 +08:00
data . Theme = darkName
2015-04-02 15:21:38 +08:00
}
2018-05-29 02:37:39 +08:00
if hasEditPermissionInFoldersQuery . Result {
children := [ ] * dtos . NavLink {
{ Text : "Dashboard" , Icon : "gicon gicon-dashboard-new" , Url : setting . AppSubUrl + "/dashboard/new" } ,
}
if c . OrgRole == m . ROLE_ADMIN || c . OrgRole == m . ROLE_EDITOR {
children = append ( children , & dtos . NavLink { Text : "Folder" , SubTitle : "Create a new folder to organize your dashboards" , Id : "folder" , Icon : "gicon gicon-folder-new" , Url : setting . AppSubUrl + "/dashboards/folder/new" } )
}
2018-06-07 22:05:56 +08:00
children = append ( children , & dtos . NavLink { Text : "Import" , SubTitle : "Import dashboard from file or Grafana.com" , Id : "import" , Icon : "gicon gicon-dashboard-import" , Url : setting . AppSubUrl + "/dashboard/import" } )
2017-08-15 23:52:52 +08:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 16:28:55 +08:00
Text : "Create" ,
Id : "create" ,
Icon : "fa fa-fw fa-plus" ,
Url : setting . AppSubUrl + "/dashboard/new" ,
Children : children ,
SortWeight : dtos . WeightCreate ,
2017-06-23 06:41:39 +08:00
} )
2016-03-10 23:38:16 +08:00
}
2017-06-23 06:41:39 +08:00
dashboardChildNavs := [ ] * dtos . NavLink {
2018-01-11 22:42:45 +08:00
{ Text : "Home" , Id : "home" , Url : setting . AppSubUrl + "/" , Icon : "gicon gicon-home" , HideFromTabs : true } ,
{ Text : "Divider" , Divider : true , Id : "divider" , HideFromTabs : true } ,
2017-12-14 06:48:44 +08:00
{ Text : "Manage" , Id : "manage-dashboards" , Url : setting . AppSubUrl + "/dashboards" , Icon : "gicon gicon-manage" } ,
{ Text : "Playlists" , Id : "playlists" , Url : setting . AppSubUrl + "/playlists" , Icon : "gicon gicon-playlists" } ,
{ Text : "Snapshots" , Id : "snapshots" , Url : setting . AppSubUrl + "/dashboard/snapshots" , Icon : "gicon gicon-snapshots" } ,
2016-03-10 23:38:16 +08:00
}
2017-08-15 23:52:52 +08:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 16:28:55 +08:00
Text : "Dashboards" ,
Id : "dashboards" ,
SubTitle : "Manage dashboards & folders" ,
Icon : "gicon gicon-dashboard" ,
Url : setting . AppSubUrl + "/" ,
SortWeight : dtos . WeightDashboard ,
Children : dashboardChildNavs ,
2015-11-20 16:43:10 +08:00
} )
2019-01-21 15:47:41 +08:00
if setting . ExploreEnabled && ( c . OrgRole == m . ROLE_ADMIN || c . OrgRole == m . ROLE_EDITOR || setting . ViewersCanEdit ) {
2018-04-27 17:39:14 +08:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 16:28:55 +08:00
Text : "Explore" ,
Id : "explore" ,
SubTitle : "Explore your data" ,
Icon : "gicon gicon-explore" ,
SortWeight : dtos . WeightExplore ,
Url : setting . AppSubUrl + "/explore" ,
2018-04-27 17:39:14 +08:00
} )
}
2018-04-26 17:58:42 +08:00
2017-08-16 02:24:16 +08:00
if c . IsSignedIn {
2018-04-16 19:37:05 +08:00
// Only set login if it's different from the name
var login string
if c . SignedInUser . Login != c . SignedInUser . NameOrFallback ( ) {
login = c . SignedInUser . Login
}
2017-08-16 21:03:49 +08:00
profileNode := & dtos . NavLink {
2017-12-15 20:06:11 +08:00
Text : c . SignedInUser . NameOrFallback ( ) ,
2018-04-16 19:37:05 +08:00
SubTitle : login ,
2017-08-16 05:17:34 +08:00
Id : "profile" ,
Img : data . User . GravatarUrl ,
Url : setting . AppSubUrl + "/profile" ,
HideFromMenu : true ,
2019-11-15 16:28:55 +08:00
SortWeight : dtos . WeightProfile ,
2017-08-16 02:24:16 +08:00
Children : [ ] * dtos . NavLink {
2017-12-14 06:48:44 +08:00
{ Text : "Preferences" , Id : "profile-settings" , Url : setting . AppSubUrl + "/profile" , Icon : "gicon gicon-preferences" } ,
2017-08-16 02:24:16 +08:00
{ Text : "Change Password" , Id : "change-password" , Url : setting . AppSubUrl + "/profile/password" , Icon : "fa fa-fw fa-lock" , HideFromMenu : true } ,
} ,
2017-08-16 21:03:49 +08:00
}
if ! setting . DisableSignoutMenu {
// add sign out first
2017-12-01 00:28:24 +08:00
profileNode . Children = append ( profileNode . Children , & dtos . NavLink {
2019-12-03 01:03:28 +08:00
Text : "Sign out" ,
Id : "sign-out" ,
Url : setting . AppSubUrl + "/logout" ,
Icon : "fa fa-fw fa-sign-out" ,
Target : "_self" ,
HideFromTabs : true ,
2017-12-01 00:28:24 +08:00
} )
2017-08-16 21:03:49 +08:00
}
data . NavTree = append ( data . NavTree , profileNode )
2017-08-16 02:24:16 +08:00
}
2017-01-25 20:32:26 +08:00
if setting . AlertingEnabled && ( c . OrgRole == m . ROLE_ADMIN || c . OrgRole == m . ROLE_EDITOR ) {
2016-06-16 21:21:23 +08:00
alertChildNavs := [ ] * dtos . NavLink {
2017-12-14 06:48:44 +08:00
{ Text : "Alert Rules" , Id : "alert-list" , Url : setting . AppSubUrl + "/alerting/list" , Icon : "gicon gicon-alert-rules" } ,
2017-11-30 18:31:38 +08:00
{ Text : "Notification channels" , Id : "channels" , Url : setting . AppSubUrl + "/alerting/notifications" , Icon : "gicon gicon-alert-notification-channel" } ,
2016-06-16 21:21:23 +08:00
}
2017-08-15 23:52:52 +08:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 16:28:55 +08:00
Text : "Alerting" ,
SubTitle : "Alert rules & notifications" ,
Id : "alerting" ,
Icon : "gicon gicon-alert" ,
Url : setting . AppSubUrl + "/alerting/list" ,
Children : alertChildNavs ,
SortWeight : dtos . WeightAlerting ,
2016-05-03 22:46:10 +08:00
} )
}
2016-01-11 04:37:11 +08:00
enabledPlugins , err := plugins . GetEnabledPlugins ( c . OrgId )
2015-12-03 23:43:55 +08:00
if err != nil {
return nil , err
}
2015-12-22 06:09:27 +08:00
for _ , plugin := range enabledPlugins . Apps {
2016-01-12 01:03:08 +08:00
if plugin . Pinned {
2016-03-22 02:07:08 +08:00
appLink := & dtos . NavLink {
2019-11-15 16:28:55 +08:00
Text : plugin . Name ,
Id : "plugin-page-" + plugin . Id ,
Url : plugin . DefaultNavUrl ,
Img : plugin . Info . Logos . Small ,
SortWeight : dtos . WeightPlugin ,
2016-02-09 21:06:23 +08:00
}
2016-03-22 02:07:08 +08:00
for _ , include := range plugin . Includes {
2016-04-25 20:00:49 +08:00
if ! c . HasUserRole ( include . Role ) {
continue
}
2016-03-22 02:07:08 +08:00
if include . Type == "page" && include . AddToNav {
link := & dtos . NavLink {
Url : setting . AppSubUrl + "/plugins/" + plugin . Id + "/page/" + include . Slug ,
Text : include . Name ,
}
appLink . Children = append ( appLink . Children , link )
2016-03-01 18:07:51 +08:00
}
2016-04-25 20:00:49 +08:00
2016-03-22 02:07:08 +08:00
if include . Type == "dashboard" && include . AddToNav {
link := & dtos . NavLink {
Url : setting . AppSubUrl + "/dashboard/db/" + include . Slug ,
Text : include . Name ,
}
appLink . Children = append ( appLink . Children , link )
}
}
2016-09-27 20:39:51 +08:00
if len ( appLink . Children ) > 0 && c . OrgRole == m . ROLE_ADMIN {
2016-03-22 02:07:08 +08:00
appLink . Children = append ( appLink . Children , & dtos . NavLink { Divider : true } )
2019-05-08 15:38:40 +08:00
appLink . Children = append ( appLink . Children , & dtos . NavLink { Text : "Plugin Config" , Icon : "gicon gicon-cog" , Url : setting . AppSubUrl + "/plugins/" + plugin . Id + "/" } )
2016-02-09 21:06:23 +08:00
}
2016-04-25 20:00:49 +08:00
if len ( appLink . Children ) > 0 {
2017-08-15 23:52:52 +08:00
data . NavTree = append ( data . NavTree , appLink )
2016-04-25 20:00:49 +08:00
}
2015-10-06 17:20:50 +08:00
}
2015-08-21 15:30:39 +08:00
}
2015-11-11 14:30:07 +08:00
2019-08-02 20:02:59 +08:00
configNodes := [ ] * dtos . NavLink { }
if c . OrgRole == m . ROLE_ADMIN {
configNodes = append ( configNodes , & dtos . NavLink {
Text : "Data Sources" ,
Icon : "gicon gicon-datasources" ,
Description : "Add and configure data sources" ,
Id : "datasources" ,
Url : setting . AppSubUrl + "/datasources" ,
} )
configNodes = append ( configNodes , & dtos . NavLink {
Text : "Users" ,
Id : "users" ,
Description : "Manage org members" ,
Icon : "gicon gicon-user" ,
Url : setting . AppSubUrl + "/org/users" ,
} )
}
2017-08-15 20:49:12 +08:00
2019-08-02 20:02:59 +08:00
if c . OrgRole == m . ROLE_ADMIN || hs . Cfg . EditorsCanAdmin {
configNodes = append ( configNodes , & dtos . NavLink {
Text : "Teams" ,
Id : "teams" ,
Description : "Manage org groups" ,
Icon : "gicon gicon-team" ,
Url : setting . AppSubUrl + "/org/teams" ,
} )
}
2018-06-05 01:28:01 +08:00
2019-08-02 20:02:59 +08:00
configNodes = append ( configNodes , & dtos . NavLink {
Text : "Plugins" ,
Id : "plugins" ,
Description : "View and configure plugins" ,
Icon : "gicon gicon-plugins" ,
Url : setting . AppSubUrl + "/plugins" ,
} )
2019-07-25 22:54:26 +08:00
2019-08-02 20:02:59 +08:00
if c . OrgRole == m . ROLE_ADMIN {
configNodes = append ( configNodes , & dtos . NavLink {
Text : "Preferences" ,
Id : "org-settings" ,
Description : "Organization preferences" ,
Icon : "gicon gicon-preferences" ,
Url : setting . AppSubUrl + "/org" ,
} )
configNodes = append ( configNodes , & dtos . NavLink {
Text : "API Keys" ,
Id : "apikeys" ,
Description : "Create & manage API keys" ,
Icon : "gicon gicon-apikeys" ,
Url : setting . AppSubUrl + "/org/apikeys" ,
} )
2016-02-15 00:37:05 +08:00
}
2019-08-02 20:02:59 +08:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 16:28:55 +08:00
Id : "cfg" ,
Text : "Configuration" ,
SubTitle : "Organization: " + c . OrgName ,
Icon : "gicon gicon-cog" ,
Url : configNodes [ 0 ] . Url ,
SortWeight : dtos . WeightConfig ,
Children : configNodes ,
2019-08-02 20:02:59 +08:00
} )
2019-03-05 20:02:41 +08:00
if c . IsGrafanaAdmin {
2019-09-17 16:27:55 +08:00
adminNavLinks := [ ] * dtos . NavLink {
{ Text : "Users" , Id : "global-users" , Url : setting . AppSubUrl + "/admin/users" , Icon : "gicon gicon-user" } ,
{ Text : "Orgs" , Id : "global-orgs" , Url : setting . AppSubUrl + "/admin/orgs" , Icon : "gicon gicon-org" } ,
{ Text : "Settings" , Id : "server-settings" , Url : setting . AppSubUrl + "/admin/settings" , Icon : "gicon gicon-preferences" } ,
{ Text : "Stats" , Id : "server-stats" , Url : setting . AppSubUrl + "/admin/stats" , Icon : "fa fa-fw fa-bar-chart" } ,
}
if setting . LDAPEnabled {
adminNavLinks = append ( adminNavLinks , & dtos . NavLink {
Text : "LDAP" , Id : "ldap" , Url : setting . AppSubUrl + "/admin/ldap" , Icon : "fa fa-fw fa-address-book-o" ,
} )
}
2019-03-05 20:02:41 +08:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
Text : "Server Admin" ,
SubTitle : "Manage all users & orgs" ,
HideFromTabs : true ,
Id : "admin" ,
Icon : "gicon gicon-shield" ,
Url : setting . AppSubUrl + "/admin/users" ,
2019-11-15 16:28:55 +08:00
SortWeight : dtos . WeightAdmin ,
2019-09-17 16:27:55 +08:00
Children : adminNavLinks ,
2019-03-05 20:02:41 +08:00
} )
}
2017-08-16 21:03:49 +08:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
Text : "Help" ,
2018-04-27 19:41:58 +08:00
SubTitle : fmt . Sprintf ( ` %s v%s (%s) ` , setting . ApplicationName , setting . BuildVersion , setting . BuildCommit ) ,
2017-08-16 21:03:49 +08:00
Id : "help" ,
Url : "#" ,
2017-12-14 06:48:44 +08:00
Icon : "gicon gicon-question" ,
2017-08-16 21:03:49 +08:00
HideFromMenu : true ,
2019-11-15 16:28:55 +08:00
SortWeight : dtos . WeightHelp ,
2017-08-16 21:03:49 +08:00
Children : [ ] * dtos . NavLink {
{ Text : "Keyboard shortcuts" , Url : "/shortcuts" , Icon : "fa fa-fw fa-keyboard-o" , Target : "_self" } ,
{ Text : "Community site" , Url : "http://community.grafana.com" , Icon : "fa fa-fw fa-comment" , Target : "_blank" } ,
{ Text : "Documentation" , Url : "http://docs.grafana.org" , Icon : "fa fa-fw fa-file" , Target : "_blank" } ,
} ,
} )
2018-10-12 17:26:42 +08:00
hs . HooksService . RunIndexDataHooks ( & data )
2019-11-15 16:28:55 +08:00
sort . SliceStable ( data . NavTree , func ( i , j int ) bool {
return data . NavTree [ i ] . SortWeight < data . NavTree [ j ] . SortWeight
} )
2015-11-20 16:43:10 +08:00
return & data , nil
2015-01-29 19:10:34 +08:00
}
2018-10-09 23:47:43 +08:00
func ( hs * HTTPServer ) Index ( c * m . ReqContext ) {
data , err := hs . setIndexViewData ( c )
2018-03-22 19:37:35 +08:00
if err != nil {
2015-01-29 19:10:34 +08:00
c . Handle ( 500 , "Failed to get settings" , err )
return
}
2018-03-22 19:37:35 +08:00
c . HTML ( 200 , "index" , data )
2015-01-29 19:10:34 +08:00
}
2018-10-09 23:47:43 +08:00
func ( hs * HTTPServer ) NotFoundHandler ( c * m . ReqContext ) {
2015-01-29 19:10:34 +08:00
if c . IsApiRequest ( ) {
2015-03-23 03:14:00 +08:00
c . JsonApiErr ( 404 , "Not found" , nil )
2015-01-29 19:10:34 +08:00
return
}
2018-10-09 23:47:43 +08:00
data , err := hs . setIndexViewData ( c )
2018-03-22 19:37:35 +08:00
if err != nil {
2015-01-29 19:10:34 +08:00
c . Handle ( 500 , "Failed to get settings" , err )
return
}
2018-03-22 19:37:35 +08:00
c . HTML ( 404 , "index" , data )
2015-01-29 19:10:34 +08:00
}
2018-11-01 04:40:58 +08:00
2019-11-01 21:56:12 +08:00
func getAppNameBodyClass ( validLicense bool ) string {
if validLicense {
2018-11-01 04:40:58 +08:00
return "app-enterprise"
}
2019-11-01 21:56:12 +08:00
return "app-grafana"
2018-11-01 04:40:58 +08:00
}