Compare commits
5 Commits
sriram/SQL
...
mampersat/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3914fb2ac1 | ||
|
|
ac8fbd15e3 | ||
|
|
119f67d20d | ||
|
|
404316140f | ||
|
|
03302046bf |
@@ -1991,6 +1991,12 @@ default_datasource_uid =
|
||||
;feature1 = true
|
||||
;feature2 = false
|
||||
|
||||
# default for the navigation sidebar docking behavior
|
||||
# true = docked by default
|
||||
# false = undocked by default
|
||||
# When unspecified the current Grafana default remains (true).
|
||||
;default_sidebar_docked = false
|
||||
|
||||
[date_formats]
|
||||
# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/
|
||||
|
||||
|
||||
27
pkg/api/frontend_settings.go
Normal file
27
pkg/api/frontend_settings.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// Note: This is a minimal example showing how to add feature toggles to the boot payload.
|
||||
// Integrate with Grafana's actual frontend boot data code path (where the server renders JSON into the page).
|
||||
func frontendSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Example existing boot data:
|
||||
boot := map[string]interface{}{
|
||||
"settings": map[string]interface{}{},
|
||||
}
|
||||
|
||||
// Add our feature toggle into the front-end settings payload.
|
||||
settings := boot["settings"].(map[string]interface{})
|
||||
|
||||
settings["featureToggles"] = map[string]interface{}{
|
||||
"default_sidebar_docked": setting.FeatureToggleConfig.DefaultSidebarDocked,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(boot)
|
||||
}
|
||||
@@ -220,6 +220,12 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
|
||||
|
||||
hs.HooksService.RunIndexDataHooks(&data, c)
|
||||
|
||||
if data.Settings.FeatureToggles == nil {
|
||||
data.Settings.FeatureToggles = map[string]bool{}
|
||||
}
|
||||
|
||||
data.Settings.FeatureToggles["default_sidebar_docked"] = setting.FeatureToggleConfig.DefaultSidebarDocked
|
||||
|
||||
data.NavTree.Sort()
|
||||
|
||||
return &data, nil
|
||||
|
||||
21
pkg/setting/feature_toggles.go
Normal file
21
pkg/setting/feature_toggles.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// FeatureToggles holds lightweight feature flags that can be set via grafana.ini
|
||||
type FeatureToggles struct {
|
||||
// DefaultSidebarDocked determines the default state of the left navigation when no per-browser
|
||||
// preference exists in localStorage (grafana.navigation.docked).
|
||||
DefaultSidebarDocked bool `json:"default_sidebar_docked"`
|
||||
}
|
||||
|
||||
var FeatureToggleConfig = &FeatureToggles{DefaultSidebarDocked: true}
|
||||
|
||||
// loadFeatureToggles reads [feature_toggles] section from the provided INI file.
|
||||
// This is a minimal helper; integrate it into your existing settings loader.
|
||||
func loadFeatureToggles(cfg *ini.File) {
|
||||
sec := cfg.Section("feature_toggles")
|
||||
FeatureToggleConfig.DefaultSidebarDocked = sec.Key("default_sidebar_docked").MustBool(true)
|
||||
}
|
||||
@@ -980,6 +980,10 @@ func (cfg *Cfg) loadConfiguration(args CommandLineArgs) (*ini.File, error) {
|
||||
defaultConfigFile := path.Join(cfg.HomePath, "conf/defaults.ini")
|
||||
cfg.configFiles = append(cfg.configFiles, defaultConfigFile)
|
||||
|
||||
// M@ DEBUG do not commit
|
||||
cfg.Logger.Info("In loadConfiguration M@-----------")
|
||||
fmt.Printf("In loadConfiguration M@-----------\n")
|
||||
|
||||
// check if config file exists
|
||||
if _, err := os.Stat(defaultConfigFile); os.IsNotExist(err) {
|
||||
fmt.Println("Grafana-server Init Failed: Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath")
|
||||
@@ -1019,6 +1023,8 @@ func (cfg *Cfg) loadConfiguration(args CommandLineArgs) (*ini.File, error) {
|
||||
// apply command line overrides
|
||||
cfg.applyCommandLineProperties(commandLineProps, parsedFile)
|
||||
|
||||
loadFeatureToggles(parsedFile)
|
||||
|
||||
// evaluate config values containing environment variables
|
||||
err = expandConfig(parsedFile)
|
||||
if err != nil {
|
||||
|
||||
121
public/scripts/set-default-sidebar.js
Normal file
121
public/scripts/set-default-sidebar.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
// Early script to initialize grafana.navigation.docked from server bootdata.
|
||||
// Behavior:
|
||||
// - If the user already has a preference (no companion ".auto" key) we never override it.
|
||||
// - If we set the value automatically, we set a companion key to mark it.
|
||||
// - If later the authoritative bootdata promise resolves, we will override only if
|
||||
// the current value was previously auto-set by us.
|
||||
(function () {
|
||||
try {
|
||||
let key = 'grafana.navigation.docked';
|
||||
let autoKey = key + '.auto';
|
||||
|
||||
function isAutoSet() {
|
||||
try {
|
||||
return localStorage.getItem(autoKey) === '1';
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setAuto(val) {
|
||||
try {
|
||||
localStorage.setItem(key, val ? 'true' : 'false');
|
||||
localStorage.setItem(autoKey, '1');
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function setIfAbsent(val) {
|
||||
try {
|
||||
if (localStorage.getItem(key) === null) {
|
||||
setAuto(val);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function setIfAutoOrAbsent(val) {
|
||||
try {
|
||||
let cur = localStorage.getItem(key);
|
||||
if (cur === null || isAutoSet()) {
|
||||
setAuto(val);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function applyDefault(preferServer) {
|
||||
try {
|
||||
// If a real user preference exists (not marked as auto) and preferServer is true,
|
||||
// we should NOT override it. setIfAutoOrAbsent will only override if auto or absent.
|
||||
// preferServer indicates this call is from authoritative bootdata (true) or inline (false).
|
||||
let serverDefault;
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
window.grafanaBootData &&
|
||||
window.grafanaBootData.settings &&
|
||||
window.grafanaBootData.settings.featureToggles &&
|
||||
typeof window.grafanaBootData.settings.featureToggles.default_sidebar_docked !== 'undefined'
|
||||
) {
|
||||
serverDefault = window.grafanaBootData.settings.featureToggles.default_sidebar_docked;
|
||||
}
|
||||
|
||||
let globalDefault = typeof window !== 'undefined' ? window.__defaultSidebarDocked : undefined;
|
||||
|
||||
let val =
|
||||
typeof serverDefault !== 'undefined'
|
||||
? serverDefault
|
||||
: typeof globalDefault !== 'undefined'
|
||||
? globalDefault
|
||||
: undefined;
|
||||
|
||||
if (typeof val === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is called for authoritative data (preferServer === true), allow override of previously
|
||||
// auto-set values; if it's non-authoritative (inline), only set if absent.
|
||||
if (preferServer) {
|
||||
setIfAutoOrAbsent(val);
|
||||
} else {
|
||||
setIfAbsent(val);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Fast-path: try immediate apply from inline bootdata (non-authoritative)
|
||||
applyDefault(false);
|
||||
|
||||
// If async bootdata is fetched, wait for the promise and apply authoritative value
|
||||
try {
|
||||
if (
|
||||
typeof window !== 'undefined' &&
|
||||
window.__grafana_boot_data_promise &&
|
||||
typeof window.__grafana_boot_data_promise.then === 'function'
|
||||
) {
|
||||
window.__grafana_boot_data_promise
|
||||
.then(function () {
|
||||
applyDefault(true);
|
||||
})
|
||||
.catch(function () {
|
||||
// ignore
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Fallback: try again shortly after load as a final chance (authoritative)
|
||||
setTimeout(function () {
|
||||
applyDefault(true);
|
||||
}, 1500);
|
||||
} catch (e) {
|
||||
// Do not let this break the page
|
||||
}
|
||||
})();
|
||||
@@ -267,6 +267,8 @@
|
||||
|
||||
<div id="reactRoot"></div>
|
||||
|
||||
<script src="/public/scripts/set-default-sidebar.js"></script>
|
||||
|
||||
<script nonce="[[.Nonce]]">
|
||||
window.grafanaBootData = {
|
||||
user: [[.User]],
|
||||
|
||||
Reference in New Issue
Block a user