grafana/pkg/api/index.go
Josh Hunt d51e7ec7ef
Preferences: Add theme preference to match system theme (#61986)
* user essentials mob! 🔱

lastFile:pkg/api/preferences.go

* user essentials mob! 🔱

* user essentials mob! 🔱

lastFile:packages/grafana-data/src/types/config.ts

* user essentials mob! 🔱

lastFile:public/app/core/services/echo/utils.test.ts

* user essentials mob! 🔱

* user essentials mob! 🔱

lastFile:public/views/index-template.html

* user essentials mob! 🔱

* Restore currentUser.lightTheme for backwards compat

* fix types

* Apply suggestions from code review

Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>

* cleanup

* cleanup

---------

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
Co-authored-by: Joao Silva <joao.silva@grafana.com>
Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
2023-01-30 10:51:51 +01:00

192 lines
6.4 KiB
Go

package api
import (
"fmt"
"net/http"
"strings"
"github.com/grafana/grafana/pkg/api/dtos"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
pref "github.com/grafana/grafana/pkg/services/preference"
"github.com/grafana/grafana/pkg/setting"
)
const (
// Themes
lightName = "light"
darkName = "dark"
systemName = "system"
)
func (hs *HTTPServer) editorInAnyFolder(c *contextmodel.ReqContext) bool {
hasEditPermissionInFoldersQuery := folder.HasEditPermissionInFoldersQuery{SignedInUser: c.SignedInUser}
hasEditPermissionInFoldersQueryResult, err := hs.DashboardService.HasEditPermissionInFolders(c.Req.Context(), &hasEditPermissionInFoldersQuery)
if err != nil {
return false
}
return hasEditPermissionInFoldersQueryResult
}
func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexViewData, error) {
hasAccess := ac.HasAccess(hs.AccessControl, c)
hasEditPerm := hasAccess(hs.editorInAnyFolder, ac.EvalAny(ac.EvalPermission(dashboards.ActionDashboardsCreate), ac.EvalPermission(dashboards.ActionFoldersCreate)))
settings, err := hs.getFrontendSettingsMap(c)
if err != nil {
return nil, err
}
settings["dateFormats"] = hs.Cfg.DateFormats
prefsQuery := pref.GetPreferenceWithDefaultsQuery{UserID: c.UserID, OrgID: c.OrgID, Teams: c.Teams}
prefs, err := hs.preferenceService.GetWithDefaults(c.Req.Context(), &prefsQuery)
if err != nil {
return nil, err
}
// Locale is used for some number and date/time formatting, whereas language is used just for
// translating words in the interface
acceptLangHeader := c.Req.Header.Get("Accept-Language")
locale := "en-US"
language := "" // frontend will set the default language
if hs.Features.IsEnabled(featuremgmt.FlagInternationalization) && prefs.JSONData.Language != "" {
language = prefs.JSONData.Language
}
if len(acceptLangHeader) > 0 {
parts := strings.Split(acceptLangHeader, ",")
locale = parts[0]
}
appURL := setting.AppUrl
appSubURL := hs.Cfg.AppSubURL
// special case when doing localhost call from image renderer
if c.IsRenderCall && !hs.Cfg.ServeFromSubPath {
appURL = fmt.Sprintf("%s://localhost:%s", hs.Cfg.Protocol, hs.Cfg.HTTPPort)
appSubURL = ""
settings["appSubUrl"] = ""
}
navTree, err := hs.navTreeService.GetNavTree(c, hasEditPerm, prefs)
if err != nil {
return nil, err
}
if c.IsPublicDashboardView {
settings["isPublicDashboardView"] = true
}
weekStart := ""
if prefs.WeekStart != nil {
weekStart = *prefs.WeekStart
}
data := dtos.IndexViewData{
User: &dtos.CurrentUser{
Id: c.UserID,
IsSignedIn: c.IsSignedIn,
Login: c.Login,
Email: c.Email,
ExternalUserId: c.SignedInUser.ExternalAuthID,
Name: c.Name,
OrgCount: c.OrgCount,
OrgId: c.OrgID,
OrgName: c.OrgName,
OrgRole: c.OrgRole,
GravatarUrl: dtos.GetGravatarUrl(c.Email),
IsGrafanaAdmin: c.IsGrafanaAdmin,
Theme: prefs.Theme,
LightTheme: prefs.Theme == lightName,
Timezone: prefs.Timezone,
WeekStart: weekStart,
Locale: locale,
Language: language,
HelpFlags1: c.HelpFlags1,
HasEditPermissionInFolders: hasEditPerm,
},
Settings: settings,
Theme: prefs.Theme,
AppUrl: appURL,
AppSubUrl: appSubURL,
GoogleAnalyticsId: setting.GoogleAnalyticsId,
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
GoogleAnalytics4SendManualPageViews: setting.GoogleAnalytics4SendManualPageViews,
GoogleTagManagerId: setting.GoogleTagManagerId,
BuildVersion: setting.BuildVersion,
BuildCommit: setting.BuildCommit,
NewGrafanaVersion: hs.grafanaUpdateChecker.LatestVersion(),
NewGrafanaVersionExists: hs.grafanaUpdateChecker.UpdateAvailable(),
AppName: setting.ApplicationName,
AppNameBodyClass: "app-grafana",
FavIcon: "public/img/fav32.png",
AppleTouchIcon: "public/img/apple-touch-icon.png",
AppTitle: "Grafana",
NavTree: navTree,
Sentry: &hs.Cfg.Sentry,
Nonce: c.RequestNonce,
ContentDeliveryURL: hs.Cfg.GetContentDeliveryURL(hs.License.ContentDeliveryPrefix()),
LoadingLogo: "public/img/grafana_icon.svg",
}
if !hs.AccessControl.IsDisabled() {
userPermissions, err := hs.accesscontrolService.GetUserPermissions(c.Req.Context(), c.SignedInUser, ac.Options{ReloadCache: false})
if err != nil {
return nil, err
}
data.User.Permissions = ac.BuildPermissionsMap(userPermissions)
}
if setting.DisableGravatar {
data.User.GravatarUrl = hs.Cfg.AppSubURL + "/public/img/user_profile.png"
}
if len(data.User.Name) == 0 {
data.User.Name = data.User.Login
}
themeURLParam := c.Query("theme")
if themeURLParam == lightName || themeURLParam == darkName || themeURLParam == systemName {
data.User.Theme = themeURLParam
data.Theme = themeURLParam
}
hs.HooksService.RunIndexDataHooks(&data, c)
// This will remove empty cfg or admin sections and move sections around if topnav is enabled
data.NavTree.RemoveEmptySectionsAndApplyNewInformationArchitecture(hs.Features.IsEnabled(featuremgmt.FlagTopnav))
data.NavTree.Sort()
return &data, nil
}
func (hs *HTTPServer) Index(c *contextmodel.ReqContext) {
data, err := hs.setIndexViewData(c)
if err != nil {
c.Handle(hs.Cfg, 500, "Failed to get settings", err)
return
}
c.HTML(http.StatusOK, "index", data)
}
func (hs *HTTPServer) NotFoundHandler(c *contextmodel.ReqContext) {
if c.IsApiRequest() {
c.JsonApiErr(404, "Not found", nil)
return
}
data, err := hs.setIndexViewData(c)
if err != nil {
c.Handle(hs.Cfg, 500, "Failed to get settings", err)
return
}
c.HTML(404, "index", data)
}