2015-01-29 05:10:34 -06:00
package api
import (
2016-09-23 05:29:53 -05:00
"fmt"
2019-11-15 02:28:55 -06:00
"sort"
2016-07-05 10:59:43 -05:00
"strings"
2015-02-05 03:37:13 -06:00
"github.com/grafana/grafana/pkg/api/dtos"
2016-04-02 15:54:06 -05:00
"github.com/grafana/grafana/pkg/bus"
2015-11-20 02:43:10 -06:00
m "github.com/grafana/grafana/pkg/models"
2015-10-06 04:20:50 -05:00
"github.com/grafana/grafana/pkg/plugins"
2015-02-05 03:37:13 -06:00
"github.com/grafana/grafana/pkg/setting"
2015-01-29 05:10:34 -06:00
)
2018-09-22 03:50:00 -05:00
const (
// Themes
lightName = "light"
darkName = "dark"
)
2018-10-09 10:47:43 -05:00
func ( hs * HTTPServer ) setIndexViewData ( c * m . ReqContext ) ( * dtos . IndexViewData , error ) {
settings , err := hs . getFrontendSettingsMap ( c )
2015-01-29 05:10:34 -06:00
if err != nil {
2015-11-20 02:43:10 -06:00
return nil , err
2015-01-29 05:10:34 -06:00
}
2018-11-12 13:01:53 -06:00
prefsQuery := m . GetPreferencesWithDefaultsQuery { User : c . SignedInUser }
2016-04-02 15:54:06 -05:00
if err := bus . Dispatch ( & prefsQuery ) ; err != nil {
return nil , err
}
prefs := prefsQuery . Result
2016-07-05 10:59:43 -05: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 06:37:35 -05:00
appURL := setting . AppUrl
appSubURL := setting . AppSubUrl
2016-09-23 05:29:53 -05:00
// special case when doing localhost call from phantomjs
2020-01-06 01:12:18 -06:00
if c . IsRenderCall && ! hs . Cfg . ServeFromSubPath {
2018-03-22 06:37:35 -05:00
appURL = fmt . Sprintf ( "%s://localhost:%s" , setting . Protocol , setting . HttpPort )
appSubURL = ""
2016-09-23 05:29:53 -05:00
settings [ "appSubUrl" ] = ""
}
2018-04-30 08:34:31 -05:00
hasEditPermissionInFoldersQuery := m . HasEditPermissionInFoldersQuery { SignedInUser : c . SignedInUser }
if err := bus . Dispatch ( & hasEditPermissionInFoldersQuery ) ; err != nil {
return nil , err
}
2015-11-20 02:43:10 -06:00
var data = dtos . IndexViewData {
User : & dtos . CurrentUser {
2018-04-30 08:34:31 -05: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 03:50:00 -05:00
LightTheme : prefs . Theme == lightName ,
2018-04-30 08:34:31 -05:00
Timezone : prefs . Timezone ,
Locale : locale ,
HelpFlags1 : c . HelpFlags1 ,
HasEditPermissionInFolders : hasEditPermissionInFoldersQuery . Result ,
2015-11-20 02:43:10 -06:00
} ,
2016-05-03 02:00:58 -05:00
Settings : settings ,
2017-10-11 14:36:03 -05:00
Theme : prefs . Theme ,
2018-03-22 06:37:35 -05:00
AppUrl : appURL ,
AppSubUrl : appSubURL ,
2016-05-03 02:00:58 -05:00
GoogleAnalyticsId : setting . GoogleAnalyticsId ,
GoogleTagManagerId : setting . GoogleTagManagerId ,
BuildVersion : setting . BuildVersion ,
BuildCommit : setting . BuildCommit ,
NewGrafanaVersion : plugins . GrafanaLatestVersion ,
NewGrafanaVersionExists : plugins . GrafanaHasUpdate ,
2018-07-02 06:33:39 -05:00
AppName : setting . ApplicationName ,
2019-11-01 08:56:12 -05:00
AppNameBodyClass : getAppNameBodyClass ( hs . License . HasValidLicense ( ) ) ,
2020-01-10 15:15:16 -06:00
FavIcon : "public/img/fav32.png" ,
AppleTouchIcon : "public/img/apple-touch-icon.png" ,
2015-01-29 05:10:34 -06:00
}
2015-05-01 01:40:13 -05:00
if setting . DisableGravatar {
2018-03-06 04:46:01 -06:00
data . User . GravatarUrl = setting . AppSubUrl + "/public/img/user_profile.png"
2015-05-01 01:40:13 -05:00
}
2015-11-20 02:43:10 -06:00
if len ( data . User . Name ) == 0 {
data . User . Name = data . User . Login
2015-02-03 03:46:52 -06:00
}
2018-03-22 06:37:35 -05:00
themeURLParam := c . Query ( "theme" )
2018-09-22 03:50:00 -05:00
if themeURLParam == lightName {
2015-11-20 02:43:10 -06:00
data . User . LightTheme = true
2018-09-22 03:50:00 -05:00
data . Theme = lightName
} else if themeURLParam == darkName {
2018-09-11 23:52:38 -05:00
data . User . LightTheme = false
2018-09-22 03:50:00 -05:00
data . Theme = darkName
2015-04-02 02:21:38 -05:00
}
2018-05-28 13:37:39 -05: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 09:05:56 -05: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 10:52:52 -05:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 02:28:55 -06:00
Text : "Create" ,
Id : "create" ,
Icon : "fa fa-fw fa-plus" ,
Url : setting . AppSubUrl + "/dashboard/new" ,
Children : children ,
SortWeight : dtos . WeightCreate ,
2017-06-22 17:41:39 -05:00
} )
2016-03-10 09:38:16 -06:00
}
2017-06-22 17:41:39 -05:00
dashboardChildNavs := [ ] * dtos . NavLink {
2018-01-11 08:42:45 -06:00
{ Text : "Home" , Id : "home" , Url : setting . AppSubUrl + "/" , Icon : "gicon gicon-home" , HideFromTabs : true } ,
{ Text : "Divider" , Divider : true , Id : "divider" , HideFromTabs : true } ,
2017-12-13 16:48:44 -06: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 09:38:16 -06:00
}
2017-08-15 10:52:52 -05:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 02:28:55 -06:00
Text : "Dashboards" ,
Id : "dashboards" ,
SubTitle : "Manage dashboards & folders" ,
Icon : "gicon gicon-dashboard" ,
Url : setting . AppSubUrl + "/" ,
SortWeight : dtos . WeightDashboard ,
Children : dashboardChildNavs ,
2015-11-20 02:43:10 -06:00
} )
2019-01-21 01:47:41 -06:00
if setting . ExploreEnabled && ( c . OrgRole == m . ROLE_ADMIN || c . OrgRole == m . ROLE_EDITOR || setting . ViewersCanEdit ) {
2018-04-27 04:39:14 -05:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 02:28:55 -06:00
Text : "Explore" ,
Id : "explore" ,
SubTitle : "Explore your data" ,
Icon : "gicon gicon-explore" ,
SortWeight : dtos . WeightExplore ,
Url : setting . AppSubUrl + "/explore" ,
2018-04-27 04:39:14 -05:00
} )
}
2018-04-26 04:58:42 -05:00
2017-08-15 13:24:16 -05:00
if c . IsSignedIn {
2018-04-16 06:37:05 -05: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 08:03:49 -05:00
profileNode := & dtos . NavLink {
2017-12-15 06:06:11 -06:00
Text : c . SignedInUser . NameOrFallback ( ) ,
2018-04-16 06:37:05 -05:00
SubTitle : login ,
2017-08-15 16:17:34 -05:00
Id : "profile" ,
Img : data . User . GravatarUrl ,
Url : setting . AppSubUrl + "/profile" ,
HideFromMenu : true ,
2019-11-15 02:28:55 -06:00
SortWeight : dtos . WeightProfile ,
2017-08-15 13:24:16 -05:00
Children : [ ] * dtos . NavLink {
2017-12-13 16:48:44 -06:00
{ Text : "Preferences" , Id : "profile-settings" , Url : setting . AppSubUrl + "/profile" , Icon : "gicon gicon-preferences" } ,
2017-08-15 13:24:16 -05:00
{ Text : "Change Password" , Id : "change-password" , Url : setting . AppSubUrl + "/profile/password" , Icon : "fa fa-fw fa-lock" , HideFromMenu : true } ,
} ,
2017-08-16 08:03:49 -05:00
}
if ! setting . DisableSignoutMenu {
// add sign out first
2017-11-30 10:28:24 -06:00
profileNode . Children = append ( profileNode . Children , & dtos . NavLink {
2019-12-02 11:03:28 -06:00
Text : "Sign out" ,
Id : "sign-out" ,
Url : setting . AppSubUrl + "/logout" ,
Icon : "fa fa-fw fa-sign-out" ,
Target : "_self" ,
HideFromTabs : true ,
2017-11-30 10:28:24 -06:00
} )
2017-08-16 08:03:49 -05:00
}
data . NavTree = append ( data . NavTree , profileNode )
2017-08-15 13:24:16 -05:00
}
2017-01-25 06:32:26 -06:00
if setting . AlertingEnabled && ( c . OrgRole == m . ROLE_ADMIN || c . OrgRole == m . ROLE_EDITOR ) {
2016-06-16 08:21:23 -05:00
alertChildNavs := [ ] * dtos . NavLink {
2017-12-13 16:48:44 -06:00
{ Text : "Alert Rules" , Id : "alert-list" , Url : setting . AppSubUrl + "/alerting/list" , Icon : "gicon gicon-alert-rules" } ,
2017-11-30 04:31:38 -06:00
{ Text : "Notification channels" , Id : "channels" , Url : setting . AppSubUrl + "/alerting/notifications" , Icon : "gicon gicon-alert-notification-channel" } ,
2016-06-16 08:21:23 -05:00
}
2017-08-15 10:52:52 -05:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 02:28:55 -06: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 09:46:10 -05:00
} )
}
2016-01-10 14:37:11 -06:00
enabledPlugins , err := plugins . GetEnabledPlugins ( c . OrgId )
2015-12-03 09:43:55 -06:00
if err != nil {
return nil , err
}
2015-12-21 16:09:27 -06:00
for _ , plugin := range enabledPlugins . Apps {
2016-01-11 11:03:08 -06:00
if plugin . Pinned {
2016-03-21 13:07:08 -05:00
appLink := & dtos . NavLink {
2019-11-15 02:28:55 -06:00
Text : plugin . Name ,
Id : "plugin-page-" + plugin . Id ,
Url : plugin . DefaultNavUrl ,
Img : plugin . Info . Logos . Small ,
SortWeight : dtos . WeightPlugin ,
2016-02-09 07:06:23 -06:00
}
2016-03-21 13:07:08 -05:00
for _ , include := range plugin . Includes {
2016-04-25 07:00:49 -05:00
if ! c . HasUserRole ( include . Role ) {
continue
}
2016-03-21 13:07:08 -05: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 04:07:51 -06:00
}
2016-04-25 07:00:49 -05:00
2016-03-21 13:07:08 -05: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 07:39:51 -05:00
if len ( appLink . Children ) > 0 && c . OrgRole == m . ROLE_ADMIN {
2016-03-21 13:07:08 -05:00
appLink . Children = append ( appLink . Children , & dtos . NavLink { Divider : true } )
2019-05-08 02:38:40 -05:00
appLink . Children = append ( appLink . Children , & dtos . NavLink { Text : "Plugin Config" , Icon : "gicon gicon-cog" , Url : setting . AppSubUrl + "/plugins/" + plugin . Id + "/" } )
2016-02-09 07:06:23 -06:00
}
2016-04-25 07:00:49 -05:00
if len ( appLink . Children ) > 0 {
2017-08-15 10:52:52 -05:00
data . NavTree = append ( data . NavTree , appLink )
2016-04-25 07:00:49 -05:00
}
2015-10-06 04:20:50 -05:00
}
2015-08-21 02:30:39 -05:00
}
2015-11-11 00:30:07 -06:00
2019-08-02 07:02:59 -05: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 07:49:12 -05:00
2020-01-24 05:00:52 -06:00
if c . OrgRole == m . ROLE_ADMIN || ( hs . Cfg . EditorsCanAdmin && c . OrgRole == m . ROLE_EDITOR ) {
2019-08-02 07:02:59 -05:00
configNodes = append ( configNodes , & dtos . NavLink {
Text : "Teams" ,
Id : "teams" ,
Description : "Manage org groups" ,
Icon : "gicon gicon-team" ,
Url : setting . AppSubUrl + "/org/teams" ,
} )
}
2018-06-04 12:28:01 -05:00
2019-08-02 07:02:59 -05: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 09:54:26 -05:00
2019-08-02 07:02:59 -05: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-14 10:37:05 -06:00
}
2019-08-02 07:02:59 -05:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
2019-11-15 02:28:55 -06: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 07:02:59 -05:00
} )
2019-03-05 06:02:41 -06:00
if c . IsGrafanaAdmin {
2019-09-17 03:27:55 -05: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 06:02:41 -06: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 02:28:55 -06:00
SortWeight : dtos . WeightAdmin ,
2019-09-17 03:27:55 -05:00
Children : adminNavLinks ,
2019-03-05 06:02:41 -06:00
} )
}
2017-08-16 08:03:49 -05:00
data . NavTree = append ( data . NavTree , & dtos . NavLink {
Text : "Help" ,
2018-04-27 06:41:58 -05:00
SubTitle : fmt . Sprintf ( ` %s v%s (%s) ` , setting . ApplicationName , setting . BuildVersion , setting . BuildCommit ) ,
2017-08-16 08:03:49 -05:00
Id : "help" ,
Url : "#" ,
2017-12-13 16:48:44 -06:00
Icon : "gicon gicon-question" ,
2017-08-16 08:03:49 -05:00
HideFromMenu : true ,
2019-11-15 02:28:55 -06:00
SortWeight : dtos . WeightHelp ,
2020-01-09 04:25:52 -06:00
Children : [ ] * dtos . NavLink { } ,
2017-08-16 08:03:49 -05:00
} )
2018-10-12 04:26:42 -05:00
hs . HooksService . RunIndexDataHooks ( & data )
2019-11-15 02:28:55 -06:00
sort . SliceStable ( data . NavTree , func ( i , j int ) bool {
return data . NavTree [ i ] . SortWeight < data . NavTree [ j ] . SortWeight
} )
2015-11-20 02:43:10 -06:00
return & data , nil
2015-01-29 05:10:34 -06:00
}
2018-10-09 10:47:43 -05:00
func ( hs * HTTPServer ) Index ( c * m . ReqContext ) {
data , err := hs . setIndexViewData ( c )
2018-03-22 06:37:35 -05:00
if err != nil {
2015-01-29 05:10:34 -06:00
c . Handle ( 500 , "Failed to get settings" , err )
return
}
2018-03-22 06:37:35 -05:00
c . HTML ( 200 , "index" , data )
2015-01-29 05:10:34 -06:00
}
2018-10-09 10:47:43 -05:00
func ( hs * HTTPServer ) NotFoundHandler ( c * m . ReqContext ) {
2015-01-29 05:10:34 -06:00
if c . IsApiRequest ( ) {
2015-03-22 14:14:00 -05:00
c . JsonApiErr ( 404 , "Not found" , nil )
2015-01-29 05:10:34 -06:00
return
}
2018-10-09 10:47:43 -05:00
data , err := hs . setIndexViewData ( c )
2018-03-22 06:37:35 -05:00
if err != nil {
2015-01-29 05:10:34 -06:00
c . Handle ( 500 , "Failed to get settings" , err )
return
}
2018-03-22 06:37:35 -05:00
c . HTML ( 404 , "index" , data )
2015-01-29 05:10:34 -06:00
}
2018-10-31 15:40:58 -05:00
2019-11-01 08:56:12 -05:00
func getAppNameBodyClass ( validLicense bool ) string {
if validLicense {
2018-10-31 15:40:58 -05:00
return "app-enterprise"
}
2019-11-01 08:56:12 -05:00
return "app-grafana"
2018-10-31 15:40:58 -05:00
}