mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
rebase against master
This commit is contained in:
commit
f4037667fa
1
.gitignore
vendored
1
.gitignore
vendored
@ -31,6 +31,7 @@ public/css/*.min.css
|
|||||||
|
|
||||||
conf/custom.ini
|
conf/custom.ini
|
||||||
fig.yml
|
fig.yml
|
||||||
|
docker-compose.yml
|
||||||
profile.cov
|
profile.cov
|
||||||
/grafana
|
/grafana
|
||||||
.notouch
|
.notouch
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
* **Snapshots UI**: Dashboard snapshots list can be managed through UI, closes[#1984](https://github.com/grafana/grafana/issues/1984)
|
* **Snapshots UI**: Dashboard snapshots list can be managed through UI, closes[#1984](https://github.com/grafana/grafana/issues/1984)
|
||||||
* **Prometheus**: Prometheus annotation support, closes[#2883](https://github.com/grafana/grafana/pull/2883)
|
* **Prometheus**: Prometheus annotation support, closes[#2883](https://github.com/grafana/grafana/pull/2883)
|
||||||
* **Cli**: New cli tool for downloading and updating plugins
|
* **Cli**: New cli tool for downloading and updating plugins
|
||||||
|
* **Annotations**: Annotations can now contain links that can be clicked (you can navigate on to annotation popovers), closes [#1588](https://github.com/grafana/grafana/issues/1588)
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
* **Plugin API**: Both datasource and panel plugin api (and plugin.json schema) have been updated, requiring an update to plugins. See [plugin api](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md) for more info.
|
* **Plugin API**: Both datasource and panel plugin api (and plugin.json schema) have been updated, requiring an update to plugins. See [plugin api](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md) for more info.
|
||||||
|
@ -7,7 +7,7 @@ template_dir=templates
|
|||||||
grafana_config_file=conf.tmp
|
grafana_config_file=conf.tmp
|
||||||
grafana_config=config
|
grafana_config=config
|
||||||
|
|
||||||
fig_file=fig.yml
|
fig_file=docker-compose.yml
|
||||||
fig_config=fig
|
fig_config=fig
|
||||||
|
|
||||||
if [ "$#" == 0 ]; then
|
if [ "$#" == 0 ]; then
|
@ -122,8 +122,8 @@ To configure Grafana add a configuration file named `custom.ini` to the
|
|||||||
`conf` folder and override any of the settings defined in
|
`conf` folder and override any of the settings defined in
|
||||||
`conf/defaults.ini`.
|
`conf/defaults.ini`.
|
||||||
|
|
||||||
Start Grafana by executing `./grafana-server web`. The `grafana-server` binary needs
|
Start Grafana by executing `./bin/grafana-server web`. The `grafana-server`
|
||||||
the working directory to be the root install directory (where the binary
|
binary needs the working directory to be the root install directory (where the
|
||||||
and the `public` folder is located).
|
binary and the `public` folder is located).
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,9 +126,9 @@ func Register(r *macaron.Macaron) {
|
|||||||
r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
|
r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
|
||||||
|
|
||||||
// apps
|
// apps
|
||||||
r.Get("/apps", wrap(GetOrgAppsList))
|
r.Get("/plugins", wrap(GetPluginList))
|
||||||
r.Get("/apps/:appId/settings", wrap(GetAppSettingsById))
|
r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById))
|
||||||
r.Post("/apps/:appId/settings", bind(m.UpdateAppSettingsCmd{}), wrap(UpdateAppSettings))
|
r.Post("/plugins/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting))
|
||||||
}, reqOrgAdmin)
|
}, reqOrgAdmin)
|
||||||
|
|
||||||
// create new org
|
// create new org
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetOrgAppsList(c *middleware.Context) Response {
|
|
||||||
orgApps, err := plugins.GetOrgAppSettings(c.OrgId)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return ApiError(500, "Failed to list of apps", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([]*dtos.AppSettings, 0)
|
|
||||||
for _, app := range plugins.Apps {
|
|
||||||
orgApp := orgApps[app.Id]
|
|
||||||
result = append(result, dtos.NewAppSettingsDto(app, orgApp))
|
|
||||||
}
|
|
||||||
|
|
||||||
return Json(200, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAppSettingsById(c *middleware.Context) Response {
|
|
||||||
appId := c.Params(":appId")
|
|
||||||
|
|
||||||
if pluginDef, exists := plugins.Apps[appId]; !exists {
|
|
||||||
return ApiError(404, "PluginId not found, no installed plugin with that id", nil)
|
|
||||||
} else {
|
|
||||||
orgApps, err := plugins.GetOrgAppSettings(c.OrgId)
|
|
||||||
if err != nil {
|
|
||||||
return ApiError(500, "Failed to get org app settings ", nil)
|
|
||||||
}
|
|
||||||
orgApp := orgApps[appId]
|
|
||||||
|
|
||||||
return Json(200, dtos.NewAppSettingsDto(pluginDef, orgApp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateAppSettings(c *middleware.Context, cmd m.UpdateAppSettingsCmd) Response {
|
|
||||||
appId := c.Params(":appId")
|
|
||||||
|
|
||||||
cmd.OrgId = c.OrgId
|
|
||||||
cmd.AppId = appId
|
|
||||||
|
|
||||||
if _, ok := plugins.Apps[cmd.AppId]; !ok {
|
|
||||||
return ApiError(404, "App type not installed.", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := bus.Dispatch(&cmd)
|
|
||||||
if err != nil {
|
|
||||||
return ApiError(500, "Failed to update App Plugin", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiSuccess("App updated")
|
|
||||||
}
|
|
@ -33,6 +33,7 @@ func init() {
|
|||||||
actionHandlers = map[string]actionHandler{
|
actionHandlers = map[string]actionHandler{
|
||||||
"GetMetricStatistics": handleGetMetricStatistics,
|
"GetMetricStatistics": handleGetMetricStatistics,
|
||||||
"ListMetrics": handleListMetrics,
|
"ListMetrics": handleListMetrics,
|
||||||
|
"DescribeAlarms": handleDescribeAlarms,
|
||||||
"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
|
"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
|
||||||
"DescribeAlarmHistory": handleDescribeAlarmHistory,
|
"DescribeAlarmHistory": handleDescribeAlarmHistory,
|
||||||
"DescribeInstances": handleDescribeInstances,
|
"DescribeInstances": handleDescribeInstances,
|
||||||
@ -142,6 +143,49 @@ func handleListMetrics(req *cwRequest, c *middleware.Context) {
|
|||||||
c.JSON(200, resp)
|
c.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleDescribeAlarms(req *cwRequest, c *middleware.Context) {
|
||||||
|
cfg := &aws.Config{
|
||||||
|
Region: aws.String(req.Region),
|
||||||
|
Credentials: getCredentials(req.DataSource.Database),
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := cloudwatch.New(session.New(cfg), cfg)
|
||||||
|
|
||||||
|
reqParam := &struct {
|
||||||
|
Parameters struct {
|
||||||
|
ActionPrefix string `json:"actionPrefix"`
|
||||||
|
AlarmNamePrefix string `json:"alarmNamePrefix"`
|
||||||
|
AlarmNames []*string `json:"alarmNames"`
|
||||||
|
StateValue string `json:"stateValue"`
|
||||||
|
} `json:"parameters"`
|
||||||
|
}{}
|
||||||
|
json.Unmarshal(req.Body, reqParam)
|
||||||
|
|
||||||
|
params := &cloudwatch.DescribeAlarmsInput{
|
||||||
|
MaxRecords: aws.Int64(100),
|
||||||
|
}
|
||||||
|
if reqParam.Parameters.ActionPrefix != "" {
|
||||||
|
params.ActionPrefix = aws.String(reqParam.Parameters.ActionPrefix)
|
||||||
|
}
|
||||||
|
if reqParam.Parameters.AlarmNamePrefix != "" {
|
||||||
|
params.AlarmNamePrefix = aws.String(reqParam.Parameters.AlarmNamePrefix)
|
||||||
|
}
|
||||||
|
if len(reqParam.Parameters.AlarmNames) != 0 {
|
||||||
|
params.AlarmNames = reqParam.Parameters.AlarmNames
|
||||||
|
}
|
||||||
|
if reqParam.Parameters.StateValue != "" {
|
||||||
|
params.StateValue = aws.String(reqParam.Parameters.StateValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := svc.DescribeAlarms(params)
|
||||||
|
if err != nil {
|
||||||
|
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, resp)
|
||||||
|
}
|
||||||
|
|
||||||
func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
|
func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
|
||||||
cfg := &aws.Config{
|
cfg := &aws.Config{
|
||||||
Region: aws.String(req.Region),
|
Region: aws.String(req.Region),
|
||||||
|
@ -64,20 +64,12 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *ht
|
|||||||
return &httputil.ReverseProxy{Director: director}
|
return &httputil.ReverseProxy{Director: director}
|
||||||
}
|
}
|
||||||
|
|
||||||
var dsMap map[int64]*m.DataSource = make(map[int64]*m.DataSource)
|
|
||||||
|
|
||||||
func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
|
func getDatasource(id int64, orgId int64) (*m.DataSource, error) {
|
||||||
// ds, exists := dsMap[id]
|
|
||||||
// if exists && ds.OrgId == orgId {
|
|
||||||
// return ds, nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
|
query := m.GetDataSourceByIdQuery{Id: id, OrgId: orgId}
|
||||||
if err := bus.Dispatch(&query); err != nil {
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dsMap[id] = &query.Result
|
|
||||||
return &query.Result, nil
|
return &query.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
package dtos
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AppSettings struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
AppId string `json:"appId"`
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
Pinned bool `json:"pinned"`
|
|
||||||
Module string `json:"module"`
|
|
||||||
BaseUrl string `json:"baseUrl"`
|
|
||||||
Info *plugins.PluginInfo `json:"info"`
|
|
||||||
Pages []*plugins.AppPluginPage `json:"pages"`
|
|
||||||
Includes []*plugins.AppIncludeInfo `json:"includes"`
|
|
||||||
JsonData map[string]interface{} `json:"jsonData"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAppSettingsDto(def *plugins.AppPlugin, data *models.AppSettings) *AppSettings {
|
|
||||||
dto := &AppSettings{
|
|
||||||
AppId: def.Id,
|
|
||||||
Name: def.Name,
|
|
||||||
Info: &def.Info,
|
|
||||||
Module: def.Module,
|
|
||||||
BaseUrl: def.BaseUrl,
|
|
||||||
Pages: def.Pages,
|
|
||||||
Includes: def.Includes,
|
|
||||||
}
|
|
||||||
|
|
||||||
if data != nil {
|
|
||||||
dto.Enabled = data.Enabled
|
|
||||||
dto.Pinned = data.Pinned
|
|
||||||
dto.Info = &def.Info
|
|
||||||
dto.JsonData = data.JsonData
|
|
||||||
}
|
|
||||||
|
|
||||||
return dto
|
|
||||||
}
|
|
@ -16,9 +16,10 @@ type PluginCss struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NavLink struct {
|
type NavLink struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text,omitempty"`
|
||||||
Icon string `json:"icon"`
|
Icon string `json:"icon,omitempty"`
|
||||||
Img string `json:"img"`
|
Img string `json:"img,omitempty"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url,omitempty"`
|
||||||
Children []*NavLink `json:"children"`
|
Divider bool `json:"divider,omitempty"`
|
||||||
|
Children []*NavLink `json:"children,omitempty"`
|
||||||
}
|
}
|
||||||
|
26
pkg/api/dtos/plugins.go
Normal file
26
pkg/api/dtos/plugins.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package dtos
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/plugins"
|
||||||
|
|
||||||
|
type PluginSetting struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
PluginId string `json:"pluginId"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Pinned bool `json:"pinned"`
|
||||||
|
Module string `json:"module"`
|
||||||
|
BaseUrl string `json:"baseUrl"`
|
||||||
|
Info *plugins.PluginInfo `json:"info"`
|
||||||
|
Pages []*plugins.AppPluginPage `json:"pages"`
|
||||||
|
Includes []*plugins.AppIncludeInfo `json:"includes"`
|
||||||
|
JsonData map[string]interface{} `json:"jsonData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginListItem struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
PluginId string `json:"pluginId"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Pinned bool `json:"pinned"`
|
||||||
|
Info *plugins.PluginInfo `json:"info"`
|
||||||
|
}
|
@ -53,15 +53,15 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
|||||||
Icon: "icon-gf icon-gf-dashboard",
|
Icon: "icon-gf icon-gf-dashboard",
|
||||||
Url: setting.AppSubUrl + "/",
|
Url: setting.AppSubUrl + "/",
|
||||||
Children: []*dtos.NavLink{
|
Children: []*dtos.NavLink{
|
||||||
{Text: "Home dashboard", Icon: "fa fa-fw fa-list", Url: setting.AppSubUrl + "/"},
|
{Text: "Home", Url: setting.AppSubUrl + "/"},
|
||||||
{Text: "Playlists", Icon: "fa fa-fw fa-list", Url: setting.AppSubUrl + "/playlists"},
|
{Text: "Playlists", Url: setting.AppSubUrl + "/playlists"},
|
||||||
{Text: "Snapshots", Icon: "fa-fw icon-gf icon-gf-snapshot", Url: setting.AppSubUrl + "/dashboard/snapshots"},
|
{Text: "Snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots"},
|
||||||
|
{Divider: true},
|
||||||
|
{Text: "New", Url: setting.AppSubUrl + "/dashboard/new"},
|
||||||
|
{Text: "Import", Url: setting.AppSubUrl + "/import/dashboard"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{Text: "Playlists", Icon: "fa fa-fw fa-list", Url: setting.AppSubUrl + "/playlists"})
|
|
||||||
// data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{Text: "Snapshots", Icon: "fa-fw icon-gf icon-gf-snapshot", Url: setting.AppSubUrl + "/dashboard/snapshots"})
|
|
||||||
|
|
||||||
if c.OrgRole == m.ROLE_ADMIN {
|
if c.OrgRole == m.ROLE_ADMIN {
|
||||||
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
|
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
|
||||||
Text: "Data Sources",
|
Text: "Data Sources",
|
||||||
@ -72,7 +72,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
|||||||
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
|
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
|
||||||
Text: "Plugins",
|
Text: "Plugins",
|
||||||
Icon: "icon-gf icon-gf-apps",
|
Icon: "icon-gf icon-gf-apps",
|
||||||
Url: setting.AppSubUrl + "/apps",
|
Url: setting.AppSubUrl + "/plugins",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
88
pkg/api/plugin_setting.go
Normal file
88
pkg/api/plugin_setting.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPluginList(c *middleware.Context) Response {
|
||||||
|
pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ApiError(500, "Failed to get list of plugins", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]*dtos.PluginListItem, 0)
|
||||||
|
for _, pluginDef := range plugins.Plugins {
|
||||||
|
listItem := &dtos.PluginListItem{
|
||||||
|
PluginId: pluginDef.Id,
|
||||||
|
Name: pluginDef.Name,
|
||||||
|
Type: pluginDef.Type,
|
||||||
|
Info: &pluginDef.Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
|
||||||
|
listItem.Enabled = pluginSetting.Enabled
|
||||||
|
listItem.Pinned = pluginSetting.Pinned
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, listItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPluginSettingById(c *middleware.Context) Response {
|
||||||
|
pluginId := c.Params(":pluginId")
|
||||||
|
|
||||||
|
if def, exists := plugins.Plugins[pluginId]; !exists {
|
||||||
|
return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
|
||||||
|
} else {
|
||||||
|
dto := &dtos.PluginSetting{
|
||||||
|
Type: def.Type,
|
||||||
|
PluginId: def.Id,
|
||||||
|
Name: def.Name,
|
||||||
|
Info: &def.Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
if app, exists := plugins.Apps[pluginId]; exists {
|
||||||
|
dto.Pages = app.Pages
|
||||||
|
dto.Includes = app.Includes
|
||||||
|
dto.BaseUrl = app.BaseUrl
|
||||||
|
dto.Module = app.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}
|
||||||
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
|
if err != m.ErrPluginSettingNotFound {
|
||||||
|
return ApiError(500, "Failed to get login settings", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dto.Enabled = query.Result.Enabled
|
||||||
|
dto.Pinned = query.Result.Pinned
|
||||||
|
dto.JsonData = query.Result.JsonData
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, dto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Response {
|
||||||
|
pluginId := c.Params(":pluginId")
|
||||||
|
|
||||||
|
cmd.OrgId = c.OrgId
|
||||||
|
cmd.PluginId = pluginId
|
||||||
|
|
||||||
|
if _, ok := plugins.Apps[cmd.PluginId]; !ok {
|
||||||
|
return ApiError(404, "Plugin not installed.", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bus.Dispatch(&cmd); err != nil {
|
||||||
|
return ApiError(500, "Failed to update plugin setting", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiSuccess("Plugin settings updated")
|
||||||
|
}
|
@ -26,7 +26,7 @@ type templateData struct {
|
|||||||
func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) {
|
func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.Header, error) {
|
||||||
result := http.Header{}
|
result := http.Header{}
|
||||||
|
|
||||||
query := m.GetAppSettingByAppIdQuery{OrgId: orgId, AppId: appId}
|
query := m.GetPluginSettingByIdQuery{OrgId: orgId, PluginId: appId}
|
||||||
|
|
||||||
if err := bus.Dispatch(&query); err != nil {
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -22,8 +22,8 @@ func TestPluginProxy(t *testing.T) {
|
|||||||
|
|
||||||
setting.SecretKey = "password"
|
setting.SecretKey = "password"
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *m.GetAppSettingByAppIdQuery) error {
|
bus.AddHandler("test", func(query *m.GetPluginSettingByIdQuery) error {
|
||||||
query.Result = &m.AppSettings{
|
query.Result = &m.PluginSetting{
|
||||||
SecureJsonData: map[string][]byte{
|
SecureJsonData: map[string][]byte{
|
||||||
"key": util.Encrypt([]byte("123"), "password"),
|
"key": util.Encrypt([]byte("123"), "password"),
|
||||||
},
|
},
|
||||||
|
@ -9,12 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrAppSettingNotFound = errors.New("AppSetting not found")
|
ErrPluginSettingNotFound = errors.New("Plugin setting not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppSettings struct {
|
type PluginSetting struct {
|
||||||
Id int64
|
Id int64
|
||||||
AppId string
|
PluginId string
|
||||||
OrgId int64
|
OrgId int64
|
||||||
Enabled bool
|
Enabled bool
|
||||||
Pinned bool
|
Pinned bool
|
||||||
@ -39,17 +39,17 @@ func (s SecureJsonData) Decrypt() map[string]string {
|
|||||||
// COMMANDS
|
// COMMANDS
|
||||||
|
|
||||||
// Also acts as api DTO
|
// Also acts as api DTO
|
||||||
type UpdateAppSettingsCmd struct {
|
type UpdatePluginSettingCmd struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
Pinned bool `json:"pinned"`
|
Pinned bool `json:"pinned"`
|
||||||
JsonData map[string]interface{} `json:"jsonData"`
|
JsonData map[string]interface{} `json:"jsonData"`
|
||||||
SecureJsonData map[string]string `json:"secureJsonData"`
|
SecureJsonData map[string]string `json:"secureJsonData"`
|
||||||
|
|
||||||
AppId string `json:"-"`
|
PluginId string `json:"-"`
|
||||||
OrgId int64 `json:"-"`
|
OrgId int64 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cmd *UpdateAppSettingsCmd) GetEncryptedJsonData() SecureJsonData {
|
func (cmd *UpdatePluginSettingCmd) GetEncryptedJsonData() SecureJsonData {
|
||||||
encrypted := make(SecureJsonData)
|
encrypted := make(SecureJsonData)
|
||||||
for key, data := range cmd.SecureJsonData {
|
for key, data := range cmd.SecureJsonData {
|
||||||
encrypted[key] = util.Encrypt([]byte(data), setting.SecretKey)
|
encrypted[key] = util.Encrypt([]byte(data), setting.SecretKey)
|
||||||
@ -59,13 +59,13 @@ func (cmd *UpdateAppSettingsCmd) GetEncryptedJsonData() SecureJsonData {
|
|||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// QUERIES
|
// QUERIES
|
||||||
type GetAppSettingsQuery struct {
|
type GetPluginSettingsQuery struct {
|
||||||
OrgId int64
|
OrgId int64
|
||||||
Result []*AppSettings
|
Result []*PluginSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetAppSettingByAppIdQuery struct {
|
type GetPluginSettingByIdQuery struct {
|
||||||
AppId string
|
PluginId string
|
||||||
OrgId int64
|
OrgId int64
|
||||||
Result *AppSettings
|
Result *PluginSetting
|
||||||
}
|
}
|
@ -56,6 +56,10 @@ func (fp *FrontendPluginBase) handleModuleDefaults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
|
func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
|
||||||
|
if pathStr == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
u, _ := url.Parse(pathStr)
|
u, _ := url.Parse(pathStr)
|
||||||
if u.IsAbs() {
|
if u.IsAbs() {
|
||||||
return pathStr
|
return pathStr
|
||||||
|
@ -5,44 +5,40 @@ import (
|
|||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetOrgAppSettings(orgId int64) (map[string]*m.AppSettings, error) {
|
func GetPluginSettings(orgId int64) (map[string]*m.PluginSetting, error) {
|
||||||
query := m.GetAppSettingsQuery{OrgId: orgId}
|
query := m.GetPluginSettingsQuery{OrgId: orgId}
|
||||||
|
|
||||||
if err := bus.Dispatch(&query); err != nil {
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
orgAppsMap := make(map[string]*m.AppSettings)
|
pluginMap := make(map[string]*m.PluginSetting)
|
||||||
for _, orgApp := range query.Result {
|
for _, plug := range query.Result {
|
||||||
orgAppsMap[orgApp.AppId] = orgApp
|
pluginMap[plug.PluginId] = plug
|
||||||
}
|
}
|
||||||
|
|
||||||
return orgAppsMap, nil
|
return pluginMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) {
|
func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) {
|
||||||
enabledPlugins := NewEnabledPlugins()
|
enabledPlugins := NewEnabledPlugins()
|
||||||
orgApps, err := GetOrgAppSettings(orgId)
|
orgPlugins, err := GetPluginSettings(orgId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
enabledApps := make(map[string]bool)
|
enabledApps := make(map[string]bool)
|
||||||
|
|
||||||
for appId, installedApp := range Apps {
|
for pluginId, app := range Apps {
|
||||||
var app AppPlugin
|
|
||||||
app = *installedApp
|
|
||||||
|
|
||||||
// check if the app is stored in the DB for this org and if so, use the
|
if b, ok := orgPlugins[pluginId]; ok {
|
||||||
// state stored there.
|
|
||||||
if b, ok := orgApps[appId]; ok {
|
|
||||||
app.Enabled = b.Enabled
|
app.Enabled = b.Enabled
|
||||||
app.Pinned = b.Pinned
|
app.Pinned = b.Pinned
|
||||||
}
|
}
|
||||||
|
|
||||||
if app.Enabled {
|
if app.Enabled {
|
||||||
enabledApps[app.Id] = true
|
enabledApps[pluginId] = true
|
||||||
enabledPlugins.Apps = append(enabledPlugins.Apps, &app)
|
enabledPlugins.Apps = append(enabledPlugins.Apps, app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
package sqlstore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
bus.AddHandler("sql", GetAppSettings)
|
|
||||||
bus.AddHandler("sql", GetAppSettingByAppId)
|
|
||||||
bus.AddHandler("sql", UpdateAppSettings)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAppSettings(query *m.GetAppSettingsQuery) error {
|
|
||||||
sess := x.Where("org_id=?", query.OrgId)
|
|
||||||
|
|
||||||
query.Result = make([]*m.AppSettings, 0)
|
|
||||||
return sess.Find(&query.Result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAppSettingByAppId(query *m.GetAppSettingByAppIdQuery) error {
|
|
||||||
appSetting := m.AppSettings{OrgId: query.OrgId, AppId: query.AppId}
|
|
||||||
has, err := x.Get(&appSetting)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if has == false {
|
|
||||||
return m.ErrAppSettingNotFound
|
|
||||||
}
|
|
||||||
query.Result = &appSetting
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateAppSettings(cmd *m.UpdateAppSettingsCmd) error {
|
|
||||||
return inTransaction2(func(sess *session) error {
|
|
||||||
var app m.AppSettings
|
|
||||||
|
|
||||||
exists, err := sess.Where("org_id=? and app_id=?", cmd.OrgId, cmd.AppId).Get(&app)
|
|
||||||
sess.UseBool("enabled")
|
|
||||||
sess.UseBool("pinned")
|
|
||||||
if !exists {
|
|
||||||
app = m.AppSettings{
|
|
||||||
AppId: cmd.AppId,
|
|
||||||
OrgId: cmd.OrgId,
|
|
||||||
Enabled: cmd.Enabled,
|
|
||||||
Pinned: cmd.Pinned,
|
|
||||||
JsonData: cmd.JsonData,
|
|
||||||
SecureJsonData: cmd.GetEncryptedJsonData(),
|
|
||||||
Created: time.Now(),
|
|
||||||
Updated: time.Now(),
|
|
||||||
}
|
|
||||||
_, err = sess.Insert(&app)
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
for key, data := range cmd.SecureJsonData {
|
|
||||||
app.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey)
|
|
||||||
}
|
|
||||||
app.SecureJsonData = cmd.GetEncryptedJsonData()
|
|
||||||
app.Updated = time.Now()
|
|
||||||
app.Enabled = cmd.Enabled
|
|
||||||
app.JsonData = cmd.JsonData
|
|
||||||
app.Pinned = cmd.Pinned
|
|
||||||
_, err = sess.Id(app.Id).Update(&app)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
@ -4,12 +4,12 @@ import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|||||||
|
|
||||||
func addAppSettingsMigration(mg *Migrator) {
|
func addAppSettingsMigration(mg *Migrator) {
|
||||||
|
|
||||||
appSettingsV2 := Table{
|
pluginSettingTable := Table{
|
||||||
Name: "app_settings",
|
Name: "plugin_setting",
|
||||||
Columns: []*Column{
|
Columns: []*Column{
|
||||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
{Name: "org_id", Type: DB_BigInt, Nullable: true},
|
{Name: "org_id", Type: DB_BigInt, Nullable: true},
|
||||||
{Name: "app_id", Type: DB_NVarchar, Length: 255, Nullable: false},
|
{Name: "plugin_id", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||||
{Name: "enabled", Type: DB_Bool, Nullable: false},
|
{Name: "enabled", Type: DB_Bool, Nullable: false},
|
||||||
{Name: "pinned", Type: DB_Bool, Nullable: false},
|
{Name: "pinned", Type: DB_Bool, Nullable: false},
|
||||||
{Name: "json_data", Type: DB_Text, Nullable: true},
|
{Name: "json_data", Type: DB_Text, Nullable: true},
|
||||||
@ -18,14 +18,12 @@ func addAppSettingsMigration(mg *Migrator) {
|
|||||||
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||||
},
|
},
|
||||||
Indices: []*Index{
|
Indices: []*Index{
|
||||||
{Cols: []string{"org_id", "app_id"}, Type: UniqueIndex},
|
{Cols: []string{"org_id", "plugin_id"}, Type: UniqueIndex},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mg.AddMigration("Drop old table app_settings v1", NewDropTableMigration("app_settings"))
|
mg.AddMigration("create plugin_setting table", NewAddTableMigration(pluginSettingTable))
|
||||||
|
|
||||||
mg.AddMigration("create app_settings table v2", NewAddTableMigration(appSettingsV2))
|
|
||||||
|
|
||||||
//------- indexes ------------------
|
//------- indexes ------------------
|
||||||
addTableIndicesMigrations(mg, "v3", appSettingsV2)
|
addTableIndicesMigrations(mg, "v1", pluginSettingTable)
|
||||||
}
|
}
|
70
pkg/services/sqlstore/plugin_setting.go
Normal file
70
pkg/services/sqlstore/plugin_setting.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package sqlstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bus.AddHandler("sql", GetPluginSettings)
|
||||||
|
bus.AddHandler("sql", GetPluginSettingById)
|
||||||
|
bus.AddHandler("sql", UpdatePluginSetting)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPluginSettings(query *m.GetPluginSettingsQuery) error {
|
||||||
|
sess := x.Where("org_id=?", query.OrgId)
|
||||||
|
|
||||||
|
query.Result = make([]*m.PluginSetting, 0)
|
||||||
|
return sess.Find(&query.Result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPluginSettingById(query *m.GetPluginSettingByIdQuery) error {
|
||||||
|
pluginSetting := m.PluginSetting{OrgId: query.OrgId, PluginId: query.PluginId}
|
||||||
|
has, err := x.Get(&pluginSetting)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if has == false {
|
||||||
|
return m.ErrPluginSettingNotFound
|
||||||
|
}
|
||||||
|
query.Result = &pluginSetting
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePluginSetting(cmd *m.UpdatePluginSettingCmd) error {
|
||||||
|
return inTransaction2(func(sess *session) error {
|
||||||
|
var pluginSetting m.PluginSetting
|
||||||
|
|
||||||
|
exists, err := sess.Where("org_id=? and plugin_id=?", cmd.OrgId, cmd.PluginId).Get(&pluginSetting)
|
||||||
|
sess.UseBool("enabled")
|
||||||
|
sess.UseBool("pinned")
|
||||||
|
if !exists {
|
||||||
|
pluginSetting = m.PluginSetting{
|
||||||
|
PluginId: cmd.PluginId,
|
||||||
|
OrgId: cmd.OrgId,
|
||||||
|
Enabled: cmd.Enabled,
|
||||||
|
Pinned: cmd.Pinned,
|
||||||
|
JsonData: cmd.JsonData,
|
||||||
|
SecureJsonData: cmd.GetEncryptedJsonData(),
|
||||||
|
Created: time.Now(),
|
||||||
|
Updated: time.Now(),
|
||||||
|
}
|
||||||
|
_, err = sess.Insert(&pluginSetting)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
for key, data := range cmd.SecureJsonData {
|
||||||
|
pluginSetting.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey)
|
||||||
|
}
|
||||||
|
pluginSetting.SecureJsonData = cmd.GetEncryptedJsonData()
|
||||||
|
pluginSetting.Updated = time.Now()
|
||||||
|
pluginSetting.Enabled = cmd.Enabled
|
||||||
|
pluginSetting.JsonData = cmd.JsonData
|
||||||
|
pluginSetting.Pinned = cmd.Pinned
|
||||||
|
_, err = sess.Id(pluginSetting.Id).Update(&pluginSetting)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
81
public/app/core/components/colorpicker/colorpicker.ts
Normal file
81
public/app/core/components/colorpicker/colorpicker.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import config from 'app/core/config';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
|
var template = `
|
||||||
|
<div class="graph-legend-popover">
|
||||||
|
<a class="drop-popopver-close" ng-click="ctrl.close();" href="" ng-hide="ctrl.autoClose">
|
||||||
|
<i class="fa fa-times-circle"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div ng-show="ctrl.series" class="p-b-1">
|
||||||
|
<label>Y Axis:</label>
|
||||||
|
<button ng-click="ctrl.toggleAxis(yaxis);" class="btn btn-small"
|
||||||
|
ng-class="{'btn-success': ctrl.series.yaxis === 1,
|
||||||
|
'btn-inverse': ctrl.series.yaxis === 2}">
|
||||||
|
Left
|
||||||
|
</button>
|
||||||
|
<button ng-click="ctrl.toggleAxis(yaxis);"
|
||||||
|
class="btn btn-small"
|
||||||
|
ng-class="{'btn-success': ctrl.series.yaxis === 2,
|
||||||
|
'btn-inverse': ctrl.series.yaxis === 1}">
|
||||||
|
Right
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="m-b-0">
|
||||||
|
<i ng-repeat="color in ctrl.colors" class="pointer fa fa-circle"
|
||||||
|
ng-style="{color:color}"
|
||||||
|
ng-click="ctrl.colorSelected(color);"> </i>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
export class ColorPickerCtrl {
|
||||||
|
colors: any;
|
||||||
|
autoClose: boolean;
|
||||||
|
series: any;
|
||||||
|
showAxisControls: boolean;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor(private $scope, private $rootScope) {
|
||||||
|
this.colors = $rootScope.colors;
|
||||||
|
this.autoClose = $scope.autoClose;
|
||||||
|
this.series = $scope.series;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAxis(yaxis) {
|
||||||
|
this.$scope.toggleAxis();
|
||||||
|
|
||||||
|
if (!this.$scope.autoClose) {
|
||||||
|
this.$scope.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
colorSelected(color) {
|
||||||
|
this.$scope.colorSelected(color);
|
||||||
|
if (!this.$scope.autoClose) {
|
||||||
|
this.$scope.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.$scope.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function colorPicker() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
controller: ColorPickerCtrl,
|
||||||
|
bindToController: true,
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
template: template,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.directive('gfColorPicker', colorPicker);
|
@ -77,8 +77,8 @@ export class GrafanaCtrl {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$rootScope.performance.scopeCount = scopes;
|
|
||||||
f(root);
|
f(root);
|
||||||
|
$rootScope.performance.scopeCount = scopes;
|
||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
55
public/app/core/components/popover/popover.ts
Normal file
55
public/app/core/components/popover/popover.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import coreModule from '../../core_module';
|
||||||
|
import Drop from 'tether-drop';
|
||||||
|
|
||||||
|
export function popoverDirective() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
transclude: true,
|
||||||
|
link: function(scope, elem, attrs, ctrl, transclude) {
|
||||||
|
var inputElem = elem.prev();
|
||||||
|
if (inputElem.length === 0) {
|
||||||
|
console.log('Failed to find input element for popover');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = attrs.offset || '0 -10px';
|
||||||
|
|
||||||
|
transclude(function(clone, newScope) {
|
||||||
|
var content = document.createElement("div");
|
||||||
|
_.each(clone, (node) => {
|
||||||
|
content.appendChild(node);
|
||||||
|
});
|
||||||
|
|
||||||
|
var drop = new Drop({
|
||||||
|
target: inputElem[0],
|
||||||
|
content: content,
|
||||||
|
position: 'right middle',
|
||||||
|
classes: 'drop-help',
|
||||||
|
openOn: 'click',
|
||||||
|
tetherOptions: {
|
||||||
|
offset: offset
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// inputElem.on('focus.popover', function() {
|
||||||
|
// drop.open();
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// inputElem.on('blur.popover', function() {
|
||||||
|
// close();
|
||||||
|
// });
|
||||||
|
|
||||||
|
scope.$on('$destroy', function() {
|
||||||
|
drop.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.directive('gfPopover', popoverDirective);
|
@ -1,53 +1,51 @@
|
|||||||
<ul class="sidemenu">
|
<ul class="sidemenu">
|
||||||
|
|
||||||
<li class="sidemenu-org-section" ng-if="ctrl.isSignedIn" class="dropdown">
|
<li class="sidemenu-org-section" ng-if="::ctrl.isSignedIn" class="dropdown">
|
||||||
<div class="sidemenu-org">
|
<a class="sidemenu-org" href="profile">
|
||||||
<div class="sidemenu-org-avatar">
|
<div class="sidemenu-org-avatar">
|
||||||
<img ng-if="ctrl.user.gravatarUrl" ng-src="{{ctrl.user.gravatarUrl}}">
|
<img ng-src="{{::ctrl.user.gravatarUrl}}">
|
||||||
<span class="sidemenu-org-avatar--missing">
|
<span class="sidemenu-org-avatar--missing">
|
||||||
<i class="fa fa-fw fa-user"></i>
|
<i class="fa fa-fw fa-user"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidemenu-org-details">
|
<div class="sidemenu-org-details">
|
||||||
<span class="sidemenu-org-user sidemenu-item-text">{{ctrl.user.name}}</span>
|
<span class="sidemenu-org-user sidemenu-item-text">{{::ctrl.user.name}}</span>
|
||||||
<span class="sidemenu-org-name sidemenu-item-text">{{ctrl.user.orgName}}</span>
|
<span class="sidemenu-org-name sidemenu-item-text">{{::ctrl.user.orgName}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
<i class="fa fa-caret-right"></i>
|
<i class="fa fa-caret-right"></i>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
<li ng-repeat="menuItem in ctrl.orgMenu" ng-class="menuItem.cssClass">
|
<li ng-repeat="menuItem in ctrl.orgMenu" ng-class="::menuItem.cssClass">
|
||||||
<span ng-if="menuItem.section">{{menuItem.section}}</span>
|
<span ng-show="::menuItem.section">{{::menuItem.section}}</span>
|
||||||
<a href="{{menuItem.url}}" ng-if="menuItem.url" target="{{menuItem.target}}">
|
<a href="{{::menuItem.url}}" ng-show="::menuItem.url" target="{{::menuItem.target}}">
|
||||||
<i class="{{menuItem.icon}}" ng-if="menuItem.icon"></i>
|
<i class="{{::menuItem.icon}}" ng-show="::menuItem.icon"></i>
|
||||||
{{menuItem.text}}
|
{{::menuItem.text}}
|
||||||
</a>
|
</a>
|
||||||
<a ng-click="menuItem.click()" ng-if="menuItem.click">
|
<a ng-click="menuItem.click()" ng-show="::menuItem.click">
|
||||||
<i class="{{menuItem.icon}}"></i>
|
<i class="{{::menuItem.icon}}"></i>
|
||||||
{{menuItem.text}}
|
{{::menuItem.text}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li ng-repeat="item in ctrl.mainLinks" class="dropdown">
|
<li ng-repeat="item in ::ctrl.mainLinks" class="dropdown">
|
||||||
<a href="{{item.url}}" class="sidemenu-item sidemenu-main-link" target="{{item.target}}">
|
<a href="{{::item.url}}" class="sidemenu-item sidemenu-main-link" target="{{::item.target}}">
|
||||||
<span class="icon-circle sidemenu-icon">
|
<span class="icon-circle sidemenu-icon">
|
||||||
<i class="{{item.icon}}" ng-show="item.icon"></i>
|
<i class="{{::item.icon}}" ng-show="::item.icon"></i>
|
||||||
<img ng-src="{{item.img}}" ng-show="item.img">
|
<img ng-src="{{::item.img}}" ng-show="::item.img">
|
||||||
</span>
|
</span>
|
||||||
<span class="sidemenu-item-text">{{item.text}}</span>
|
<span class="sidemenu-item-text">{{::item.text}}</span>
|
||||||
<span class="fa fa-caret-right" ng-if="item.children"></span>
|
<span class="fa fa-caret-right" ng-if="::item.children"></span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu" role="menu" ng-if="item.children">
|
<ul class="dropdown-menu" role="menu" ng-if="::item.children">
|
||||||
<li ng-repeat="child in item.children">
|
<li ng-repeat="child in ::item.children" ng-class="{divider: child.divider}">
|
||||||
<a href="{{child.url}}">
|
<a href="{{::child.url}}">{{::child.text}}</a>
|
||||||
{{child.text}}
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li ng-if="!ctrl.isSignedIn">
|
<li ng-show="::!ctrl.isSignedIn">
|
||||||
<a href="login" class="sidemenu-item" target="_self">
|
<a href="login" class="sidemenu-item" target="_self">
|
||||||
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-sign-in"></i></span>
|
<span class="icon-circle sidemenu-icon"><i class="fa fa-fw fa-sign-in"></i></span>
|
||||||
<span class="sidemenu-item-text">Sign in</span>
|
<span class="sidemenu-item-text">Sign in</span>
|
||||||
|
@ -38,7 +38,6 @@ export class SideMenuCtrl {
|
|||||||
openUserDropdown() {
|
openUserDropdown() {
|
||||||
this.orgMenu = [
|
this.orgMenu = [
|
||||||
{section: 'You', cssClass: 'dropdown-menu-title'},
|
{section: 'You', cssClass: 'dropdown-menu-title'},
|
||||||
{text: 'Preferences', url: this.getUrl('/profile')},
|
|
||||||
{text: 'Profile', url: this.getUrl('/profile')},
|
{text: 'Profile', url: this.getUrl('/profile')},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -100,6 +99,22 @@ export function sideMenuDirective() {
|
|||||||
bindToController: true,
|
bindToController: true,
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
scope: {},
|
scope: {},
|
||||||
|
link: function(scope, elem) {
|
||||||
|
// hack to hide dropdown menu
|
||||||
|
elem.on('click.dropdown', '.dropdown-menu a', function(evt) {
|
||||||
|
var menu = $(evt.target).parents('.dropdown-menu');
|
||||||
|
var parent = menu.parent();
|
||||||
|
menu.detach();
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
parent.append(menu);
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$on("$destory", function() {
|
||||||
|
elem.off('click.dropdown');
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ import './partials';
|
|||||||
import {grafanaAppDirective} from './components/grafana_app';
|
import {grafanaAppDirective} from './components/grafana_app';
|
||||||
import {sideMenuDirective} from './components/sidemenu/sidemenu';
|
import {sideMenuDirective} from './components/sidemenu/sidemenu';
|
||||||
import {searchDirective} from './components/search/search';
|
import {searchDirective} from './components/search/search';
|
||||||
|
import {popoverDirective} from './components/popover/popover';
|
||||||
|
import {colorPicker} from './components/colorpicker/colorpicker';
|
||||||
import {navbarDirective} from './components/navbar/navbar';
|
import {navbarDirective} from './components/navbar/navbar';
|
||||||
import {arrayJoin} from './directives/array_join';
|
import {arrayJoin} from './directives/array_join';
|
||||||
import 'app/core/controllers/all';
|
import 'app/core/controllers/all';
|
||||||
@ -32,4 +34,13 @@ import 'app/core/routes/routes';
|
|||||||
import './filters/filters';
|
import './filters/filters';
|
||||||
import coreModule from './core_module';
|
import coreModule from './core_module';
|
||||||
|
|
||||||
export {arrayJoin, coreModule, grafanaAppDirective, sideMenuDirective, navbarDirective, searchDirective};
|
export {
|
||||||
|
arrayJoin,
|
||||||
|
coreModule,
|
||||||
|
grafanaAppDirective,
|
||||||
|
sideMenuDirective,
|
||||||
|
navbarDirective,
|
||||||
|
searchDirective,
|
||||||
|
colorPicker,
|
||||||
|
popoverDirective
|
||||||
|
};
|
||||||
|
@ -25,7 +25,8 @@ function ($, _, coreModule) {
|
|||||||
var dashboard = dashboardSrv.getCurrent();
|
var dashboard = dashboardSrv.getCurrent();
|
||||||
var time = '<i>' + dashboard.formatDate(event.min) + '</i>';
|
var time = '<i>' + dashboard.formatDate(event.min) + '</i>';
|
||||||
|
|
||||||
var tooltip = '<div class="graph-tooltip small"><div class="graph-tooltip-time">' + title + ' ' + time + '</div> ' ;
|
var tooltip = '<div class="graph-annotation">';
|
||||||
|
tooltip += '<div class="graph-annotation-title">' + title + "</div>";
|
||||||
|
|
||||||
if (event.text) {
|
if (event.text) {
|
||||||
var text = sanitizeString(event.text);
|
var text = sanitizeString(event.text);
|
||||||
@ -42,9 +43,10 @@ function ($, _, coreModule) {
|
|||||||
|
|
||||||
if (tags && tags.length) {
|
if (tags && tags.length) {
|
||||||
scope.tags = tags;
|
scope.tags = tags;
|
||||||
tooltip += '<span class="label label-tag" ng-repeat="tag in tags" tag-color-from-name="tag">{{tag}}</span><br/>';
|
tooltip += '<span class="label label-tag small" ng-repeat="tag in tags" tag-color-from-name="tag">{{tag}}</span><br/>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tooltip += '<div class="graph-annotation-time">' + time + '</div>' ;
|
||||||
tooltip += "</div>";
|
tooltip += "</div>";
|
||||||
|
|
||||||
var $tooltip = $(tooltip);
|
var $tooltip = $(tooltip);
|
||||||
|
@ -73,12 +73,7 @@ function ($, coreModule) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var src = "'" + payload.src + "'";
|
var src = "'" + payload.src + "'";
|
||||||
var cssClass = payload.cssClass || 'gf-box';
|
var view = $('<div class="tabbed-view" ng-include="' + src + '"></div>');
|
||||||
var view = $('<div class="' + cssClass + '" ng-include="' + src + '"></div>');
|
|
||||||
|
|
||||||
if (payload.cssClass) {
|
|
||||||
view.addClass(payload.cssClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
elem.append(view);
|
elem.append(view);
|
||||||
$compile(elem.contents())(editorScope);
|
$compile(elem.contents())(editorScope);
|
||||||
|
@ -39,7 +39,7 @@ function (angular, coreModule, kbn) {
|
|||||||
var tip = attrs.tip ? (' <tip>' + attrs.tip + '</tip>') : '';
|
var tip = attrs.tip ? (' <tip>' + attrs.tip + '</tip>') : '';
|
||||||
var showIf = attrs.showIf ? (' ng-show="' + attrs.showIf + '" ') : '';
|
var showIf = attrs.showIf ? (' ng-show="' + attrs.showIf + '" ') : '';
|
||||||
|
|
||||||
var template = '<div class="editor-option text-center"' + showIf + '>' +
|
var template = '<div class="editor-option gf-form-checkbox text-center"' + showIf + '>' +
|
||||||
' <label for="' + attrs.model + '" class="small">' +
|
' <label for="' + attrs.model + '" class="small">' +
|
||||||
attrs.text + tip + '</label>' +
|
attrs.text + tip + '</label>' +
|
||||||
'<input class="cr1" id="' + attrs.model + '" type="checkbox" ' +
|
'<input class="cr1" id="' + attrs.model + '" type="checkbox" ' +
|
||||||
|
@ -160,13 +160,13 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
}
|
}
|
||||||
// AppConfigCtrl
|
// AppConfigCtrl
|
||||||
case 'app-config-ctrl': {
|
case 'app-config-ctrl': {
|
||||||
let appModel = scope.ctrl.appModel;
|
let model = scope.ctrl.model;
|
||||||
return System.import(appModel.module).then(function(appModule) {
|
return System.import(model.module).then(function(appModule) {
|
||||||
return {
|
return {
|
||||||
baseUrl: appModel.baseUrl,
|
baseUrl: model.baseUrl,
|
||||||
name: 'app-config-' + appModel.appId,
|
name: 'app-config-' + model.pluginId,
|
||||||
bindings: {appModel: "=", appEditCtrl: "="},
|
bindings: {appModel: "=", appEditCtrl: "="},
|
||||||
attrs: {"app-model": "ctrl.appModel", "app-edit-ctrl": "ctrl"},
|
attrs: {"app-model": "ctrl.model", "app-edit-ctrl": "ctrl"},
|
||||||
Component: appModule.ConfigCtrl,
|
Component: appModule.ConfigCtrl,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -11,7 +11,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
|||||||
$locationProvider.html5Mode(true);
|
$locationProvider.html5Mode(true);
|
||||||
|
|
||||||
var loadOrgBundle = new BundleLoader('app/features/org/all');
|
var loadOrgBundle = new BundleLoader('app/features/org/all');
|
||||||
var loadAppsBundle = new BundleLoader('app/features/apps/all');
|
var loadPluginsBundle = new BundleLoader('app/features/plugins/all');
|
||||||
var loadAdminBundle = new BundleLoader('app/features/admin/admin');
|
var loadAdminBundle = new BundleLoader('app/features/admin/admin');
|
||||||
|
|
||||||
$routeProvider
|
$routeProvider
|
||||||
@ -165,23 +165,23 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
|||||||
controller : 'SnapshotsCtrl',
|
controller : 'SnapshotsCtrl',
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
})
|
})
|
||||||
.when('/apps', {
|
.when('/plugins', {
|
||||||
templateUrl: 'public/app/features/apps/partials/list.html',
|
templateUrl: 'public/app/features/plugins/partials/list.html',
|
||||||
controller: 'AppListCtrl',
|
controller: 'PluginListCtrl',
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
resolve: loadAppsBundle,
|
resolve: loadPluginsBundle,
|
||||||
})
|
})
|
||||||
.when('/apps/:appId/edit', {
|
.when('/plugins/:pluginId/edit', {
|
||||||
templateUrl: 'public/app/features/apps/partials/edit.html',
|
templateUrl: 'public/app/features/plugins/partials/edit.html',
|
||||||
controller: 'AppEditCtrl',
|
controller: 'PluginEditCtrl',
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
resolve: loadAppsBundle,
|
resolve: loadPluginsBundle,
|
||||||
})
|
})
|
||||||
.when('/apps/:appId/page/:slug', {
|
.when('/plugins/:pluginId/page/:slug', {
|
||||||
templateUrl: 'public/app/features/apps/partials/page.html',
|
templateUrl: 'public/app/features/plugins/partials/page.html',
|
||||||
controller: 'AppPageCtrl',
|
controller: 'AppPageCtrl',
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
resolve: loadAppsBundle,
|
resolve: loadPluginsBundle,
|
||||||
})
|
})
|
||||||
.when('/global-alerts', {
|
.when('/global-alerts', {
|
||||||
templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',
|
templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
define([
|
|
||||||
'angular',
|
|
||||||
'lodash',
|
|
||||||
'jquery',
|
|
||||||
'../core_module',
|
|
||||||
],
|
|
||||||
function (angular, _, $, coreModule) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
coreModule.default.service('popoverSrv', function($templateCache, $timeout, $q, $http, $compile) {
|
|
||||||
|
|
||||||
this.getTemplate = function(url) {
|
|
||||||
return $q.when($templateCache.get(url) || $http.get(url, {cache: true}));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.show = function(options) {
|
|
||||||
var popover;
|
|
||||||
|
|
||||||
// hide other popovers
|
|
||||||
$('.popover').each(function() {
|
|
||||||
popover = $(this).prev().data('popover');
|
|
||||||
if (popover) {
|
|
||||||
popover.scope.$destroy();
|
|
||||||
popover.destroy();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
options.scope.dismiss = function() {
|
|
||||||
popover = options.element.data('popover');
|
|
||||||
if (popover) {
|
|
||||||
popover.destroy();
|
|
||||||
}
|
|
||||||
options.scope.$destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getTemplate(options.templateUrl).then(function(result) {
|
|
||||||
$timeout(function() {
|
|
||||||
var template = _.isString(result) ? result : result.data;
|
|
||||||
|
|
||||||
options.element.popover({
|
|
||||||
content: template,
|
|
||||||
placement: options.placement || 'bottom',
|
|
||||||
html: true
|
|
||||||
});
|
|
||||||
|
|
||||||
popover = options.element.data('popover');
|
|
||||||
popover.hasContent = function () {
|
|
||||||
return template;
|
|
||||||
};
|
|
||||||
|
|
||||||
popover.toggle();
|
|
||||||
popover.scope = options.scope;
|
|
||||||
$compile(popover.$tip)(popover.scope);
|
|
||||||
}, 1);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
55
public/app/core/services/popover_srv.ts
Normal file
55
public/app/core/services/popover_srv.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import config from 'app/core/config';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import coreModule from 'app/core/core_module';
|
||||||
|
import Drop from 'tether-drop';
|
||||||
|
|
||||||
|
/** @ngInject **/
|
||||||
|
function popoverSrv($compile, $rootScope) {
|
||||||
|
|
||||||
|
this.show = function(options) {
|
||||||
|
var popoverScope = _.extend($rootScope.$new(true), options.model);
|
||||||
|
var drop;
|
||||||
|
|
||||||
|
function destroyDrop() {
|
||||||
|
setTimeout(function() {
|
||||||
|
if (drop.tether) {
|
||||||
|
drop.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
popoverScope.dismiss = function() {
|
||||||
|
popoverScope.$destroy();
|
||||||
|
destroyDrop();
|
||||||
|
};
|
||||||
|
|
||||||
|
var contentElement = document.createElement('div');
|
||||||
|
contentElement.innerHTML = options.template;
|
||||||
|
|
||||||
|
$compile(contentElement)(popoverScope);
|
||||||
|
|
||||||
|
drop = new Drop({
|
||||||
|
target: options.element,
|
||||||
|
content: contentElement,
|
||||||
|
position: options.position,
|
||||||
|
classes: 'drop-popover',
|
||||||
|
openOn: options.openOn || 'hover',
|
||||||
|
hoverCloseDelay: 200,
|
||||||
|
tetherOptions: {
|
||||||
|
constraints: [{to: 'window', pin: true, attachment: "both"}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.on('close', () => {
|
||||||
|
popoverScope.dismiss({fromDropClose: true});
|
||||||
|
destroyDrop();
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.open();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.service('popoverSrv', popoverSrv);
|
@ -12,39 +12,66 @@ function($, _) {
|
|||||||
|
|
||||||
kbn.round_interval = function(interval) {
|
kbn.round_interval = function(interval) {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
// 0.5s
|
// 0.3s
|
||||||
case (interval <= 500):
|
case (interval <= 300):
|
||||||
return 100; // 0.1s
|
return 100; // 0.1s
|
||||||
// 5s
|
// 0.75s
|
||||||
case (interval <= 5000):
|
case (interval <= 750):
|
||||||
|
return 500; // 0.5s
|
||||||
|
// 1.5s
|
||||||
|
case (interval <= 1500):
|
||||||
return 1000; // 1s
|
return 1000; // 1s
|
||||||
|
// 3.5s
|
||||||
|
case (interval <= 3500):
|
||||||
|
return 2000; // 2s
|
||||||
// 7.5s
|
// 7.5s
|
||||||
case (interval <= 7500):
|
case (interval <= 7500):
|
||||||
return 5000; // 5s
|
return 5000; // 5s
|
||||||
// 15s
|
// 12.5s
|
||||||
case (interval <= 15000):
|
case (interval <= 12500):
|
||||||
return 10000; // 10s
|
return 10000; // 10s
|
||||||
|
// 17.5s
|
||||||
|
case (interval <= 17500):
|
||||||
|
return 15000; // 15s
|
||||||
|
// 25s
|
||||||
|
case (interval <= 25000):
|
||||||
|
return 20000; // 20s
|
||||||
// 45s
|
// 45s
|
||||||
case (interval <= 45000):
|
case (interval <= 45000):
|
||||||
return 30000; // 30s
|
return 30000; // 30s
|
||||||
// 3m
|
// 1.5m
|
||||||
case (interval <= 180000):
|
case (interval <= 90000):
|
||||||
return 60000; // 1m
|
return 60000; // 1m
|
||||||
// 9m
|
// 3.5m
|
||||||
|
case (interval <= 210000):
|
||||||
|
return 120000; // 2m
|
||||||
|
// 7.5m
|
||||||
case (interval <= 450000):
|
case (interval <= 450000):
|
||||||
return 300000; // 5m
|
return 300000; // 5m
|
||||||
// 20m
|
// 12.5m
|
||||||
case (interval <= 1200000):
|
case (interval <= 750000):
|
||||||
return 600000; // 10m
|
return 600000; // 10m
|
||||||
|
// 12.5m
|
||||||
|
case (interval <= 1050000):
|
||||||
|
return 900000; // 15m
|
||||||
|
// 25m
|
||||||
|
case (interval <= 1500000):
|
||||||
|
return 1200000; // 20m
|
||||||
// 45m
|
// 45m
|
||||||
case (interval <= 2700000):
|
case (interval <= 2700000):
|
||||||
return 1800000; // 30m
|
return 1800000; // 30m
|
||||||
// 2h
|
// 1.5h
|
||||||
case (interval <= 7200000):
|
case (interval <= 5400000):
|
||||||
return 3600000; // 1h
|
return 3600000; // 1h
|
||||||
// 6h
|
// 2.5h
|
||||||
case (interval <= 21600000):
|
case (interval <= 9000000):
|
||||||
|
return 7200000; // 2h
|
||||||
|
// 4.5h
|
||||||
|
case (interval <= 16200000):
|
||||||
return 10800000; // 3h
|
return 10800000; // 3h
|
||||||
|
// 9h
|
||||||
|
case (interval <= 32400000):
|
||||||
|
return 21600000; // 6h
|
||||||
// 24h
|
// 24h
|
||||||
case (interval <= 86400000):
|
case (interval <= 86400000):
|
||||||
return 43200000; // 12h
|
return 43200000; // 12h
|
||||||
@ -593,12 +620,12 @@ function($, _) {
|
|||||||
{text: 'packets/sec', value: 'pps'},
|
{text: 'packets/sec', value: 'pps'},
|
||||||
{text: 'bits/sec', value: 'bps'},
|
{text: 'bits/sec', value: 'bps'},
|
||||||
{text: 'bytes/sec', value: 'Bps'},
|
{text: 'bytes/sec', value: 'Bps'},
|
||||||
{text: 'kilobites/sec', value: 'Kbits'},
|
{text: 'kilobits/sec', value: 'Kbits'},
|
||||||
{text: 'kilobytes/sec', value: 'KBs'},
|
{text: 'kilobytes/sec', value: 'KBs'},
|
||||||
{text: 'megabites/sec', value: 'Mbits'},
|
{text: 'megabits/sec', value: 'Mbits'},
|
||||||
{text: 'megabytes/sec', value: 'MBs'},
|
{text: 'megabytes/sec', value: 'MBs'},
|
||||||
{text: 'gigabytes/sec', value: 'GBs'},
|
{text: 'gigabytes/sec', value: 'GBs'},
|
||||||
{text: 'gigabites/sec', value: 'Gbits'},
|
{text: 'gigabits/sec', value: 'Gbits'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<navbar icon="fa fa-fw fa-cogs" title="Admin">
|
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
|
@ -1,58 +1,47 @@
|
|||||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||||
<nav-button title="Orgs" title-url="admin/orgs" icon="icon-gf icon-gf-users"></nav-button>
|
<a href="admin/orgs" class="navbar-page-btn">
|
||||||
|
<i class="icon-gf icon-gf-users"></i>
|
||||||
|
Orgs
|
||||||
|
</a>
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>
|
<h1>Edit Organization</h1>
|
||||||
Edit Organization
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form name="orgDetailsForm">
|
<form name="orgDetailsForm" class="gf-form-group">
|
||||||
<div>
|
<div class="gf-form">
|
||||||
<div class="tight-form">
|
<span class="gf-form-label width-10">Name</span>
|
||||||
<ul class="tight-form-list">
|
<input type="text" required ng-model="org.name" class="gf-form-input max-width-14" >
|
||||||
<li class="tight-form-item" style="width: 100px">
|
</div>
|
||||||
Name
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" required ng-model="org.name" class="input-xxlarge tight-form-input last" >
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
<div class="gf-form-button-row">
|
||||||
<button type="submit" class="pull-right btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
|
<button type="submit" class="btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
|
||||||
</form>
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<h3>
|
<h3 class="page-heading">Organization Users</h3>
|
||||||
Organization Users
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<table class="grafana-options-table form-inline">
|
<table class="grafana-options-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Username</th>
|
<th>Username</th>
|
||||||
<th>Email</th>
|
<th>Email</th>
|
||||||
<th>Role</th>
|
<th>Role</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="orgUser in orgUsers">
|
<tr ng-repeat="orgUser in orgUsers">
|
||||||
<td>{{orgUser.login}}</td>
|
<td>{{orgUser.login}}</td>
|
||||||
<td>{{orgUser.email}}</td>
|
<td>{{orgUser.email}}</td>
|
||||||
<td>
|
<td>
|
||||||
<select type="text" ng-model="orgUser.role" class="input-small" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(orgUser)">
|
<select type="text" ng-model="orgUser.role" class="gf-form-input max-width-8" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(orgUser)">
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1%">
|
<td style="width: 1%">
|
||||||
<a ng-click="removeOrgUser(orgUser)" class="btn btn-danger btn-mini">
|
<a ng-click="removeOrgUser(orgUser)" class="btn btn-danger btn-mini">
|
||||||
<i class="fa fa-remove"></i>
|
<i class="fa fa-remove"></i>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,129 +1,78 @@
|
|||||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||||
<nav-button title="Users" title-url="admin/users" icon="icon-gf icon-gf-users"></nav-button>
|
<a href="admin/users" class="navbar-page-btn">
|
||||||
|
<i class="icon-gf icon-gf-users"></i>
|
||||||
|
Users
|
||||||
|
</a>
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>
|
<h1>Edit User</h1>
|
||||||
Edit User
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form name="userForm">
|
<form name="userForm" class="gf-form-group">
|
||||||
<div>
|
<div class="gf-form">
|
||||||
<div class="tight-form">
|
<span class="gf-form-label width-10">Name</span>
|
||||||
<ul class="tight-form-list">
|
<input type="text" required ng-model="user.name" class="gf-form-input max-width-25" >
|
||||||
<li class="tight-form-item" style="width: 100px">
|
|
||||||
Name
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" required ng-model="user.name" class="input-xxlarge tight-form-input last" >
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-10">Email</span>
|
||||||
<li class="tight-form-item" style="width: 100px">
|
<input type="email" ng-model="user.email" class="gf-form-input max-width-25" >
|
||||||
Email
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="email" ng-model="user.email" class="input-xxlarge tight-form-input last" >
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-10">Username</span>
|
||||||
<li class="tight-form-item" style="width: 100px">
|
<input type="text" ng-model="user.login" class="gf-form-input max-width-25" >
|
||||||
Username
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" ng-model="user.login" class="input-xxlarge tight-form-input last" >
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
<div class="gf-form-button-row">
|
||||||
<button type="submit" class="pull-right btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
|
<button type="submit" class="btn btn-success" ng-click="update()" ng-show="!createMode">Update</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h3>
|
<h3 class="page-heading">Change password</h3>
|
||||||
Change password
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<form name="passwordForm">
|
<form name="passwordForm" class="gf-form-group">
|
||||||
<div>
|
<div class="gf-form">
|
||||||
<div class="tight-form">
|
<span class="gf-form-label width-10">New password</span>
|
||||||
<ul class="tight-form-list">
|
<input type="password" required ng-minlength="4" ng-model="password" class="gf-form-input max-width-25">
|
||||||
<li class="tight-form-item" style="width: 100px">
|
|
||||||
New password
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="password" required ng-minlength="4" ng-model="password" class="input-xxlarge tight-form-input last">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<div class="gf-form-button-row">
|
||||||
<button type="submit" class="pull-right btn btn-success" ng-click="setPassword()">Update</button>
|
<button type="submit" class="btn btn-success" ng-click="setPassword()">Update</button>
|
||||||
</form>
|
|
||||||
|
|
||||||
<h3>
|
|
||||||
Permissions
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
Grafana Admin
|
|
||||||
<input class="cr1" id="permissions.isGrafanaAdmin" type="checkbox"
|
|
||||||
ng-model="permissions.isGrafanaAdmin" ng-checked="permissions.isGrafanaAdmin">
|
|
||||||
<label for="permissions.isGrafanaAdmin" class="cr1"></label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<button type="submit" class="pull-right btn btn-success" ng-click="updatePermissions()">Update</button>
|
|
||||||
<br>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>
|
|
||||||
Organizations
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<form name="addOrgForm">
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 160px">
|
|
||||||
Add organization
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" ng-model="newOrg.name" bs-typeahead="searchOrgs"
|
|
||||||
required class="input-xlarge tight-form-input" placeholder="organization name">
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item">
|
|
||||||
Role
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<select type="text" ng-model="newOrg.role" class="input-small tight-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']">
|
|
||||||
</select>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button class="btn btn-success tight-form-btn" ng-click="addOrgUser()">Add</button>
|
|
||||||
</li>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<table class="grafana-options-table form-inline">
|
<h3 class="page-heading">Permissions</h3>
|
||||||
|
|
||||||
|
<form name="passwordForm" class="gf-form-group">
|
||||||
|
<div class="gf-form" >
|
||||||
|
<editor-checkbox text="Grafana Admin" model="permissions.isGrafanaAdmin" style="line-height: 1.5rem;"></editor-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-button-row">
|
||||||
|
<button type="submit" class="btn btn-success" ng-click="updatePermissions()">Update</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h3 class="page-heading">Organizations</h3>
|
||||||
|
|
||||||
|
<form name="addOrgForm" class="gf-form-group">
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-12">Add organization</span>
|
||||||
|
<input type="text" ng-model="newOrg.name" bs-typeahead="searchOrgs" required class="gf-form-input max-width-20" placeholder="organization name">
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label">Role</span>
|
||||||
|
<select type="text" ng-model="newOrg.role" class="gf-form-input width-10" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']"></select>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<button class="btn btn-success gf-form-btn" ng-click="addOrgUser()">Add</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<table class="grafana-options-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Role</th>
|
<th>Role</th>
|
||||||
@ -134,7 +83,7 @@
|
|||||||
{{org.name}} <span class="label label-info" ng-show="org.orgId === user.orgId">Current</span>
|
{{org.name}} <span class="label label-info" ng-show="org.orgId === user.orgId">Current</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select type="text" ng-model="org.role" class="input-small" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(org)">
|
<select type="text" ng-model="org.role" class="gf-form-input max-width-12" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']" ng-change="updateOrgUser(org)">
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1%">
|
<td style="width: 1%">
|
||||||
|
@ -1,63 +1,35 @@
|
|||||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||||
<nav-button title="Users" title-url="admin/users" icon="icon-gf icon-gf-users"></nav-button>
|
<a href="admin/users" class="navbar-page-btn">
|
||||||
|
<i class="icon-gf icon-gf-users"></i>
|
||||||
|
Users
|
||||||
|
</a>
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>
|
<h1>Add new user</h1>
|
||||||
Add new user
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form name="userForm">
|
<form name="userForm" class="gf-form-group">
|
||||||
<div>
|
<div class="gf-form">
|
||||||
<div class="tight-form">
|
<span class="gf-form-label width-10">Name</span>
|
||||||
<ul class="tight-form-list">
|
<input type="text" required ng-model="user.name" class="gf-form-input max-width-20" >
|
||||||
<li class="tight-form-item" style="width: 100px">
|
</div>
|
||||||
<strong>Name</strong>
|
<div class="gf-form">
|
||||||
</li>
|
<span class="gf-form-label width-10">Email</span>
|
||||||
<li>
|
<input type="email" ng-model="user.email" class="gf-form-input max-width-20" >
|
||||||
<input type="text" required ng-model="user.name" class="input-xxlarge tight-form-input last" >
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
</ul>
|
<span class="gf-form-label width-10">Username</span>
|
||||||
<div class="clearfix"></div>
|
<input type="text" ng-model="user.login" class="gf-form-input max-width-20" >
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-10">Password</span>
|
||||||
<li class="tight-form-item" style="width: 100px">
|
<input type="password" required ng-model="user.password" class="gf-form-input max-width-20" >
|
||||||
<strong>Email</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="email" ng-model="user.email" class="input-xxlarge tight-form-input last" >
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 100px">
|
|
||||||
<strong>Username</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" ng-model="user.login" class="input-xxlarge tight-form-input last" >
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 100px">
|
|
||||||
<strong>Password</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="password" required ng-model="user.password" class="input-xxlarge tight-form-input last" >
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<div class="gf-form-button-row">
|
||||||
<button type="submit" class="pull-right btn btn-success" ng-click="create()">Create</button>
|
<button type="submit" class="btn btn-success" ng-click="create()">Create</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||||
<nav-button title="Orgs" title-url="admin/orgs" icon="icon-gf icon-gf-users"></nav-button>
|
<a href="admin/orgs" class="navbar-page-btn">
|
||||||
|
<i class="icon-gf icon-gf-users"></i>
|
||||||
|
Orgs
|
||||||
|
</a>
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>
|
<h1>Organizations</h1>
|
||||||
Organizations
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="filter-table form-inline">
|
<table class="filter-table form-inline">
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>
|
<h1>Server settings</h1>
|
||||||
Server settings
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grafana-info-box span8" style="margin: 20px 0 25px 0">
|
<div class="grafana-info-box span8" style="margin: 20px 0 25px 0">
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>
|
<h1>Stats</h1>
|
||||||
Stats
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="filter-table form-inline">
|
<table class="filter-table form-inline">
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||||
<nav-button title="Users" title-url="admin/users" icon="icon-gf icon-gf-users"></nav-button>
|
<a href="admin/users" class="navbar-page-btn">
|
||||||
|
<i class="icon-gf icon-gf-users"></i>
|
||||||
|
Users
|
||||||
|
</a>
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
|
@ -1,106 +1,99 @@
|
|||||||
<div ng-controller="AnnotationsEditorCtrl" ng-init="init()">
|
<div ng-controller="AnnotationsEditorCtrl" ng-init="init()">
|
||||||
|
<div class="tabbed-view-header">
|
||||||
<div class="gf-box-header">
|
<h2 class="tabbed-view-title">
|
||||||
<div class="gf-box-title">
|
|
||||||
<i class="fa fa-bolt"></i>
|
|
||||||
Annotations
|
Annotations
|
||||||
</div>
|
</h2>
|
||||||
|
|
||||||
<div class="tabs">
|
<ul class="gf-tabs">
|
||||||
<ul class="nav nav-tabs">
|
<li class="gf-tabs-item" >
|
||||||
<li ng-class="{active: mode === 'list'}">
|
<a class="gf-tabs-link" ng-click="mode = 'list';" ng-class="{active: mode === 'list'}">
|
||||||
<a ng-click="mode = 'list';">
|
List
|
||||||
List
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
<li class="gf-tabs-item" ng-show="mode === 'edit'">
|
||||||
|
<a class="gf-tabs-link" ng-class="{active: mode === 'edit'}">
|
||||||
|
{{currentAnnotation.name}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="gf-tabs-item" ng-show="mode === 'new'">
|
||||||
|
<span class="active gf-tabs-link">New</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<li ng-class="{active: mode === 'edit'}" ng-show="mode === 'edit'">
|
<button class="tabbed-view-close-btn" ng-click="dismiss();">
|
||||||
<a>
|
|
||||||
{{currentAnnotation.name}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li ng-class="{active: mode === 'new'}">
|
|
||||||
<a ng-click="mode = 'new';">
|
|
||||||
<i class="fa fa-plus"></i>
|
|
||||||
New
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
|
||||||
<i class="fa fa-remove"></i>
|
<i class="fa fa-remove"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-box-body">
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="tabbed-view-body">
|
||||||
<div class="editor-row row" ng-if="mode === 'list'">
|
<div class="editor-row row" ng-if="mode === 'list'">
|
||||||
<div class="span6">
|
<div ng-if="annotations.length === 0">
|
||||||
<div ng-if="annotations.length === 0">
|
<em>No annotations defined</em>
|
||||||
<em>No annotations defined</em>
|
</div>
|
||||||
</div>
|
<table class="grafana-options-table">
|
||||||
<table class="grafana-options-table">
|
<tr ng-repeat="annotation in annotations">
|
||||||
<tr ng-repeat="annotation in annotations">
|
<td style="width:90%">
|
||||||
<td style="width:90%">
|
<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i>
|
||||||
<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i>
|
{{annotation.name}}
|
||||||
{{annotation.name}}
|
</td>
|
||||||
</td>
|
<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
|
||||||
<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
|
<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
|
||||||
<td style="width: 1%"><i ng-click="_.move(annotations,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
|
|
||||||
|
|
||||||
<td style="width: 1%" class="nobg">
|
<td style="width: 1%">
|
||||||
<a ng-click="edit(annotation)" class="btn btn-inverse btn-mini">
|
<a ng-click="edit(annotation)" class="btn btn-inverse btn-mini">
|
||||||
<i class="fa fa-edit"></i>
|
<i class="fa fa-edit"></i>
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td style="width: 1%" class="nobg">
|
<td style="width: 1%">
|
||||||
<a ng-click="removeAnnotation(annotation)" class="btn btn-danger btn-mini">
|
<a ng-click="removeAnnotation(annotation)" class="btn btn-danger btn-mini">
|
||||||
<i class="fa fa-remove"></i>
|
<i class="fa fa-remove"></i>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form" ng-show="mode === 'list'">
|
||||||
|
<div class="gf-form-button-row">
|
||||||
|
<a type="button" class="btn gf-form-button btn-success" ng-click="mode = 'new';"><i class="fa fa-plus" ></i> New</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="mode === 'edit' || mode === 'new'">
|
<div class="annotations-basic-settings" ng-if="mode === 'edit' || mode === 'new'">
|
||||||
<div class="editor-row">
|
<div class="gf-form-group">
|
||||||
<div class="editor-option">
|
<div class="gf-form-inline">
|
||||||
<label class="small">Name</label>
|
<div class="gf-form gf-size-max-xxl">
|
||||||
<input type="text" class="input-medium" ng-model='currentAnnotation.name' placeholder="name"></input>
|
<span class="gf-form-label">Name</span>
|
||||||
</div>
|
<input type="text" class="gf-form-input" ng-model='currentAnnotation.name' placeholder="name"></input>
|
||||||
<div class="editor-option">
|
</div>
|
||||||
<label class="small">Datasource</label>
|
<div class="gf-form">
|
||||||
<select ng-model="currentAnnotation.datasource" ng-options="f.name as f.name for f in datasources" ng-change="datasourceChanged()"></select>
|
<span class="gf-form-label max-width-10">Datasource</span>
|
||||||
</div>
|
<div class="gf-form-select-wrapper">
|
||||||
<div class="editor-option text-center">
|
<select class="gf-form-input gf-size-auto" ng-model="currentAnnotation.datasource" ng-options="f.name as f.name for f in datasources" ng-change="datasourceChanged()"></select>
|
||||||
<label class="small">Icon color</label>
|
</div>
|
||||||
<spectrum-picker ng-model="currentAnnotation.iconColor"></spectrum-picker>
|
</div>
|
||||||
</div>
|
<div class="gf-form">
|
||||||
<div class="editor-option">
|
<label class="gf-form-label">
|
||||||
<label class="small">Icon size</label>
|
<span>Color</span>
|
||||||
<select class="input-mini" ng-model="currentAnnotation.iconSize" ng-options="f for f in [7,8,9,10,13,15,17,20,25,30]"></select>
|
<spectrum-picker ng-model="currentAnnotation.iconColor"></spectrum-picker>
|
||||||
</div>
|
</label>
|
||||||
<editor-opt-bool text="Grid line" model="currentAnnotation.showLine"></editor-opt-bool>
|
</div>
|
||||||
<div class="editor-option text-center">
|
|
||||||
<label class="small">Line color</label>
|
|
||||||
<spectrum-picker ng-model="currentAnnotation.lineColor"></spectrum-picker>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<rebuild-on-change property="currentAnnotation.datasource">
|
<rebuild-on-change property="currentDatasource">
|
||||||
<plugin-component type="annotations-query-ctrl">
|
<plugin-component type="annotations-query-ctrl">
|
||||||
</plugin-component>
|
</plugin-component>
|
||||||
</rebuild-on-change>
|
</rebuild-on-change>
|
||||||
|
|
||||||
<br>
|
<div class="gf-form">
|
||||||
<button ng-show="mode === 'new'" type="button" class="btn btn-success" ng-click="add()">Add</button>
|
<div class="gf-form-button-row p-y-0">
|
||||||
<button ng-show="mode === 'edit'" type="button" class="btn btn-success pull-left" ng-click="update();">Update</button>
|
<button ng-show="mode === 'new'" type="button" class="btn gf-form-button btn-success" ng-click="add()">Add</button>
|
||||||
<br>
|
<button ng-show="mode === 'edit'" type="button" class="btn btn-success pull-left" ng-click="update()">Update</button>
|
||||||
<br>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import angular from 'angular';
|
|
||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
export class AppEditCtrl {
|
|
||||||
appModel: any;
|
|
||||||
appId: any;
|
|
||||||
includedPanels: any;
|
|
||||||
includedDatasources: any;
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor(private backendSrv: any, private $routeParams: any) {
|
|
||||||
this.appModel = {};
|
|
||||||
this.appId = $routeParams.appId;
|
|
||||||
|
|
||||||
this.backendSrv.get(`/api/org/apps/${this.appId}/settings`).then(result => {
|
|
||||||
this.appModel = result;
|
|
||||||
this.includedPanels = _.where(result.includes, {type: 'panel'});
|
|
||||||
this.includedDatasources = _.where(result.includes, {type: 'datasource'});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
|
||||||
var updateCmd = _.extend({
|
|
||||||
appId: this.appModel.appId,
|
|
||||||
orgId: this.appModel.orgId,
|
|
||||||
enabled: this.appModel.enabled,
|
|
||||||
pinned: this.appModel.pinned,
|
|
||||||
jsonData: this.appModel.jsonData,
|
|
||||||
secureJsonData: this.appModel.secureJsonData,
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
this.backendSrv.post(`/api/org/apps/${this.appId}/settings`, updateCmd).then(function() {
|
|
||||||
window.location.href = window.location.href;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleEnabled() {
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
togglePinned() {
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
angular.module('grafana.controllers').controller('AppEditCtrl', AppEditCtrl);
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
|
||||||
|
|
||||||
import angular from 'angular';
|
|
||||||
|
|
||||||
export class AppListCtrl {
|
|
||||||
apps: any[];
|
|
||||||
|
|
||||||
/** @ngInject */
|
|
||||||
constructor(private backendSrv: any) {
|
|
||||||
|
|
||||||
this.backendSrv.get('api/org/apps').then(apps => {
|
|
||||||
this.apps = apps;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
angular.module('grafana.controllers').controller('AppListCtrl', AppListCtrl);
|
|
@ -1,108 +0,0 @@
|
|||||||
<navbar title="Plugins" title-url="Plugins" icon="icon-gf icon-gf-apps">
|
|
||||||
</navbar>
|
|
||||||
|
|
||||||
<div class="page-container">
|
|
||||||
<div class="flex-container">
|
|
||||||
<div class="flex-column app-edit-logo-box">
|
|
||||||
<img src="{{ctrl.appModel.info.logos.large}}">
|
|
||||||
</div>
|
|
||||||
<div class="flex-column">
|
|
||||||
<h1>
|
|
||||||
{{ctrl.appModel.name}}
|
|
||||||
</h1>
|
|
||||||
<div class="app-edit-description">
|
|
||||||
{{ctrl.appModel.info.description}}<br>
|
|
||||||
<span style="small">
|
|
||||||
Version: {{ctrl.appModel.info.version}} Updated: {{ctrl.appModel.info.updated}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-inline">
|
|
||||||
<editor-checkbox text="Enabled" model="ctrl.appModel.enabled" change="ctrl.toggleEnabled()"></editor-checkbox>
|
|
||||||
|
|
||||||
<editor-checkbox text="Pinned" model="ctrl.appModel.pinned" change="ctrl.togglePinned()"></editor-checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex-column">
|
|
||||||
<ul class="app-edit-links">
|
|
||||||
<li>
|
|
||||||
By <a href="{{ctrl.appModel.info.author.url}}" class="external-link" target="_blank">{{ctrl.appModel.info.author.name}}</a>
|
|
||||||
</li>
|
|
||||||
<li ng-repeat="link in ctrl.appModel.info.links">
|
|
||||||
<a href="{{link.url}}" class="external-link" target="_blank">{{link.name}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="simple-box">
|
|
||||||
<h3 class="simple-box-header">Included with app:</h3>
|
|
||||||
<div class="flex-container">
|
|
||||||
<div class="simple-box-body simple-box-column">
|
|
||||||
<div class="simple-box-column-header">
|
|
||||||
<i class="fa fa-th-large"></i>
|
|
||||||
Dashboards
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li><em class="small">None</em></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="simple-box-body simple-box-column">
|
|
||||||
<div class="simple-box-column-header">
|
|
||||||
<i class="fa fa-line-chart"></i>
|
|
||||||
Panels
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li ng-show="!ctrl.includedPanels.length"><em class="small">None</em></li>
|
|
||||||
<li ng-repeat="panel in ctrl.includedPanels">
|
|
||||||
{{panel.name}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="simple-box-body simple-box-column">
|
|
||||||
<div class="simple-box-column-header">
|
|
||||||
<i class="fa fa-database"></i>
|
|
||||||
Datasources
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li ng-show="!ctrl.includedDatasources.length"><em class="small">None</em></li>
|
|
||||||
<li ng-repeat="ds in ctrl.includedDatasources">
|
|
||||||
{{ds.name}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="simple-box-body simple-box-column">
|
|
||||||
<div class="simple-box-column-header">
|
|
||||||
<i class="fa fa-files-o"></i>
|
|
||||||
Pages
|
|
||||||
</div>
|
|
||||||
<ul>
|
|
||||||
<li ng-repeat="page in ctrl.appModel.pages">
|
|
||||||
<a href="apps/{{ctrl.appId}}/page/{{page.slug}}" class="external-link">{{page.name}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="simple-box">
|
|
||||||
<h3 class="simple-box-header">Dependencies:</h3>
|
|
||||||
<div class="simple-box-body">
|
|
||||||
Grafana 2.6.x
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="simple-box">
|
|
||||||
<h3 class="simple-box-header">Configuration:</h3>
|
|
||||||
<div class="simple-box-body">
|
|
||||||
<div ng-if="ctrl.appModel.appId">
|
|
||||||
<plugin-component type="app-config-ctrl"></plugin-component>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,47 +0,0 @@
|
|||||||
<navbar title="Plugins" icon="icon-gf icon-gf-apps">
|
|
||||||
</navbar>
|
|
||||||
|
|
||||||
<div class="page-container">
|
|
||||||
<div class="page-header">
|
|
||||||
<h1>Plugins</h1>
|
|
||||||
</div>
|
|
||||||
<div ng-if="!ctrl.apps">
|
|
||||||
<em>No apps defined</em>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="filter-list">
|
|
||||||
<li ng-repeat="app in ctrl.apps">
|
|
||||||
<ul class="filter-list-card">
|
|
||||||
<li class="filter-list-card-image">
|
|
||||||
<img ng-src="{{app.info.logos.small}}">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<div class="filter-list-card-controls">
|
|
||||||
<div class="filter-list-card-config">
|
|
||||||
<a href="apps/{{app.appId}}/edit">
|
|
||||||
<i class="fa fa-cog"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span class="filter-list-card-title">
|
|
||||||
<a href="apps/{{app.appId}}/edit">
|
|
||||||
{{app.name}}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<span class="label label-info" ng-if="app.enabled">
|
|
||||||
Enabled
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="label label-info" ng-if="app.pinned">
|
|
||||||
Pinned
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</span>
|
|
||||||
<span class="filter-list-card-status">
|
|
||||||
<span class="filter-list-card-state">Dashboards: 1</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
@ -23,12 +23,12 @@
|
|||||||
<li ng-show="dashboardMeta.canShare" class="dropdown">
|
<li ng-show="dashboardMeta.canShare" class="dropdown">
|
||||||
<a class="pointer" ng-click="hideTooltip($event)" bs-tooltip="'Share dashboard'" data-placement="bottom" data-toggle="dropdown"><i class="fa fa-share-square-o"></i></a>
|
<a class="pointer" ng-click="hideTooltip($event)" bs-tooltip="'Share dashboard'" data-placement="bottom" data-toggle="dropdown"><i class="fa fa-share-square-o"></i></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li ng-if="dashboardMeta.canEdit">
|
<li>
|
||||||
<a class="pointer" ng-click="shareDashboard(0)">
|
<a class="pointer" ng-click="shareDashboard(0)">
|
||||||
<i class="fa fa-link"></i> Link to Dashboard
|
<i class="fa fa-link"></i> Link to Dashboard
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-if="dashboardMeta.canEdit">
|
<li>
|
||||||
<a class="pointer" ng-click="shareDashboard(1)">
|
<a class="pointer" ng-click="shareDashboard(1)">
|
||||||
<i class="icon-gf icon-gf-snapshot"></i>Snapshot sharing
|
<i class="icon-gf icon-gf-snapshot"></i>Snapshot sharing
|
||||||
</a>
|
</a>
|
||||||
|
@ -12,6 +12,8 @@ export class DashNavCtrl {
|
|||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
$scope.onAppEvent('save-dashboard', $scope.saveDashboard);
|
$scope.onAppEvent('save-dashboard', $scope.saveDashboard);
|
||||||
$scope.onAppEvent('delete-dashboard', $scope.deleteDashboard);
|
$scope.onAppEvent('delete-dashboard', $scope.deleteDashboard);
|
||||||
|
$scope.onAppEvent('export-dashboard', $scope.snapshot);
|
||||||
|
$scope.onAppEvent('quick-snapshot', $scope.quickSnapshot);
|
||||||
|
|
||||||
$scope.showSettingsMenu = $scope.dashboardMeta.canEdit || $scope.contextSrv.isEditor;
|
$scope.showSettingsMenu = $scope.dashboardMeta.canEdit || $scope.contextSrv.isEditor;
|
||||||
|
|
||||||
@ -52,6 +54,10 @@ export class DashNavCtrl {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.quickSnapshot = function() {
|
||||||
|
$scope.shareDashboard(1);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.openSearch = function() {
|
$scope.openSearch = function() {
|
||||||
$scope.appEvent('show-dash-search');
|
$scope.appEvent('show-dash-search');
|
||||||
};
|
};
|
||||||
|
@ -60,6 +60,14 @@ function(angular, $) {
|
|||||||
scope.appEvent('zoom-out', evt);
|
scope.appEvent('zoom-out', evt);
|
||||||
}, { inputDisabled: true });
|
}, { inputDisabled: true });
|
||||||
|
|
||||||
|
keyboardManager.bind('ctrl+e', function(evt) {
|
||||||
|
scope.appEvent('export-dashboard', evt);
|
||||||
|
}, { inputDisabled: true });
|
||||||
|
|
||||||
|
keyboardManager.bind('ctrl+i', function(evt) {
|
||||||
|
scope.appEvent('quick-snapshot', evt);
|
||||||
|
}, { inputDisabled: true });
|
||||||
|
|
||||||
keyboardManager.bind('esc', function() {
|
keyboardManager.bind('esc', function() {
|
||||||
var popups = $('.popover.in');
|
var popups = $('.popover.in');
|
||||||
if (popups.length > 0) {
|
if (popups.length > 0) {
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
<div ng-controller="GraphiteImportCtrl" ng-init="init()">
|
<div ng-controller="GraphiteImportCtrl" ng-init="init()">
|
||||||
|
|
||||||
<div ng-if="datasources.length > 0">
|
<div ng-if="datasources.length > 0">
|
||||||
<h2 style="margin-top: 30px;">Load dashboard from Graphite-Web</h2>
|
<h2 class="page-heading">Load dashboard from Graphite-Web</h2>
|
||||||
|
|
||||||
<div class="tight-form last">
|
<div class="gf-form-group">
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form-inline">
|
||||||
<li class="tight-form-item" style="width: 150px">
|
<div class="gf-form">
|
||||||
<strong>Data source</strong>
|
<span class="gf-form-label width-10">Data source</span>
|
||||||
</li>
|
</div>
|
||||||
<li>
|
<div class="gf-form">
|
||||||
<select type="text" ng-model="options.sourceName" class="input-medium tight-form-input" ng-options="f for f in datasources">
|
<div class="gf-form-select-wrapper">
|
||||||
</select>
|
<select type="text" ng-model="options.sourceName" class="gf-form-input gf-size-auto" ng-options="f for f in datasources">
|
||||||
</li>
|
</select>
|
||||||
<li style="float: right">
|
</div>
|
||||||
<button class="btn btn-inverse tight-form-btn" ng-click="listAll()">List dashboards</button>
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
<div class="clearfix"></div>
|
<button class="btn btn-success pull-right" ng-click="listAll()">List dashboards</button>
|
||||||
</ul>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="grafana-options-table" style="margin-top: 20px;">
|
<table class="grafana-options-table" style="margin-top: 20px;">
|
||||||
|
@ -24,7 +24,9 @@
|
|||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<div class="gf-form-label">Dashboard source</div>
|
<div class="gf-form-label">Dashboard source</div>
|
||||||
<div>
|
<div>
|
||||||
<select type="text" ng-model="sourceName" class="input-medium tight-form-input" ng-options="f for f in datasources"></select>
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input gf-size-auto" ng-model="sourceName" ng-options="f for f in datasources"></select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form-btn">
|
<div class="gf-form-btn">
|
||||||
<button class="btn btn-success" ng-click="startImport()">Import</button>
|
<button class="btn btn-success" ng-click="startImport()">Import</button>
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
<div ng-controller="DashLinksController">
|
|
||||||
<div class="editor-row">
|
|
||||||
<div class="tight-form-section">
|
|
||||||
<h5>Links and Dash Navigation</h5>
|
|
||||||
|
|
||||||
<div ng-repeat="link in panel.links">
|
|
||||||
<div class="tight-form" >
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item">
|
|
||||||
<i class="fa fa-remove pointer" ng-click="deleteLink(link)"></i>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="tight-form-item" style="width: 80px;">Link title</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" ng-model="link.title" class="input-medium tight-form-input">
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="tight-form-item">Type</li>
|
|
||||||
<li>
|
|
||||||
<select class="input-medium tight-form-input" style="width: 101px;" ng-model="link.type" ng-options="f for f in ['dashboard','absolute']"></select>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'dashboard'">Dashboard</li>
|
|
||||||
<li ng-show="link.type === 'dashboard'">
|
|
||||||
<input type="text"
|
|
||||||
ng-model="link.dashboard"
|
|
||||||
bs-typeahead="searchDashboards"
|
|
||||||
class="input-large tight-form-input">
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'absolute'">Url</li>
|
|
||||||
<li ng-show="link.type === 'absolute'">
|
|
||||||
<input type="text" ng-model="link.url" class="input-xlarge tight-form-input">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list" role="menu">
|
|
||||||
<li class="tight-form-item">
|
|
||||||
<i class="fa fa-remove invisible"></i>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" style="width: 80px;">
|
|
||||||
Params
|
|
||||||
<tip>Use var-variableName=value to pass templating variables.</tip>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" ng-model="link.params" class="input-xxlarge tight-form-input">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="editor-row">
|
|
||||||
<br>
|
|
||||||
<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,106 +1,81 @@
|
|||||||
<div class="gf-box-header">
|
<div class="tabbed-view-header">
|
||||||
<div class="gf-box-title">
|
<h2 class="tabbed-view-title">
|
||||||
<i class="fa fa-cogs"></i>
|
|
||||||
Settings
|
Settings
|
||||||
</div>
|
</h2>
|
||||||
|
|
||||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
<ul class="gf-tabs">
|
||||||
<div ng-repeat="tab in ['General', 'Rows', 'Links', 'Time picker', 'Metadata']" data-title="{{tab}}">
|
<li class="gf-tabs-item" ng-repeat="tab in ::['General', 'Rows', 'Links', 'Time picker', 'Metadata']">
|
||||||
</div>
|
<a class="gf-tabs-link" ng-click="editor.index = $index" ng-class="{active: editor.index === $index}">
|
||||||
</div>
|
{{::tab}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
<button class="tabbed-view-close-btn" ng-click="dismiss();">
|
||||||
<i class="fa fa-remove"></i>
|
<i class="fa fa-remove"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-box-body" style="padding-bottom: 50px;">
|
<div class="tabbed-view-body">
|
||||||
<div ng-if="editor.index == 0">
|
<div ng-if="editor.index == 0">
|
||||||
<div class="editor-row">
|
|
||||||
<div class="tight-form-section">
|
<h5 class="section-heading">Dashboard Detail</h5>
|
||||||
<h5>Dashboard info</h5>
|
<div class="gf-form-group">
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<label class="gf-form-label width-7">Title</label>
|
||||||
<li class="tight-form-item" style="width: 90px">
|
<input type="text" class="gf-form-input max-width-25" ng-model='dashboard.title'></input>
|
||||||
Title
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
<li>
|
<label class="gf-form-label width-7">Tags<tip>Press enter to a add tag</tip></label>
|
||||||
<input type="text" class="input-large tight-form-input" ng-model='dashboard.title'></input>
|
<bootstrap-tagsinput ng-model="dashboard.tags" tagclass="label label-tag" placeholder="add tags">
|
||||||
</li>
|
</bootstrap-tagsinput>
|
||||||
<li class="tight-form-item">
|
</div>
|
||||||
Tags
|
|
||||||
<tip>Press enter to a add tag</tip>
|
<div class="gf-form">
|
||||||
</li>
|
<label class="gf-form-label width-7">Timezone</label>
|
||||||
<li>
|
<div class="gf-form-select-wrapper">
|
||||||
<bootstrap-tagsinput ng-model="dashboard.tags" tagclass="label label-tag" placeholder="add tags">
|
<select ng-model="dashboard.timezone" class='gf-form-input' ng-options="f for f in ['browser','utc']"></select>
|
||||||
</bootstrap-tagsinput>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 90px">
|
|
||||||
Timezone
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<select ng-model="dashboard.timezone" class='input-small tight-form-input' ng-options="f for f in ['browser','utc']"></select>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row">
|
<h5 class="section-heading">On/Off Toggles</h5>
|
||||||
<div class="tight-form-section">
|
<div class="gf-form-group">
|
||||||
<h5>Toggles</h5>
|
<div class="gf-form-inline">
|
||||||
<div class="tight-form last">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<editor-checkbox text="Editable" model="dashboard.editable"></editor-checkbox>
|
||||||
<li class="tight-form-item">
|
</div>
|
||||||
<editor-checkbox text="Editable" model="dashboard.editable"></editor-checkbox>
|
<div class="gf-form">
|
||||||
</li>
|
<editor-checkbox text="Hide Controls (CTRL+H)" model="dashboard.hideControls"></editor-checkbox>
|
||||||
<li class="tight-form-item">
|
</div>
|
||||||
<editor-checkbox text="Hide Controls (CTRL+H)" model="dashboard.hideControls"></editor-checkbox>
|
<div class="gf-form">
|
||||||
</li>
|
<editor-checkbox text="Shared Crosshair (CTRL+O)" model="dashboard.sharedCrosshair"></editor-checkbox>
|
||||||
<li class="tight-form-item last">
|
|
||||||
<editor-checkbox text="Shared Crosshair (CTRL+O)" model="dashboard.sharedCrosshair"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="editor.index == 1">
|
<div ng-if="editor.index == 1">
|
||||||
<div class="editor-row">
|
<h5 class="section-heading">Rows settings</h5>
|
||||||
<div class="tight-form-section">
|
|
||||||
<h5>Rows settings</h5>
|
<div class="gf-form-group">
|
||||||
<div class="tight-form-container">
|
<div class="gf-form-inline" ng-repeat="row in dashboard.rows">
|
||||||
<div class="tight-form" ng-repeat="row in dashboard.rows">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label">Title</span>
|
||||||
<li class="tight-form-item">
|
<input type="text" class="gf-form-input max-width-14" ng-model='row.title'></input>
|
||||||
Title
|
<editor-checkbox text="Show title" model="row.showTitle"></editor-checkbox>
|
||||||
</li>
|
</div>
|
||||||
<li>
|
|
||||||
<input type="text" class="input tight-form-input" style="width: 400px;" ng-model='row.title'></input>
|
<div class="gf-form">
|
||||||
</li>
|
<button class="btn btn-inverse btn-mini" style="margin-right: 5px;" ng-click="dashboard.rows = _.without(dashboard.rows,row)">
|
||||||
<li class="tight-form-item">
|
<i class="fa fa-trash"></i>
|
||||||
<editor-checkbox text="Show title" model="row.showTitle"></editor-checkbox>
|
</button>
|
||||||
</li>
|
<button class="btn btn-inverse btn-mini" ng-hide="$first" style="margin-right: 5px;" ng-click="_.move(dashboard.rows,$index,$index-1)">
|
||||||
<li class="tight-form-item last">
|
<i ng-class="{'invisible': $first}" class="fa fa-arrow-up"></i>
|
||||||
<i ng-click="_.move(dashboard.rows,$index,$index-1)" ng-class="{'invisible': $first}" class="pointer fa fa-arrow-up"></i>
|
</button>
|
||||||
</li>
|
<button class="btn btn-inverse btn-mini" ng-hide="$last" style="margin-right: 5px;" ng-click="_.move(dashboard.rows,$index,$index+1)">
|
||||||
<li class="tight-form-item last">
|
<i ng-class="{'invisible': $last}" class="fa fa-arrow-down"></i>
|
||||||
<i ng-click="_.move(dashboard.rows,$index,$index+1)" ng-class="{'invisible': $last}" class="pointer fa fa-fw fa-arrow-down"></i>
|
</button>
|
||||||
</li>
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<i ng-click="dashboard.rows = _.without(dashboard.rows,row)" class="pointer fa fa-remove"></i>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -114,69 +89,31 @@
|
|||||||
<gf-time-picker-settings dashboard="dashboard"></gf-time-picker-settings>
|
<gf-time-picker-settings dashboard="dashboard"></gf-time-picker-settings>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="editor.index == 4">
|
<div ng-if="editor.index == 4">
|
||||||
<div class="row">
|
<h5 class="section-heading">Dashboard info</h5>
|
||||||
<h5>Dashboard info</h5>
|
<div class="gf-form-group">
|
||||||
<div class="pull-left tight-form">
|
<div class="gf-form">
|
||||||
<div class="tight-form">
|
<span class="gf-form-label width-10">Last updated at:</span>
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-18">{{formatDate(dashboardMeta.updated)}}</span>
|
||||||
<li class="tight-form-item" style="width: 120px">
|
</div>
|
||||||
Last updated at:
|
<div class="gf-form">
|
||||||
</li>
|
<span class="gf-form-label width-10">Last updated by:</span>
|
||||||
<li class="tight-form-item" style="width: 180px">
|
<span class="gf-form-label width-18">{{dashboardMeta.updatedBy}} </span>
|
||||||
{{formatDate(dashboardMeta.updated)}}
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
</ul>
|
<span class="gf-form-label width-10">Created at:</span>
|
||||||
<div class="clearfix"></div>
|
<span class="gf-form-label width-18">{{formatDate(dashboardMeta.created)}} </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-10">Created by:</span>
|
||||||
<li class="tight-form-item" style="width: 120px">
|
<span class="gf-form-label width-18">{{dashboardMeta.createdBy}} </span>
|
||||||
Last updated by:
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
<li class="tight-form-item" style="width: 180px">
|
<span class="gf-form-label width-10">Current version:</span>
|
||||||
{{dashboardMeta.updatedBy}}
|
<span class="gf-form-label width-18">{{dashboardMeta.version}} </span>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</div>
|
||||||
<div class="clearfix"></div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 120px">
|
|
||||||
Created at:
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" style="width: 180px">
|
|
||||||
{{formatDate(dashboardMeta.created)}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 120px">
|
|
||||||
Created by:
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" style="width: 180px">
|
|
||||||
{{dashboardMeta.createdBy}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 120px">
|
|
||||||
Current version:
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" style="width: 180px">
|
|
||||||
{{dashboardMeta.version}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
@ -35,53 +35,30 @@
|
|||||||
|
|
||||||
<div ng-include src="'shareLinkOptions.html'"></div>
|
<div ng-include src="'shareLinkOptions.html'"></div>
|
||||||
|
|
||||||
<div class="gf-form">
|
<div class="gf-form-group position-center">
|
||||||
<div class="gf-form-row">
|
<div class="gf-form width-30" >
|
||||||
<span class="gf-fluid-input">
|
<textarea rows="5" data-share-panel-url class="gf-form-input width-30" ng-model='iframeHtml'></textarea>
|
||||||
<textarea rows="5" data-share-panel-url class="input" ng-model='iframeHtml'></textarea>
|
</div>
|
||||||
</span>
|
</div>
|
||||||
|
<div class="gf-form-group">
|
||||||
|
<div class="gf-form position-center">
|
||||||
|
<button class="btn btn-inverse" data-clipboard-text="{{iframeHtml}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-inverse" data-clipboard-text="{{iframeHtml}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
|
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/ng-template" id="shareLinkOptions.html">
|
<script type="text/ng-template" id="shareLinkOptions.html">
|
||||||
<div class="editor-row" style="margin: 11px 20px 33px 20px">
|
<div class="gf-form-group position-center">
|
||||||
<div class="section">
|
<div class="gf-form">
|
||||||
<div class="tight-form">
|
<editor-checkbox text="Current time range" model="options.forCurrent" change="updated()"></editor-checkbox>
|
||||||
<ul class="tight-form-list">
|
</div>
|
||||||
<li class="tight-form-item" style="width: 170px;">
|
<div class="gf-form">
|
||||||
<label class="checkbox-label" for="options.forCurrent">Current time range</label>
|
<editor-checkbox text="Include template variables" model="options.includeTemplateVars" change="updated()"></editor-checkbox>
|
||||||
</li>
|
</div>
|
||||||
<li class="tight-form-item last">
|
<div class="gf-form">
|
||||||
<input class="cr1" id="options.forCurrent" type="checkbox" ng-model="options.forCurrent" ng-checked="options.forCurrent" ng-change="buildUrl()">
|
<span class="gf-form-label">Theme</span>
|
||||||
<label for="options.forCurrent" class="cr1"></label>
|
<div class="gf-form-select-wrapper max-width-10">
|
||||||
</li>
|
<select class="gf-form-input" ng-model="options.theme" ng-options="f as f for f in ['current', 'dark', 'light']" ng-change="buildUrl()"></select>
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 170px">
|
|
||||||
<label class="checkbox-label" for="options.includeTemplateVars">Include template variables</label>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<input class="cr1" id="options.includeTemplateVars" type="checkbox" ng-model="options.includeTemplateVars" ng-checked="options.includeTemplateVars" ng-change="buildUrl()">
|
|
||||||
<label for="options.includeTemplateVars" class="cr1"></label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 170px">
|
|
||||||
Theme
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<select class="input-small tight-form-input last" style="width: 211px" ng-model="options.theme" ng-options="f as f for f in ['current', 'dark', 'light']" ng-change="buildUrl()"></select>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -93,14 +70,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-include src="'shareLinkOptions.html'"></div>
|
<div ng-include src="'shareLinkOptions.html'"></div>
|
||||||
<div class="gf-form">
|
<div class="gf-form-group position-center">
|
||||||
<div class="gf-form-row">
|
<div class="gf-form-inline">
|
||||||
<button class="btn btn-inverse pull-right" data-clipboard-text="{{shareUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
|
|
||||||
<span class="gf-fluid-input">
|
<div class="gf-form width-30">
|
||||||
<input type="text" data-share-panel-url class="input" ng-model='shareUrl'></input>
|
<input type="text" data-share-panel-url class="gf-form-input" ng-model="shareUrl"></input>
|
||||||
</span>
|
</div>
|
||||||
|
<div class="gf-form pull-right">
|
||||||
|
<button class="btn btn-inverse pull-right" data-clipboard-text="{{shareUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-row" style="margin-top: 5px;" ng-show="modeSharePanel">
|
</div>
|
||||||
|
<div class="gf-form-group">
|
||||||
|
<div class="gf-form position-center" ng-show="modeSharePanel">
|
||||||
<a href="{{imageUrl}}" target="_blank"><i class="fa fa-camera"></i> Direct link rendered image</a>
|
<a href="{{imageUrl}}" target="_blank"><i class="fa fa-camera"></i> Direct link rendered image</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -132,29 +114,15 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row share-modal-options" style="">
|
<div class="gf-form-group share-modal-options position-center">
|
||||||
<div class="section" ng-if="step === 1">
|
<div class="gf-form" ng-if="step === 1">
|
||||||
<div class="tight-form">
|
<span class="gf-form-label width-12">Snapshot name</span>
|
||||||
<ul class="tight-form-list">
|
<input type="text" ng-model="snapshot.name" class="gf-form-input max-width-15" >
|
||||||
<li class="tight-form-item" style="width: 110px;">
|
</div>
|
||||||
Snapshot name
|
<div class="gf-form" ng-if="step === 1">
|
||||||
</li>
|
<span class="gf-form-label width-12">Expire</span>
|
||||||
<li>
|
<div class="gf-form-select-wrapper max-width-15">
|
||||||
<input type="text" ng-model="snapshot.name" class="input-large tight-form-input last" >
|
<select class="gf-form-input" ng-model="snapshot.expires" ng-options="f.value as f.text for f in expireOptions"></select>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 110px">
|
|
||||||
Expire
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<select class="input-small tight-form-input last" style="width: 211px" ng-model="snapshot.expires" ng-options="f.value as f.text for f in expireOptions"></select>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -168,21 +136,21 @@
|
|||||||
<button class="btn btn-inverse btn-large" data-clipboard-text="{{snapshotUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy Link</button>
|
<button class="btn btn-inverse btn-large" data-clipboard-text="{{snapshotUrl}}" clipboard-button><i class="fa fa-clipboard"></i> Copy Link</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="step === 1">
|
<div ng-if="step === 1" class="gf-form-buttons-row">
|
||||||
<button class="btn btn-success btn-large" ng-click="createSnapshot()" ng-disabled="loading">
|
<button class="btn btn-success btn-large" ng-click="createSnapshot()" ng-disabled="loading">
|
||||||
<i class="fa fa-save"></i>
|
<i class="fa fa-save"></i>
|
||||||
Local Snapshot
|
Local Snapshot
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary btn-large" ng-if="externalEnabled" ng-click="createSnapshot(true)" ng-disabled="loading">
|
<button class="btn btn-primary btn-large" ng-if="externalEnabled" ng-click="createSnapshot(true)" ng-disabled="loading">
|
||||||
<i class="fa fa-cloud-upload"></i>
|
<i class="fa fa-cloud-upload"></i>
|
||||||
{{sharingButtonText}}
|
{{sharingButtonText}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pull-right" ng-if="step === 2" style="padding: 5px">
|
<div class="pull-right" ng-if="step === 2" style="padding: 5px">
|
||||||
Did you make a mistake? <a class="pointer" ng-click="deleteSnapshot()" target="_blank">delete snapshot.</a>
|
Did you make a mistake? <a class="pointer" ng-click="deleteSnapshot()" target="_blank">delete snapshot.</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,30 +1,27 @@
|
|||||||
<div class="submenu-controls">
|
<div class="submenu-controls">
|
||||||
<div class="tight-form borderless">
|
<ul ng-if="ctrl.dashboard.templating.list.length > 0">
|
||||||
|
<li ng-repeat="variable in ctrl.variables" class="submenu-item">
|
||||||
|
<span class="submenu-item-label template-variable " ng-show="!variable.hideLabel">
|
||||||
|
{{variable.label || variable.name}}:
|
||||||
|
</span>
|
||||||
|
<value-select-dropdown variable="variable" on-updated="ctrl.variableUpdated(variable)" get-values-for-tag="ctrl.getValuesForTag(variable, tagKey)"></value-select-dropdown>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<ul class="tight-form-list" ng-if="ctrl.dashboard.templating.list.length > 0">
|
<ul ng-if="ctrl.dashboard.annotations.list.length > 0">
|
||||||
<li ng-repeat="variable in ctrl.variables" class="submenu-item">
|
<li ng-repeat="annotation in ctrl.dashboard.annotations.list" class="submenu-item annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">
|
||||||
<span class="template-variable tight-form-item" ng-show="!variable.hideLabel" style="padding-right: 5px">
|
<a ng-click="ctrl.disableAnnotation(annotation)">
|
||||||
{{variable.label || variable.name}}:
|
<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i>
|
||||||
</span>
|
{{annotation.name}}
|
||||||
<value-select-dropdown variable="variable" on-updated="ctrl.variableUpdated(variable)" get-values-for-tag="ctrl.getValuesForTag(variable, tagKey)"></value-select-dropdown>
|
<input class="cr1" id="hideYAxis" type="checkbox" ng-model="annotation.enable" ng-checked="annotation.enable">
|
||||||
</li>
|
<label for="hideYAxis" class="cr1"></label>
|
||||||
</ul>
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<ul class="tight-form-list" ng-if="ctrl.dashboard.annotations.list.length > 0">
|
<ul class="pull-right" ng-if="ctrl.dashboard.links.length > 0">
|
||||||
<li ng-repeat="annotation in ctrl.dashboard.annotations.list" class="submenu-item annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">
|
<dash-links-container links="ctrl.dashboard.links"></dash-links-container>
|
||||||
<a ng-click="ctrl.disableAnnotation(annotation)">
|
</ul>
|
||||||
<i class="fa fa-bolt" style="color:{{annotation.iconColor}}"></i>
|
|
||||||
{{annotation.name}}
|
|
||||||
<input class="cr1" id="hideYAxis" type="checkbox" ng-model="annotation.enable" ng-checked="annotation.enable">
|
|
||||||
<label for="hideYAxis" class="cr1"></label>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="tight-form-list pull-right" ng-if="ctrl.dashboard.links.length > 0">
|
<div class="clearfix"></div>
|
||||||
<dash-links-container links="ctrl.dashboard.links"></dash-links-container>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,48 +1,15 @@
|
|||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<div class="section">
|
<div class="gf-form-group">
|
||||||
<div>
|
<div class="gf-form">
|
||||||
<!-- <div class="tight-form"> -->
|
<span class="gf-form-label width-10">Auto-refresh</span>
|
||||||
<!-- <ul class="tight-form-list"> -->
|
<input type="text" class="gf-form-input max-width-25" ng-model="ctrl.panel.refresh_intervals" array-join>
|
||||||
<!-- <li class="tight-form-item" style="width: 118px"> -->
|
</div>
|
||||||
<!-- Relative times -->
|
<div class="gf-form">
|
||||||
<!-- </li> -->
|
<span class="gf-form-label width-10">Now delay now-</span>
|
||||||
<!-- <li> -->
|
<input type="text" class="gf-form-input max-width-25"
|
||||||
<!-- <input type="text" class="input-xlarge tight-form-input last" style="width: 450px" ng-model="ctrl.panel.time_options" array-join> -->
|
ng-model="ctrl.panel.nowDelay" placeholder="0m"
|
||||||
<!-- </li> -->
|
valid-time-span
|
||||||
<!-- </ul> -->
|
bs-tooltip="'Enter 1m to ignore the last minute (because it can contain incomplete metrics)'"
|
||||||
<!-- <div class="clearfix"></div> -->
|
data-placement="right">
|
||||||
<!-- </div> -->
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 118px">
|
|
||||||
Auto-refresh
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" class="input-xlarge tight-form-input last" style="width: 450px" ng-model="ctrl.panel.refresh_intervals" array-join>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 118px">
|
|
||||||
Now delay
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item">
|
|
||||||
now-
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" class="input-mini tight-form-input last"
|
|
||||||
ng-model="ctrl.panel.nowDelay" placeholder="0m"
|
|
||||||
valid-time-span
|
|
||||||
bs-tooltip="'Enter 1m to ignore the last minute (because it can contain incomplete metrics)'"
|
|
||||||
data-placement="right">
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -129,8 +129,6 @@ export class TimePickerCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setRelativeFilter(timespan) {
|
setRelativeFilter(timespan) {
|
||||||
this.panel.now = true;
|
|
||||||
|
|
||||||
var range = {from: timespan.from, to: timespan.to};
|
var range = {from: timespan.from, to: timespan.to};
|
||||||
|
|
||||||
if (this.panel.nowDelay && range.to === 'now') {
|
if (this.panel.nowDelay && range.to === 'now') {
|
||||||
|
@ -1,90 +1,76 @@
|
|||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<h5>Links and Dash Navigation</h5>
|
<h5 class="section-heading">Links and Dash Navigation</h5>
|
||||||
|
|
||||||
<div ng-repeat="link in dashboard.links" style="margin-top: 10px;">
|
<div ng-repeat="link in dashboard.links">
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list pull-right">
|
|
||||||
<li class="tight-form-item">
|
|
||||||
<i ng-click="moveLink($index, -1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i>
|
|
||||||
<i ng-click="moveLink($index, 1)" ng-hide="$last" class="pointer fa fa-fw fa-arrow-down"></i>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<i class="fa fa-remove pointer" ng-click="deleteLink($index)"></i>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form-group">
|
||||||
<li class="tight-form-item" style="width: 20px">
|
<div class="gf-form-inline">
|
||||||
<i class="fa fa-fw fa-unlink"></i>
|
<div class="gf-form">
|
||||||
</li>
|
<span class="gf-form-label width-6">Type</span>
|
||||||
|
<div class="gf-form-select-wrapper width-10">
|
||||||
|
<select class="gf-form-input" ng-model="link.type" ng-options="f for f in ['dashboards','link']" ng-change="updated()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<li class="tight-form-item">Type</li>
|
<div class="gf-form" ng-show="link.type === 'dashboards'">
|
||||||
<li>
|
<span class="gf-form-label">With tags</span>
|
||||||
<select class="input-medium tight-form-input" style="width: 150px;" ng-model="link.type" ng-options="f for f in ['dashboards','link']" ng-change="updated()"></select>
|
<bootstrap-tagsinput ng-model="link.tags" tagclass="label label-tag" placeholder="add tags"></bootstrap-tagsinput>
|
||||||
</li>
|
</div>
|
||||||
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'dashboards'">With tags</li>
|
<div class="gf-form" ng-show="link.type === 'dashboards'">
|
||||||
<li ng-show="link.type === 'dashboards'">
|
|
||||||
<bootstrap-tagsinput ng-model="link.tags" tagclass="label label-tag" placeholder="add tags">
|
|
||||||
</bootstrap-tagsinput>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'dashboards'">
|
|
||||||
<editor-checkbox text="As dropdown" model="link.asDropdown" change="updated()"></editor-checkbox>
|
<editor-checkbox text="As dropdown" model="link.asDropdown" change="updated()"></editor-checkbox>
|
||||||
</li>
|
</div>
|
||||||
<li class="tight-form-item" ng-show="link.type === 'dashboards' && link.asDropdown">
|
|
||||||
Title
|
<div class="gf-form max-width-30" ng-show="link.type === 'link'">
|
||||||
</li>
|
<li class="gf-form-label width-6">Url</li>
|
||||||
<li ng-show="link.type === 'dashboards' && link.asDropdown">
|
<input type="text" ng-model="link.url" class="gf-form-input" ng-model-onblur ng-change="updated()">
|
||||||
<input type="text" ng-model="link.title" class="input-medium tight-form-input" ng-model-onblur ng-change="updated()">
|
</div>
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'link'" style="width: 51px">Url</li>
|
<div class="gf-form">
|
||||||
<li ng-show="link.type === 'link'">
|
<button class="btn btn-inverse btn-mini" ng-click="moveLink($index, -1)" ng-hide="$first"><i class="fa fa-arrow-up"></i></button>
|
||||||
<input type="text" ng-model="link.url" class="input-xlarge tight-form-input" style="width: 302px;" ng-model-onblur ng-change="updated()">
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
<li class="tight-form-item" ng-show="link.type === 'link'">
|
<button class="btn btn-inverse btn-mini" ng-click="moveLink($index, 1)" ng-hide="$last"><i class="fa fa-arrow-down"></i></button>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<button class="btn btn-inverse btn-mini" ng-click="deleteLink($index)"><i class="fa fa-trash" ></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form" ng-show="link.type === 'dashboards' && link.asDropdown">
|
||||||
|
<span class="gf-form-label width-6">Title</span>
|
||||||
|
<input type="text" ng-model="link.title" class="gf-form-input max-width-25" ng-model-onblur ng-change="updated()">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-inline" ng-show="link.type === 'link'">
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-6">Title</span>
|
||||||
|
<input type="text" ng-model="link.title" class="gf-form-input max-width-10" ng-model-onblur ng-change="updated()">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-6">Tooltip</span>
|
||||||
|
<input type="text" ng-model="link.tooltip" class="gf-form-input max-width-10" placeholder="Open dashboard" ng-model-onblur ng-change="updated()">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-6">Icon</span>
|
||||||
|
<div class="gf-form-select-wrapper max-width-10">
|
||||||
|
<select class="gf-form-input" ng-model="link.icon" ng-options="k as k for (k, v) in iconMap" ng-change="updated()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-6">Include</span>
|
||||||
|
<editor-checkbox text="Time range" model="link.keepTime" change="updated()"></editor-checkbox>
|
||||||
|
<editor-checkbox text="Variable values" model="link.includeVars" change="updated()"></editor-checkbox>
|
||||||
<editor-checkbox text="Open in new tab " model="link.targetBlank" change="updated()"></editor-checkbox>
|
<editor-checkbox text="Open in new tab " model="link.targetBlank" change="updated()"></editor-checkbox>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</div>
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form" ng-if="link.type === 'link'">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 20px">
|
|
||||||
<i class="fa fa-fw fa-unlink invisible"></i>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'link'" style="width: 31px">Title</li>
|
|
||||||
<li ng-show="link.type === 'link'">
|
|
||||||
<input type="text" ng-model="link.title" class="input-medium tight-form-input" ng-model-onblur ng-change="updated()">
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'link'" style="width: 51px">Tooltip</li>
|
|
||||||
<li ng-show="link.type === 'link'">
|
|
||||||
<input type="text" ng-model="link.tooltip" class="input-medium tight-form-input" style="width: 151px" placeholder="Open dashboard" ng-model-onblur ng-change="updated()">
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'link'">Icon</li>
|
|
||||||
<li ng-show="link.type === 'link'">
|
|
||||||
<select class="input-medium tight-form-input" style="width: 110px;" ng-model="link.icon" ng-options="k as k for (k, v) in iconMap" ng-change="updated()"></select>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 20px">
|
|
||||||
<i class="fa fa-fw fa-unlink invisible"></i>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item">
|
|
||||||
<editor-checkbox text="Keep current time range" model="link.keepTime" change="updated()"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item">
|
|
||||||
<editor-checkbox text="Add current variable values" model="link.includeVars" change="updated()"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-row">
|
|
||||||
<br>
|
|
||||||
<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
|
||||||
|
@ -15,6 +15,12 @@
|
|||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label width-7">Name</span>
|
<span class="gf-form-label width-7">Name</span>
|
||||||
<input class="gf-form-input max-width-21" type="text" ng-model="current.name" placeholder="My data source name" required>
|
<input class="gf-form-input max-width-21" type="text" ng-model="current.name" placeholder="My data source name" required>
|
||||||
|
<gf-popover offset="0px -95px">
|
||||||
|
The name is used when you select the data source in panels.
|
||||||
|
The <code>Default</code> data source is preselected in new
|
||||||
|
panels.
|
||||||
|
</gf-popover>
|
||||||
|
|
||||||
<editor-checkbox text="Default" model="current.isDefault"></editor-checkbox>
|
<editor-checkbox text="Default" model="current.isDefault"></editor-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -24,6 +30,7 @@
|
|||||||
<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="k as v.name for (k, v) in types" ng-change="typeChanged()"></select>
|
<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="k as v.name for (k, v) in types" ng-change="typeChanged()"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<rebuild-on-change property="datasourceMeta.id">
|
<rebuild-on-change property="datasourceMeta.id">
|
||||||
|
@ -6,13 +6,27 @@
|
|||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label width-7">Url</span>
|
<span class="gf-form-label width-7">Url</span>
|
||||||
<input class="gf-form-input max-width-21" type="text" ng-model='current.url' placeholder="http://my.server.com:8080" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
|
<input class="gf-form-input max-width-21" type="text" ng-model='current.url' placeholder="http://my.server.com:8080" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
|
||||||
|
|
||||||
|
<gf-popover>
|
||||||
|
<p>Specify a complete HTTP url (http://your_server:8080)</p>
|
||||||
|
<span ng-show="current.access === 'direct'">
|
||||||
|
Your access method is <code>Direct</code>, this means the url
|
||||||
|
needs to be accessable from the browser.
|
||||||
|
</span>
|
||||||
|
<span ng-show="current.access === 'proxy'">
|
||||||
|
Your access method is currently <code>Proxy</code>, this means the url
|
||||||
|
needs to be accessable from the grafana backend.
|
||||||
|
</span>
|
||||||
|
</gf-popover>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label width-7">
|
<span class="gf-form-label width-7">
|
||||||
Access <tip>Direct = url is used directly from browser, Proxy = Grafana backend will proxy the request</tip>
|
Access <tip>Direct = url is used directly from browser, Proxy = Grafana backend will proxy the request</tip>
|
||||||
</span>
|
</span>
|
||||||
<select class="gf-form-input gf-size-auto" ng-model="current.access" ng-options="f for f in ['direct', 'proxy']"></select>
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input gf-size-auto" ng-model="current.access" ng-options="f for f in ['direct', 'proxy']"></select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
<table class="filter-table" ng-if="ctrl.datasources.length > 0">
|
<table class="filter-table" ng-if="ctrl.datasources.length > 0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><strong>Name</strong></th>
|
<th><strong>name</strong></th>
|
||||||
<th><strong>Url</strong></th>
|
<th><strong>url</strong></th>
|
||||||
<th style="width: 60px;"></th>
|
<th style="width: 60px;"></th>
|
||||||
<th style="width: 85px;"></th>
|
<th style="width: 85px;"></th>
|
||||||
<th style="width: 44px;"></th>
|
<th style="width: 44px;"></th>
|
||||||
|
@ -10,23 +10,16 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-box-body" style="min-height: 0px;">
|
<div class="gf-box-body">
|
||||||
|
|
||||||
<div class="tight-form last">
|
<div class="gf-form-group">
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form">
|
||||||
<li class="tight-form-item">
|
<span class="gf-form-label">Key</span>
|
||||||
<strong>Key</strong>
|
<span class="gf-form-label">{{key}}</span>
|
||||||
</li>
|
</div>
|
||||||
<li class="tight-form-item last">
|
|
||||||
{{key}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div class="grafana-info-box" style="text-align: left">
|
<div class="grafana-info-box" style="border: 0;">
|
||||||
You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now.
|
You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
@ -6,24 +6,16 @@
|
|||||||
|
|
||||||
<h2 style="margin-top: 30px;">Add Organization</h2>
|
<h2 style="margin-top: 30px;">Add Organization</h2>
|
||||||
|
|
||||||
<form name="form">
|
<form name="form" class="gf-form-group">
|
||||||
<div class="tight-form last">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-10">Org. name</span>
|
||||||
<li class="tight-form-item" style="width: 100px;">
|
<input type="text" ng-model="newOrg.name" required class="gf-form-input" placeholder="organization name">
|
||||||
<strong>Org. name</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" ng-model="newOrg.name" required class="input-xxlarge tight-form-input last" placeholder="organization name">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<button class="btn btn-success pull-right" ng-click="createOrg()">Create</button>
|
<div class="gf-form-buttons-row">
|
||||||
|
<button class="btn btn-success pull-right" ng-click="createOrg()">Create</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<navbar icon="icon-gf icon-gf-users" title="Organization">
|
<navbar icon="icon-gf icon-gf-users" title="Organization" title-url="org">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<navbar icon="icon-gf icon-gf-users" title="Organization Users" title-url="org">
|
<navbar icon="icon-gf icon-gf-users" title="Organization Users" title-url="org/users">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
|
@ -28,24 +28,27 @@ var panelTemplate = `
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-full-edit" ng-if="ctrl.editMode">
|
<div class="panel-full-edit" ng-if="ctrl.editMode">
|
||||||
<div class="gf-box">
|
<div class="tabbed-view tabbed-view--panel-edit">
|
||||||
<div class="gf-box-header">
|
<div class="tabbed-view-header">
|
||||||
<div class="gf-box-title">
|
<h2 class="tabbed-view-title">
|
||||||
<i ng-class="ctrl.icon"></i>
|
<i ng-class="ctrl.icon"></i>
|
||||||
{{ctrl.pluginName}}
|
{{ctrl.pluginName}}
|
||||||
</div>
|
</h2>
|
||||||
|
|
||||||
<div ng-model="ctrl.editorTabIndex" bs-tabs>
|
<ul class="gf-tabs">
|
||||||
<div ng-repeat="tab in ctrl.editorTabs" data-title="{{tab.title}}">
|
<li class="gf-tabs-item" ng-repeat="tab in ::ctrl.editorTabs">
|
||||||
</div>
|
<a class="gf-tabs-link" ng-click="ctrl.editorTabIndex = $index" ng-class="{active: ctrl.editorTabIndex === $index}">
|
||||||
</div>
|
{{::tab.title}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<button class="gf-box-header-close-btn" ng-click="ctrl.exitFullscreen();">
|
<button class="tabbed-view-close-btn" ng-click="ctrl.exitFullscreen();">
|
||||||
Back to dashboard
|
<i class="fa fa-remove"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-box-body">
|
<div class="tabbed-view-body">
|
||||||
<div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index">
|
<div ng-repeat="tab in ctrl.editorTabs" ng-if="ctrl.editorTabIndex === $index">
|
||||||
<panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab>
|
<panel-editor-tab editor-tab="tab" ctrl="ctrl" index="$index"></panel-editor-tab>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,59 +1,34 @@
|
|||||||
<div class="editor-row">
|
<div class="gf-form-group">
|
||||||
<div class="section tight-form-container" style="margin-bottom: 20px">
|
<div class="gf-form">
|
||||||
<div class="tight-form">
|
<span class="gf-form-label">
|
||||||
<ul class="tight-form-list">
|
<i class="fa fa-clock-o"></i>
|
||||||
<li class="tight-form-item tight-form-item-icon">
|
</span>
|
||||||
<i class="fa fa-clock-o"></i>
|
|
||||||
</li>
|
<span class="gf-form-label width-12">Override relative time</span>
|
||||||
<li class="tight-form-item" style="width: 178px">
|
<span class="gf-form-label width-6">Last</span>
|
||||||
<strong>Override relative time</strong>
|
|
||||||
</li>
|
<input type="text" class="gf-form-input max-width-8" placeholder="1h"
|
||||||
<li class="tight-form-item" style="width: 50px">
|
empty-to-null ng-model="ctrl.panel.timeFrom" valid-time-span
|
||||||
Last
|
ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
</li>
|
</div>
|
||||||
<li>
|
|
||||||
<input type="text" class="input-small tight-form-input last" placeholder="1h"
|
<div class="gf-form">
|
||||||
empty-to-null ng-model="ctrl.panel.timeFrom" valid-time-span
|
<span class="gf-form-label">
|
||||||
ng-change="ctrl.refresh()" ng-model-onblur>
|
<i class="fa fa-clock-o"></i>
|
||||||
</li>
|
</span>
|
||||||
</ul>
|
<span class="gf-form-label width-12">Add time shift</span>
|
||||||
<div class="clearfix"></div>
|
<span class="gf-form-label width-6">Amount</span>
|
||||||
</div>
|
<input type="text" class="gf-form-input max-width-8" placeholder="1h"
|
||||||
<div class="tight-form">
|
empty-to-null ng-model="ctrl.panel.timeShift" valid-time-span
|
||||||
<ul class="tight-form-list">
|
ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
<li class="tight-form-item tight-form-item-icon">
|
</div>
|
||||||
<i class="fa fa-clock-o"></i>
|
|
||||||
</li>
|
<div class="gf-form">
|
||||||
<li class="tight-form-item" style="width: 178px">
|
<span class="gf-form-label">
|
||||||
<strong>Add time shift</strong>
|
<i class="fa fa-clock-o"></i>
|
||||||
</li>
|
</span>
|
||||||
<li class="tight-form-item" style="width: 50px">
|
<editor-checkbox text="Hide time override info" model="ctrl.panel.hideTimeOverride" change="ctrl.refresh()"></editor-checkbox>
|
||||||
Amount
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" class="input-small tight-form-input last" placeholder="1h"
|
|
||||||
empty-to-null ng-model="ctrl.panel.timeShift" valid-time-span
|
|
||||||
ng-change="ctrl.refresh()" ng-model-onblur>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item tight-form-item-icon">
|
|
||||||
<i class="fa fa-clock-o"></i>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" style="width: 178px">
|
|
||||||
<strong>Hide time override info</strong>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<input class="cr1" id="ctrl.panel.hideTimeOverride" type="checkbox"
|
|
||||||
ng-model="ctrl.panel.hideTimeOverride" ng-checked="ctrl.panel.hideTimeOverride" ng-change="ctrl.refresh()">
|
|
||||||
<label for="ctrl.panel.hideTimeOverride" class="cr1"></label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,84 +1,67 @@
|
|||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<div class="section">
|
<h5 class="section-heading">
|
||||||
<h5>Drilldown / detail link<tip>These links appear in the dropdown menu in the panel menu. </tip></h5>
|
Drilldown / detail link<tip>These links appear in the dropdown menu in the panel menu. </tip></h5>
|
||||||
|
</h5>
|
||||||
|
|
||||||
<div ng-repeat="link in panel.links" style="margin-top: 20px;">
|
<div ng-repeat="link in panel.links" style="margin-top: 20px;">
|
||||||
<div class="tight-form">
|
<div class="gf-form-group">
|
||||||
<ul class="tight-form-list pull-right">
|
|
||||||
<li class="tight-form-item">
|
|
||||||
<i ng-click="moveLink($index, -1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i>
|
|
||||||
<i ng-click="moveLink($index, 1)" ng-hide="$last" class="pointer fa fa-fw fa-arrow-down"></i>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<i class="fa fa-remove pointer" ng-click="deleteLink(link)"></i>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form-inline">
|
||||||
<li class="tight-form-item" style="width: 20px">
|
<div class="gf-form width-2">
|
||||||
<i class="fa fa-fw fa-unlink"></i>
|
<i class="fa fa-fw fa-unlink"></i>
|
||||||
</li>
|
</div>
|
||||||
|
|
||||||
<li class="tight-form-item">Type</li>
|
<div class="gf-form">
|
||||||
<li>
|
<span class="gf-form-label width-7">Type</span>
|
||||||
<select class="input-medium tight-form-input" style="width: 150px;" ng-model="link.type" ng-options="f for f in ['dashboard','absolute']"></select>
|
<div class="gf-form-select-wrapper width-14">
|
||||||
</li>
|
<select class="gf-form-input" ng-model="link.type" ng-options="f for f in ['dashboard','absolute']"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'dashboard'" style="width: 73px;">Dashboard</li>
|
<div class="gf-form">
|
||||||
<li ng-show="link.type === 'dashboard'">
|
<span class="gf-form-label width-7" ng-show="link.type === 'dashboard'">Dashboard</span>
|
||||||
<input type="text" ng-model="link.dashboard" bs-typeahead="searchDashboards" class="input-large tight-form-input" ng-blur="dashboardChanged(link)">
|
<input ng-show="link.type === 'dashboard'" type="text" ng-model="link.dashboard" bs-typeahead="searchDashboards" class="gf-form-input max-width-14" ng-blur="dashboardChanged(link)">
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="tight-form-item" ng-show="link.type === 'absolute'" style="width: 73px;">Url</li>
|
<span class="gf-form-label width-7" ng-show="link.type === 'absolute'">Url</span>
|
||||||
<li ng-show="link.type === 'absolute'">
|
<input ng-show="link.type === 'absolute'" type="text" ng-model="link.url" class="gf-form-input max-width-14">
|
||||||
<input type="text" ng-model="link.url" class="input-large tight-form-input">
|
</div>
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
<div class="gf-form">
|
||||||
<div class="clearfix"></div>
|
<button class="btn-inverse gf-form-btn btn-small" ng-click="deleteLink(link)"><i class="fa fa-trash"></i></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tight-form">
|
<div class="gf-form-inline">
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form width-2">
|
||||||
<li class="tight-form-item" style="width: 20px">
|
<i class="fa fa-fw fa-unlink invisible"></i>
|
||||||
<i class="fa fa-fw fa-unlink invisible"></i>
|
</div>
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" style="width: 31px">Title</li>
|
<div class="gf-form">
|
||||||
<li>
|
<div class="gf-form-label width-7">Title</div>
|
||||||
<input type="text" ng-model="link.title" class="input-medium tight-form-input">
|
<input type="text" ng-model="link.title" class="gf-form-input">
|
||||||
</li>
|
</div>
|
||||||
<li class="tight-form-item" style="width: 73px;">
|
|
||||||
Url params
|
<div class="gf-form">
|
||||||
</li>
|
<span class="gf-form-label width-7">Url params</span>
|
||||||
<li>
|
<input type="text" ng-model="link.params" class="gf-form-input">
|
||||||
<input type="text" ng-model="link.params" class="input-large tight-form-input">
|
</div>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form-inline">
|
||||||
<li class="tight-form-item" style="width: 20px">
|
<div class="gf-form width-2">
|
||||||
<i class="fa fa-fw fa-unlink invisible"></i>
|
<i class="fa fa-fw fa-unlink invisible"></i>
|
||||||
</li>
|
</div>
|
||||||
<li class="tight-form-item">
|
|
||||||
<editor-checkbox text="Keep current time range" model="link.keepTime"></editor-checkbox>
|
<div class="gf-form">
|
||||||
</li>
|
<editor-checkbox text="Keep current time range" model="link.keepTime"></editor-checkbox>
|
||||||
<li class="tight-form-item">
|
<editor-checkbox text="Add current variable values" model="link.includeVars"></editor-checkbox>
|
||||||
<editor-checkbox text="Add current variable values" model="link.includeVars"></editor-checkbox>
|
<editor-checkbox text="Open in new tab " model="link.targetBlank"></editor-checkbox>
|
||||||
</li>
|
</div>
|
||||||
<li class="tight-form-item last">
|
|
||||||
<editor-checkbox text="Open in new tab " model="link.targetBlank"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<br>
|
|
||||||
<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
|
<button class="btn btn-inverse" ng-click="addLink()"><i class="fa fa-plus"></i> Add link</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,6 +52,5 @@ function (angular, _) {
|
|||||||
$scope.deleteLink = function(link) {
|
$scope.deleteLink = function(link) {
|
||||||
$scope.panel.links = _.without($scope.panel.links, link);
|
$scope.panel.links = _.without($scope.panel.links, link);
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<navbar icon="fa fa-fw fa-list" title="Playlist">
|
<navbar icon="fa fa-fw fa-list" title="Playlists" title-url="playlists">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container" ng-form="playlistEditForm">
|
<div class="page-container" ng-form="playlistEditForm">
|
||||||
@ -7,6 +7,8 @@
|
|||||||
<h1 ng-show="!ctrl.isNew()">Edit Playlist</h1>
|
<h1 ng-show="!ctrl.isNew()">Edit Playlist</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p class="playlist-description">A playlist rotates through a pre-selected list of Dashboards. A Playlist can be a great way to build situational awareness, or just show off your metrics to your team or visitors.</p>
|
||||||
|
|
||||||
<div class="gf-form-group">
|
<div class="gf-form-group">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<span class="gf-form-label width-7">Name</span>
|
<span class="gf-form-label width-7">Name</span>
|
||||||
@ -19,24 +21,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-group">
|
<div class="gf-form-group">
|
||||||
<div class="max-width-28">
|
<h3 class="page-headering">Dashboards</h3>
|
||||||
<h5 class="page-headering">Add dashboards</h5>
|
|
||||||
<div style="">
|
|
||||||
<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h5>Search results ({{ctrl.filteredDashboards.length + ctrl.filteredTags.length}})</h5>
|
<div class="playlist-search-containerwrapper">
|
||||||
|
<div class="max-width-32">
|
||||||
|
<h5 class="page-headering playlist-column-header">Available</h5>
|
||||||
|
<div style="">
|
||||||
|
<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div ng-if="ctrl.filteredDashboards.length > 0">
|
<div ng-if="ctrl.filteredDashboards.length > 0">
|
||||||
<table class="grafana-options-table">
|
<table class="grafana-options-table playlist-available-list">
|
||||||
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
|
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
|
||||||
<td style="white-space: nowrap;">
|
<td>
|
||||||
{{playlistItem.title}}
|
<i class="icon-gf icon-gf-dashboard"></i>
|
||||||
|
{{playlistItem.title}}
|
||||||
|
<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: center">
|
<td class="add-dashboard">
|
||||||
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
|
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
|
||||||
<i class="fa fa-plus"></i>
|
<i class="fa fa-plus"></i>
|
||||||
Add to playlist
|
Add to playlist
|
||||||
@ -46,31 +53,40 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
|
<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
|
||||||
<div ng-repeat="tag in ctrl.filteredTags" class="pointer" style="width: 180px; float: left;"
|
<table class="grafana-options-table playlist-available-list">
|
||||||
ng-click="ctrl.addTagPlaylistItem(tag, $event)">
|
<tr ng-repeat="tag in ctrl.filteredTags">
|
||||||
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
|
<td>
|
||||||
<i class="fa fa-tag"></i>
|
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
|
||||||
<span>{{tag.term}} ({{tag.count}})</span>
|
<i class="fa fa-tag"></i>
|
||||||
</a>
|
<span>{{tag.term}} ({{tag.count}})</span>
|
||||||
</div>
|
</a>
|
||||||
</div>
|
</td>
|
||||||
|
<td class="add-dashboard">
|
||||||
|
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add to playlist
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h5>Added dashboards</h5>
|
<h5 class="page headering playlist-column-header">Selected</h5>
|
||||||
<table class="grafana-options-table">
|
<table class="grafana-options-table playlist-available-list">
|
||||||
<tr ng-repeat="playlistItem in ctrl.playlistItems">
|
<tr ng-repeat="playlistItem in ctrl.playlistItems">
|
||||||
<td style="white-space: nowrap;" ng-if="playlistItem.type === 'dashboard_by_id'">
|
<td ng-if="playlistItem.type === 'dashboard_by_id'">
|
||||||
{{playlistItem.title}}
|
<i class="icon-gf icon-gf-dashboard"></i> {{playlistItem.title}}
|
||||||
</td>
|
</td>
|
||||||
<td style="white-space: nowrap;" ng-if="playlistItem.type === 'dashboard_by_tag'">
|
<td ng-if="playlistItem.type === 'dashboard_by_tag'">
|
||||||
<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
|
<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
|
||||||
<i class="fa fa-tag"></i>
|
<i class="fa fa-tag"></i>
|
||||||
<span>{{playlistItem.title}}</span>
|
<span>{{playlistItem.title}}</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td style="text-align: right">
|
<td class="selected-playlistitem-settings">
|
||||||
<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
|
<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
|
||||||
<i class="fa fa-arrow-up"></i>
|
<i class="fa fa-arrow-up"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -89,7 +105,10 @@
|
|||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<div class="gf-form-button-row">
|
<div class="gf-form-button-row">
|
||||||
<a class="btn btn-success"
|
<a class="btn btn-success " ng-show="ctrl.isNew()"
|
||||||
|
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
|
||||||
|
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create new playlist</a>
|
||||||
|
<a class="btn btn-success" ng-show="!ctrl.isNew()"
|
||||||
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
|
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
|
||||||
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
|
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
|
||||||
<a class="btn-text" ng-click="ctrl.backToList()">Cancel</a>
|
<a class="btn-text" ng-click="ctrl.backToList()">Cancel</a>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<navbar icon="fa fa-fw fa-list" title="Playlists">
|
<navbar icon="fa fa-fw fa-list" title="Playlists" title-url="playlists">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
|
@ -11,7 +11,7 @@ export class PlaylistEditCtrl {
|
|||||||
searchQuery: string = '';
|
searchQuery: string = '';
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
playlist: any = {
|
playlist: any = {
|
||||||
interval: '10m',
|
interval: '5m',
|
||||||
};
|
};
|
||||||
playlistItems: any = [];
|
playlistItems: any = [];
|
||||||
dashboardresult: any = [];
|
dashboardresult: any = [];
|
||||||
|
51
public/app/features/plugins/edit_ctrl.ts
Normal file
51
public/app/features/plugins/edit_ctrl.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export class PluginEditCtrl {
|
||||||
|
model: any;
|
||||||
|
pluginId: any;
|
||||||
|
includedPanels: any;
|
||||||
|
includedDatasources: any;
|
||||||
|
tabIndex: number;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor(private backendSrv: any, private $routeParams: any) {
|
||||||
|
this.model = {};
|
||||||
|
this.pluginId = $routeParams.pluginId;
|
||||||
|
this.tabIndex = 0;
|
||||||
|
|
||||||
|
this.backendSrv.get(`/api/org/plugins/${this.pluginId}/settings`).then(result => {
|
||||||
|
this.model = result;
|
||||||
|
this.includedPanels = _.where(result.includes, {type: 'panel'});
|
||||||
|
this.includedDatasources = _.where(result.includes, {type: 'datasource'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
var updateCmd = _.extend({
|
||||||
|
pluginId: this.model.pluginId,
|
||||||
|
orgId: this.model.orgId,
|
||||||
|
enabled: this.model.enabled,
|
||||||
|
pinned: this.model.pinned,
|
||||||
|
jsonData: this.model.jsonData,
|
||||||
|
secureJsonData: this.model.secureJsonData,
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
this.backendSrv.post(`/api/org/plugins/${this.pluginId}/settings`, updateCmd).then(function() {
|
||||||
|
window.location.href = window.location.href;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleEnabled() {
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePinned() {
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('grafana.controllers').controller('PluginEditCtrl', PluginEditCtrl);
|
||||||
|
|
17
public/app/features/plugins/list_ctrl.ts
Normal file
17
public/app/features/plugins/list_ctrl.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
|
|
||||||
|
export class PluginListCtrl {
|
||||||
|
plugins: any[];
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor(private backendSrv: any) {
|
||||||
|
|
||||||
|
this.backendSrv.get('api/org/plugins').then(plugins => {
|
||||||
|
this.plugins = plugins;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('grafana.controllers').controller('PluginListCtrl', PluginListCtrl);
|
185
public/app/features/plugins/partials/edit.html
Normal file
185
public/app/features/plugins/partials/edit.html
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<navbar title="Plugins" title-url="plugins" icon="icon-gf icon-gf-apps">
|
||||||
|
<a href="plugins/apps" class="navbar-page-btn">
|
||||||
|
<i class="fa fa-chevron-right"></i>
|
||||||
|
Apps
|
||||||
|
</a>
|
||||||
|
</navbar>
|
||||||
|
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="plugin-header">
|
||||||
|
<span ng-show="ctrl.model.info.logos.large" class="plugin-header-logo">
|
||||||
|
<img src="{{ctrl.model.info.logos.large}}">
|
||||||
|
</span>
|
||||||
|
<div class="plugin-header-info-block">
|
||||||
|
<h1 class="plugin-header-name">{{ctrl.model.name}}</h1>
|
||||||
|
<div class="plugin-header-author">By {{ctrl.model.info.author.name}}</div>
|
||||||
|
<div class="plugin-header-stamps">
|
||||||
|
<span class="plugin-header-stamps-type">
|
||||||
|
<i class="icon-gf icon-gf-apps"></i> {{ctrl.model.type}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs nav-tabs-alt">
|
||||||
|
<li ng-repeat="tab in ::['Overview', 'Details', 'Config']" ng-class="{active: ctrl.tabIndex === $index}">
|
||||||
|
<a ng-click="ctrl.tabIndex= $index">
|
||||||
|
{{::tab}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="page-body">
|
||||||
|
<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabIndex === 0">
|
||||||
|
README.md
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabIndex === 1">
|
||||||
|
Details
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-content page-content-with-sidebar" ng-if="ctrl.tabIndex === 2">
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<editor-checkbox text="Enabled" model="ctrl.model.enabled" change="ctrl.toggleEnabled()"></editor-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<editor-checkbox text="Pinned" model="ctrl.model.pinned" change="ctrl.togglePinned()"></editor-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="ctrl.model.pluginId">
|
||||||
|
<plugin-component type="app-config-ctrl"></plugin-component>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="page-sidebar">
|
||||||
|
<section class="page-sidebar-section">
|
||||||
|
<h4>Version</h4>
|
||||||
|
<span>1.0.1</span>
|
||||||
|
</section>
|
||||||
|
<section class="page-sidebar-section" ng-show="ctrl.model.type === 'app'">
|
||||||
|
<h5>Includes</h4>
|
||||||
|
<ul class="ui-list">
|
||||||
|
<li ng-show="!ctrl.includedPanels.length"><em>None</em></li>
|
||||||
|
<li ng-repeat="panel in ctrl.includedPanels">
|
||||||
|
{{panel.name}}
|
||||||
|
</li>
|
||||||
|
<li ng-repeat="ds in ctrl.includedDatasources">
|
||||||
|
{{ds.name}}
|
||||||
|
</li>
|
||||||
|
<li ng-repeat="page in ctrl.model.pages">
|
||||||
|
<a href="apps/{{ctrl.appId}}/page/{{page.slug}}" class="external-link">{{page.name}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<section class="page-sidebar-section">
|
||||||
|
<h5>Dependencies</h4>
|
||||||
|
<span>TODO</span>
|
||||||
|
</section>
|
||||||
|
<section class="page-sidebar-section">
|
||||||
|
<h5>Links</h4>
|
||||||
|
<ul class="ui-list">
|
||||||
|
<li ng-repeat="link in ctrl.model.info.links">
|
||||||
|
<a href="{{link.url}}" class="external-link" target="_blank">{{link.name}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="app-edit-description"> -->
|
||||||
|
<!-- {{ctrl.model.info.description}}<br> -->
|
||||||
|
<!-- <span style="small"> -->
|
||||||
|
<!-- Version: {{ctrl.model.info.version}} &nbsp; &nbsp; Updated: {{ctrl.model.info.updated}} -->
|
||||||
|
<!-- </span> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <div class="flex-column"> -->
|
||||||
|
<!-- <ul class="app-edit-links"> -->
|
||||||
|
<!-- <li> -->
|
||||||
|
<!-- By <a href="{{ctrl.model.info.author.url}}" class="external-link" target="_blank">{{ctrl.model.info.author.name}}</a> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- <li ng-repeat="link in ctrl.model.info.links"> -->
|
||||||
|
<!-- <a href="{{link.url}}" class="external-link" target="_blank">{{link.name}}</a> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- </ul> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
|
||||||
|
<!-- <section class="simple-box"> -->
|
||||||
|
<!-- <h3 class="simple-box-header">Included with app:</h3> -->
|
||||||
|
<!-- <div class="flex-container"> -->
|
||||||
|
<!-- <div class="simple-box-body simple-box-column"> -->
|
||||||
|
<!-- <div class="simple-box-column-header"> -->
|
||||||
|
<!-- <i class="fa fa-th-large"></i> -->
|
||||||
|
<!-- Dashboards -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <ul> -->
|
||||||
|
<!-- <li><em class="small">None</em></li> -->
|
||||||
|
<!-- </ul> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <div class="simple-box-body simple-box-column"> -->
|
||||||
|
<!-- <div class="simple-box-column-header"> -->
|
||||||
|
<!-- <i class="fa fa-line-chart"></i> -->
|
||||||
|
<!-- Panels -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <ul> -->
|
||||||
|
<!-- <li ng-show="!ctrl.includedPanels.length"><em class="small">None</em></li> -->
|
||||||
|
<!-- <li ng-repeat="panel in ctrl.includedPanels"> -->
|
||||||
|
<!-- {{panel.name}} -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- </ul> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <div class="simple-box-body simple-box-column"> -->
|
||||||
|
<!-- <div class="simple-box-column-header"> -->
|
||||||
|
<!-- <i class="fa fa-database"></i> -->
|
||||||
|
<!-- Datasources -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <ul> -->
|
||||||
|
<!-- <li ng-show="!ctrl.includedDatasources.length"><em class="small">None</em></li> -->
|
||||||
|
<!-- <li ng-repeat="ds in ctrl.includedDatasources"> -->
|
||||||
|
<!-- {{ds.name}} -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- </ul> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <div class="simple-box-body simple-box-column"> -->
|
||||||
|
<!-- <div class="simple-box-column-header"> -->
|
||||||
|
<!-- <i class="fa fa-files-o"></i> -->
|
||||||
|
<!-- Pages -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- <ul> -->
|
||||||
|
<!-- <li ng-repeat="page in ctrl.model.pages"> -->
|
||||||
|
<!-- <a href="apps/{{ctrl.appId}}/page/{{page.slug}}" class="external-link">{{page.name}}</a> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
<!-- </ul> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- </section> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- <section class="simple-box"> -->
|
||||||
|
<!-- <h3 class="simple-box-header">Dependencies:</h3> -->
|
||||||
|
<!-- <div class="simple-box-body"> -->
|
||||||
|
<!-- Grafana 2.6.x -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- </section> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- <section class="simple-box"> -->
|
||||||
|
<!-- <h3 class="simple-box-header">Configuration:</h3> -->
|
||||||
|
<!-- <div class="simple-box-body"> -->
|
||||||
|
<!-- <div ng-if="ctrl.model.appId"> -->
|
||||||
|
<!-- <plugin-component type="app-config-ctrl"></plugin-component> -->
|
||||||
|
<!-- <div class="clearfix"></div> -->
|
||||||
|
<!-- <button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<!-- </section> -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </div> -->
|
42
public/app/features/plugins/partials/list.html
Normal file
42
public/app/features/plugins/partials/list.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<navbar title="Plugins" icon="icon-gf icon-gf-apps" title-url="plugins">
|
||||||
|
</navbar>
|
||||||
|
|
||||||
|
<div class="page-container">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>Plugins</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="filter-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><strong>Name</strong></th>
|
||||||
|
<th><strong>Type</strong></th>
|
||||||
|
<th style="width: 60px;"></th>
|
||||||
|
<th style="width: 80px;"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="plugin in ctrl.plugins">
|
||||||
|
<td>
|
||||||
|
<a href="plugins/{{plugin.pluginId}}/edit">
|
||||||
|
{{plugin.name}}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{plugin.type}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="label label-info" ng-if="plugin.enabled">Enabled</span>
|
||||||
|
<span class="label label-info" ng-if="plugin.pinned">Pinned</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="plugins/{{plugin.pluginId}}/edit" class="btn btn-inverse btn-small">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
<navbar icon="icon-gf icon-gf-users" title="Profile">
|
<navbar icon="icon-gf icon-gf-users" title="Profile" title-url="profile">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
<navbar icon="icon-gf icon-gf-snapshot" title="Snapshots">
|
<navbar icon="icon-gf icon-gf-snapshot" title="Snapshots" title-url="dashboard/snapshots">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-wide">
|
<div class="page-header">
|
||||||
|
|
||||||
<h1>Available snapshots</h1>
|
<h1>Available snapshots</h1>
|
||||||
|
|
||||||
<table class="filter-table" style="margin-top: 20px">
|
|
||||||
<thead>
|
|
||||||
<th><strong>Name</strong></th>
|
|
||||||
<th><strong>Snapshot url</strong></th>
|
|
||||||
<th style="width: 70px"></th>
|
|
||||||
<th style="width: 25px"></th>
|
|
||||||
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tr ng-repeat="snapshot in ctrl.snapshots">
|
|
||||||
<td>
|
|
||||||
<a href="dashboard/snapshot/{{snapshot.key}}">{{snapshot.name}}</a>
|
|
||||||
</td>
|
|
||||||
<td >
|
|
||||||
<a href="dashboard/snapshot/{{snapshot.key}}">dashboard/snapshot/{{snapshot.key}}</a>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href="dashboard/snapshot/{{snapshot.key}}" class="btn btn-inverse btn-mini">
|
|
||||||
<i class="fa fa-eye"></i>
|
|
||||||
View
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<a ng-click="ctrl.removeSnapshot(snapshot)" class="btn btn-danger btn-mini">
|
|
||||||
<i class="fa fa-remove"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<table class="filter-table" style="margin-top: 20px">
|
||||||
|
<thead>
|
||||||
|
<th><strong>Name</strong></th>
|
||||||
|
<th><strong>Snapshot url</strong></th>
|
||||||
|
<th style="width: 70px"></th>
|
||||||
|
<th style="width: 25px"></th>
|
||||||
|
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tr ng-repeat="snapshot in ctrl.snapshots">
|
||||||
|
<td>
|
||||||
|
<a href="dashboard/snapshot/{{snapshot.key}}">{{snapshot.name}}</a>
|
||||||
|
</td>
|
||||||
|
<td >
|
||||||
|
<a href="dashboard/snapshot/{{snapshot.key}}">dashboard/snapshot/{{snapshot.key}}</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="dashboard/snapshot/{{snapshot.key}}" class="btn btn-inverse btn-mini">
|
||||||
|
<i class="fa fa-eye"></i>
|
||||||
|
View
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a ng-click="ctrl.removeSnapshot(snapshot)" class="btn btn-danger btn-mini">
|
||||||
|
<i class="fa fa-remove"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<navbar icon="fa fa-fw fa-adjust" title="Style Guide">
|
<navbar icon="fa fa-fw fa-adjust" title="Style Guide" title-url="styleguide">
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
|
@ -1,332 +1,218 @@
|
|||||||
<div ng-controller="TemplateEditorCtrl" ng-init="init()">
|
<div ng-controller="TemplateEditorCtrl" ng-init="init()">
|
||||||
<div class="gf-box-header">
|
<div class="tabbed-view-header">
|
||||||
<div class="gf-box-title">
|
<h2 class="tabbed-view-title">
|
||||||
<i class="fa fa-code"></i>
|
|
||||||
Templating
|
Templating
|
||||||
</div>
|
</h2>
|
||||||
|
|
||||||
<div class="tabs">
|
<ul class="gf-tabs">
|
||||||
<ul class="nav nav-tabs">
|
<li class="gf-tabs-item" >
|
||||||
<li ng-class="{active: mode === 'list'}">
|
<a class="gf-tabs-link" ng-click="mode = 'list';" ng-class="{active: mode === 'list'}">
|
||||||
<a ng-click="mode = 'list';">
|
Variables
|
||||||
Variables
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
<li class="gf-tabs-item" ng-show="mode === 'edit'">
|
||||||
|
<a class="gf-tabs-link" ng-class="{active: mode === 'edit'}">
|
||||||
|
{{current.name}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="gf-tabs-item" ng-show="mode === 'new'">
|
||||||
|
<span class="active gf-tabs-link">New</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<li ng-class="{active: mode === 'edit'}" ng-show="mode === 'edit'">
|
<button class="tabbed-view-close-btn" ng-click="dismiss();dashboard.refresh();">
|
||||||
<a>
|
|
||||||
{{current.name}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li ng-class="{active: mode === 'new'}">
|
|
||||||
<a ng-click="mode = 'new';">
|
|
||||||
<i class="fa fa-plus"></i>
|
|
||||||
New
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="gf-box-header-close-btn" ng-click="dismiss();dashboard.refresh();">
|
|
||||||
<i class="fa fa-remove"></i>
|
<i class="fa fa-remove"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-box-body">
|
<div class="tabbed-view-body">
|
||||||
|
|
||||||
<div ng-if="mode === 'list'">
|
<div ng-if="mode === 'list'">
|
||||||
|
<div ng-if="variables.length === 0">
|
||||||
|
<em>No template variables defined</em>
|
||||||
|
</div>
|
||||||
|
<table class="grafana-options-table">
|
||||||
|
<tr ng-repeat="variable in variables">
|
||||||
|
<td style="width: 1%">
|
||||||
|
<span class="template-variable">
|
||||||
|
${{variable.name}}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="max-width" style="max-width: 200px;">
|
||||||
|
{{variable.query}}
|
||||||
|
</td>
|
||||||
|
|
||||||
<div class="editor-row row">
|
<td style="width: 1%"><i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
|
||||||
<div style="max-width: 1024px">
|
<td style="width: 1%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
|
||||||
<div ng-if="variables.length === 0">
|
<td style="width: 1%">
|
||||||
<em>No template variables defined</em>
|
<a ng-click="duplicate(variable)" class="btn btn-inverse btn-mini">
|
||||||
</div>
|
Duplicate
|
||||||
<table class="grafana-options-table">
|
</a>
|
||||||
<tr ng-repeat="variable in variables">
|
</td>
|
||||||
<td style="width: 1%">
|
<td style="width: 1%">
|
||||||
<span class="template-variable">
|
<a ng-click="edit(variable)" class="btn btn-inverse btn-mini">
|
||||||
${{variable.name}}
|
<i class="fa fa-edit"></i>
|
||||||
</span>
|
Edit
|
||||||
</td>
|
</a>
|
||||||
<td class="max-width" style="max-width: 200px;">
|
</td>
|
||||||
{{variable.query}}
|
<td style="width: 1%">
|
||||||
</td>
|
<a ng-click="removeVariable(variable)" class="btn btn-danger btn-mini">
|
||||||
<td style="width: 1%">
|
<i class="fa fa-remove"></i>
|
||||||
<a ng-click="edit(variable)" class="btn btn-inverse btn-small">
|
</a>
|
||||||
<i class="fa fa-edit"></i>
|
</td>
|
||||||
Edit
|
</tr>
|
||||||
</a>
|
</table>
|
||||||
</td>
|
</div>
|
||||||
<td style="width: 1%">
|
|
||||||
<a ng-click="duplicate(variable)" class="btn btn-inverse btn-small">
|
<div class="gf-form" ng-show="mode === 'list'">
|
||||||
Duplicate
|
<div class="gf-form-button-row">
|
||||||
</a>
|
<a type="button" class="btn gf-form-button btn-success" ng-click="mode = 'new';"><i class="fa fa-plus" ></i> New</a>
|
||||||
</td>
|
|
||||||
<td style="width: 1%"><i ng-click="_.move(variables,$index,$index-1)" ng-hide="$first" class="pointer fa fa-arrow-up"></i></td>
|
|
||||||
<td style="width: 1%"><i ng-click="_.move(variables,$index,$index+1)" ng-hide="$last" class="pointer fa fa-arrow-down"></i></td>
|
|
||||||
<td style="width: 1%">
|
|
||||||
<a ng-click="removeVariable(variable)" class="btn btn-danger btn-small">
|
|
||||||
<i class="fa fa-remove"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="mode === 'edit' || mode === 'new'">
|
<div ng-if="mode === 'edit' || mode === 'new'">
|
||||||
<div class="editor-row">
|
<h5 class="section-heading">Variable</h5>
|
||||||
<div class="tight-form-section">
|
<div class="gf-form-group">
|
||||||
<h5>Variable</h5>
|
<div class="gf-form-inline">
|
||||||
<div class="tight-form last">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-7">Name</span>
|
||||||
<li class="tight-form-item" style="width: 100px">
|
<input type="text" class="gf-form-input max-width-14" placeholder="name" ng-model='current.name'></input>
|
||||||
Name
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
<li>
|
<span class="gf-form-label width-7">Type</span>
|
||||||
<input type="text" class="input-large tight-form-input" placeholder="name" ng-model='current.name'></input>
|
<div class="gf-form-select-wrapper max-width-10">
|
||||||
</li>
|
<select class="gf-form-input max-width-10" ng-model="current.type" ng-options="f for f in ['query', 'interval', 'custom']" ng-change="typeChanged()"></select>
|
||||||
<li class="tight-form-item">
|
</div>
|
||||||
Type
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
<li>
|
<span class="gf-form-label width-7" ng-show="current.type === 'query'">Data source</span>
|
||||||
<select class="input-small tight-form-input" ng-model="current.type" ng-options="f for f in ['query', 'interval', 'custom']" ng-change="typeChanged()"></select>
|
<div class="gf-form-select-wrapper" ng-show="current.type === 'query'">
|
||||||
</li>
|
<select class="gf-form-input max-width-14" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
|
||||||
<li class="tight-form-item" ng-show="current.type === 'query'">
|
</div>
|
||||||
Data source
|
</div>
|
||||||
</li>
|
</div>
|
||||||
<li ng-show="current.type === 'query'">
|
<div class="gf-form">
|
||||||
<select class="input input-medium tight-form-input last" ng-model="current.datasource" ng-options="f.value as f.name for f in datasources"></select>
|
<span class="gf-form-label width-7">Label</span>
|
||||||
</li>
|
<input type="text" class="gf-form-input max-width-14" ng-model='current.label' placeholder="optional display name"></input>
|
||||||
</ul>
|
<editor-checkbox class="width-13" text="Hide label" model="current.hideLabel" change="runQuery()"></editor-checkbox>
|
||||||
<div class="clearfix"></div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="section-heading">Value Options</h5>
|
||||||
|
<div ng-show="current.type === 'interval'" class="gf-form-group">
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-7">Values</span>
|
||||||
|
<input type="text" class="gf-form-input max-width-28" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()"></input>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<editor-checkbox text="Include auto interval" model="current.auto" change="runQuery()"></editor-checkbox>
|
||||||
|
<span class="gf-form-label" ng-show="current.auto">
|
||||||
|
Auto interval steps <tip>How many times should the current time range be divided to calculate the value</tip>
|
||||||
|
</span>
|
||||||
|
<div class="gf-form-select-wrapper max-width-10" ng-show="current.auto">
|
||||||
|
<select class="gf-form-input" ng-model="current.auto_count" ng-options="f for f in [3,5,10,30,50,100,200]" ng-change="runQuery()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label" ng-show="current.auto">
|
||||||
|
Auto interval min value <tip>The calculated value will not go below this threshold</tip>
|
||||||
|
</span>
|
||||||
|
<input type="text" class="gf-form-input max-width-10" ng-show="current.auto" ng-model="current.auto_min" ng-change="runQuery()"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="current.type === 'custom'" class="gf-form-group">
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-13">Values seperated by comma</span>
|
||||||
|
<input type="text" class="gf-form-input max-width-22" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form ">
|
||||||
|
<editor-checkbox class="width-13" text="All value" model="current.includeAll" change="runQuery()"></editor-checkbox>
|
||||||
|
<input ng-show="current.includeAll" type="text" class="gf-form-input max-width-22" ng-model='current.options[0].value' style="margin-left: 4px;"></input>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-13" ng-show="current.includeAll">All format</span>
|
||||||
|
<div class="gf-form-select-wrapper max-width-10" ng-show="current.includeAll">
|
||||||
|
<select class="gf-form-input" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row">
|
<div ng-show="current.type === 'query'" class="gf-form-group">
|
||||||
<div class="tight-form-section">
|
<div class="gf-form">
|
||||||
<h5>Value Options</h5>
|
<span class="gf-form-label width-7">Query</span>
|
||||||
|
<input type="text" class="gf-form-input" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()"></input>
|
||||||
<div ng-show="current.type === 'interval'">
|
</div>
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-7">
|
||||||
<li class="tight-form-item" style="width: 160px">
|
Regex
|
||||||
Values
|
<tip>Optional, if you want to extract part of a series name or metric node segment</tip>
|
||||||
</li>
|
</span>
|
||||||
<li>
|
<input type="text" class="gf-form-input" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
|
||||||
<input type="text" style="width: 345px" class="input-xxlarge tight-form-input last" placeholder="name" ng-model='current.query' placeholder="1m,10m,1h,6h,1d,7d" ng-model-onblur ng-change="runQuery()"></input>
|
</div>
|
||||||
</li>
|
<div class="gf-form">
|
||||||
</ul>
|
<span class="gf-form-label width-7">All value</span>
|
||||||
<div class="clearfix"></div>
|
<editor-checkbox class="width-13" text="Enable" model="current.includeAll" change="runQuery()"></editor-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form last">
|
<div class="gf-form-inline" ng-show="current.includeAll">
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form">
|
||||||
<li class="tight-form-item" style="width: 160px">
|
<span class="gf-form-label width-7">All format</span>
|
||||||
<editor-checkbox text="Include auto interval" model="current.auto" change="runQuery()"></editor-checkbox>
|
<div class="gf-form-select-wrapper">
|
||||||
</li>
|
<select class="gf-form-input" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
|
||||||
<li class="tight-form-item" ng-show="current.auto">
|
|
||||||
Auto interval steps <tip>How many times should the current time range be divided to calculate the value</tip>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<select class="input-mini tight-form-input last" ng-model="current.auto_count" ng-options="f for f in [3,5,10,30,50,100,200]" ng-change="runQuery()"></select>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="current.auto">
|
|
||||||
Auto interval min value <tip>The calculated value will not go below this threshold</tip>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" style="width: 35px" class="input-xxlarge tight-form-input last" ng-model="current.auto_min" ng-change="runQuery()"></input>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="gf-form max-width-30">
|
||||||
<div ng-show="current.type === 'custom'">
|
<span class="gf-form-label width-7">All value</span>
|
||||||
<div class="tight-form last">
|
<input type="text" class="gf-form-input" ng-model='current.options[0].value'></input>
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 180px">
|
|
||||||
Values seperated by comma
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" class="input tight-form-input last" style="width: 325px;" ng-model='current.query' ng-blur="runQuery()" placeholder="1, 10, 20, myvalue"></input>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 100px;">
|
|
||||||
<editor-checkbox text="All value" model="current.includeAll" change="runQuery()"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
<li ng-show="current.includeAll">
|
|
||||||
<input type="text" class="input-xlarge tight-form-input" style="width:364px" ng-model='current.options[0].value'></input>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="current.includeAll">
|
|
||||||
All format
|
|
||||||
</li>
|
|
||||||
<li ng-show="current.includeAll">
|
|
||||||
<select class="input-medium tight-form-input last" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<span class="gf-form-label width-7">Update</span>
|
||||||
|
<editor-checkbox text="On Dashboard Load" model="current.refresh"></editor-checkbox>
|
||||||
|
<tip>Check if you want values to be updated on dashboard load, will slow down dashboard load time</tip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div ng-show="current.type === 'query'">
|
<div class="gf-form-group" >
|
||||||
|
<h5 class="section-heading">Multi-value selection <tip>Enables multiple values to be selected at the same time</tip></h5>
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<editor-checkbox text="Enable" model="current.multi" change="runQuery()"></editor-checkbox>
|
||||||
<li class="tight-form-item" style="width: 100px">
|
<span class="gf-form-label" ng-show="current.multi">Multi format</span>
|
||||||
Query
|
<div class="gf-form-select-wrapper max-width-10" ng-show="current.multi">
|
||||||
</li>
|
<select class="gf-form-input" ng-model="current.multiFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'regex values', 'lucene', 'pipe']"></select>
|
||||||
<li>
|
|
||||||
<input type="text" style="width: 588px" class="input-xxlarge tight-form-input last" ng-model='current.query' placeholder="metric name or tags query" ng-model-onblur ng-change="runQuery()"></input>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 100px;">
|
|
||||||
Regex
|
|
||||||
<tip>Optional, if you want to extract part of a series name or metric node segment</tip>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" style="width: 588px" class="input tight-form-input last" ng-model='current.regex' placeholder="/.*-(.*)-.*/" ng-model-onblur ng-change="runQuery()"></input>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 100px;">
|
|
||||||
<editor-checkbox text="All value" model="current.includeAll" change="runQuery()"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
<li ng-show="current.includeAll">
|
|
||||||
<input type="text" class="input-xlarge tight-form-input" style="width:364px" ng-model='current.options[0].value'></input>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="current.includeAll">
|
|
||||||
All format
|
|
||||||
</li>
|
|
||||||
<li ng-show="current.includeAll">
|
|
||||||
<select class="input-medium tight-form-input last" ng-model="current.allFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'wildcard', 'regex wildcard', 'regex values', 'lucene', 'pipe']"></select>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<editor-checkbox text="Refresh on load" model="current.refresh"></editor-checkbox>
|
|
||||||
<tip>Check if you want values to be updated on dashboard load, will slow down dashboard load time</tip>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row">
|
<div class="gf-form-group" ng-if="current.type === 'query'">
|
||||||
<div class="tight-form-section" ng-hide="current.type === 'interval'">
|
<h5>Value groups/tags (Experimental feature)</h5>
|
||||||
<h5>Multi-value selection <tip>Enables multiple values to be selected at the same time</tip></h5>
|
<div class="gf-form">
|
||||||
<div class="tight-form last">
|
<editor-checkbox text="Enable" model="current.useTags" change="runQuery()"></editor-checkbox>
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item last" style="width: 100px;">
|
|
||||||
<editor-checkbox text="Enable" model="current.multi" change="runQuery()"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item" ng-show="current.multi">
|
|
||||||
Multi format
|
|
||||||
</li>
|
|
||||||
<li ng-show="current.multi">
|
|
||||||
<select class="input-medium tight-form-input last" ng-model="current.multiFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'regex values', 'lucene', 'pipe']"></select>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="gf-form last" ng-if="current.useTags">
|
||||||
<div class="tight-form-section">
|
<span class="gf-form-label width-10">Tags query</span>
|
||||||
<h5>Display options</h5>
|
<input type="text" class="gf-form-input" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
|
||||||
<div class="tight-form last">
|
</div>
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form" ng-if="current.useTags">
|
||||||
<li class="tight-form-item" style="width: 100px">
|
<li class="gf-form-label width-10">Tag values query</li>
|
||||||
Variable Label
|
<input type="text" class="gf-form-input" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" class="input-medium tight-form-input" ng-model='current.label' placeholder=""></input>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<editor-checkbox text="Hide label" model="current.hideLabel" change="runQuery()"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row" ng-if="current.type === 'query'">
|
<div class="gf-form-group">
|
||||||
<div class="tight-form-section">
|
<h5>Preview of values (shows max 20)</h5>
|
||||||
<h5>Value groups/tags (Experimental feature)</h5>
|
<div class="gf-form">
|
||||||
<div class="tight-form last" ng-if="current.useTags">
|
<span class="gf-form-label" ng-repeat="option in current.options | limitTo: 20">
|
||||||
<ul class="tight-form-list">
|
{{option.text}}
|
||||||
<li class="tight-form-item" style="width: 135px">
|
</span>
|
||||||
Tags query
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" style="width: 588px" class="input-xxlarge tight-form-input last" ng-model='current.tagsQuery' placeholder="metric name or tags query" ng-model-onblur></input>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form" ng-if="current.useTags">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" style="width: 135px;">
|
|
||||||
Tag values query
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" style="width: 588px" class="input tight-form-input last" ng-model='current.tagValuesQuery' placeholder="apps.$tag.*" ng-model-onblur></input>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tight-form">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<editor-checkbox text="Enable" model="current.useTags" change="runQuery()"></editor-checkbox>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="editor-row">
|
|
||||||
<div class="tight-form-section">
|
|
||||||
<h5>Preview of values (shows max 20)</h5>
|
|
||||||
<div class="tight-form last">
|
|
||||||
<ul class="tight-form-list">
|
|
||||||
<li class="tight-form-item" ng-repeat="option in current.options | limitTo: 20">
|
|
||||||
{{option.text}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row" style="margin-top: 20px">
|
<div class="gf-form-button-row p-y-0">
|
||||||
<button type="button" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
|
<button type="button" class="btn btn-success" ng-show="mode === 'edit'" ng-click="update();">Update</button>
|
||||||
<button type="button" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
|
<button type="button" class="btn btn-success" ng-show="mode === 'new'" ng-click="add();">Add</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
8
public/app/headers/common.d.ts
vendored
8
public/app/headers/common.d.ts
vendored
@ -39,4 +39,12 @@ declare module 'app/core/store' {
|
|||||||
export default store;
|
export default store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module 'tether' {
|
||||||
|
var config: any;
|
||||||
|
export default config;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tether-drop' {
|
||||||
|
var config: any;
|
||||||
|
export default config;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<div>
|
<div>
|
||||||
<ul class="nav nav-{{type || 'tabs'}} nav-tabs-alt" ng-class="{'nav-stacked': vertical, 'nav-justified': justified}" ng-transclude></ul>
|
<ul class="nav nav-tabs" ng-class="{'nav-stacked': vertical, 'nav-justified': justified}" ng-transclude>
|
||||||
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane"
|
<div class="tab-pane"
|
||||||
ng-repeat="tab in tabs"
|
ng-repeat="tab in tabs"
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
<div class="graph-legend-popover">
|
|
||||||
<a class="close" ng-click="dismiss();" href="">×</a>
|
|
||||||
|
|
||||||
<div class="editor-row">
|
|
||||||
<i ng-repeat="color in colors" class="pointer fa fa-circle"
|
|
||||||
ng-style="{color:color}"
|
|
||||||
ng-click="colorSelected(color);dismiss();"> </i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
@ -1,22 +1,21 @@
|
|||||||
<div ng-controller="JsonEditorCtrl">
|
<div ng-controller="JsonEditorCtrl">
|
||||||
|
<div class="tabbed-view-header">
|
||||||
|
<h2 class="tabbed-view-title">
|
||||||
|
JSON
|
||||||
|
</h2>
|
||||||
|
|
||||||
<div class="gf-box-header">
|
<button class="tabbed-view-close-btn" ng-click="dismiss()">
|
||||||
<div class="gf-box-title">
|
|
||||||
<i class="fa fa-edit"></i>
|
|
||||||
JSON
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
|
||||||
<i class="fa fa-remove"></i>
|
<i class="fa fa-remove"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-box-body" style="height: 500px">
|
<div class="tabbed-view-body">
|
||||||
<textarea ng-model="json" rows="20" spellcheck="false" style="width: 100%;"></textarea>
|
<div class="gf-form">
|
||||||
<br>
|
<textarea class="gf-form-input" ng-model="json" rows="20" spellcheck="false"></textarea>
|
||||||
<br>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-success" ng-show="canUpdate" ng-click="update(); dismiss();">Update</button>
|
<div class="gf-form-button-row">
|
||||||
|
<button type="button" class="btn btn-success" ng-show="canUpdate" ng-click="update(); dismiss();">Update</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +32,10 @@
|
|||||||
<td><span class="label label-info">CTRL+S</span></td>
|
<td><span class="label label-info">CTRL+S</span></td>
|
||||||
<td>Save dashboard</td>
|
<td>Save dashboard</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class="label label-info">CTRL+E</span></td>
|
||||||
|
<td>Export dashboard</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class="label label-info">CTRL+H</span></td>
|
<td><span class="label label-info">CTRL+H</span></td>
|
||||||
<td>Hide row controls</td>
|
<td>Hide row controls</td>
|
||||||
@ -40,6 +44,10 @@
|
|||||||
<td><span class="label label-info">CTRL+Z</span></td>
|
<td><span class="label label-info">CTRL+Z</span></td>
|
||||||
<td>Zoom out</td>
|
<td>Zoom out</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><span class="label label-info">CTRL+I</span></td>
|
||||||
|
<td>Quick snapshot</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class="label label-info">CTRL+O</span></td>
|
<td><span class="label label-info">CTRL+O</span></td>
|
||||||
<td>Enable/Disable shared graph crosshair</td>
|
<td>Enable/Disable shared graph crosshair</td>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<span class="gf-form-label width-6">Span</span>
|
<span class="gf-form-label width-6">Span</span>
|
||||||
<select class="gf-form-input gf-size-auto" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
|
<select class="gf-form-input gf-size-auto" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form max-width-21">
|
<div class="gf-form max-width-26">
|
||||||
<span class="gf-form-label width-8">Height</span>
|
<span class="gf-form-label width-8">Height</span>
|
||||||
<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.panel.height' placeholder="100px"></input>
|
<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.panel.height' placeholder="100px"></input>
|
||||||
<editor-checkbox text="Transparent" model="ctrl.panel.transparent"></editor-checkbox>
|
<editor-checkbox text="Transparent" model="ctrl.panel.transparent"></editor-checkbox>
|
||||||
@ -31,10 +31,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<panel-links-editor panel="ctrl.panel"></panel-links-editor>
|
<panel-links-editor panel="ctrl.panel"></panel-links-editor>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
<div class="container">
|
<div class="login-container container">
|
||||||
|
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
|
|
||||||
<div class="login-box-logo">
|
<div class="login-box-logo">
|
||||||
<a href="login">
|
<img class="logo-icon" src="public/img/grafana_icon.svg"></img><br>
|
||||||
<img src="img/logo_transparent_200x75.png">
|
<i class="icon-gf icon-gf-grafana_wordmark"></i>
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-inner-box">
|
<div class="login-inner-box">
|
||||||
@ -15,76 +14,50 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form name="sendResetForm" class="login-form" ng-show="mode === 'send'">
|
<form name="sendResetForm" class="login-form gf-form-group" ng-show="mode === 'send'">
|
||||||
<div class="tight-form last">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-7">User</span>
|
||||||
<li class="tight-form-item" style="width: 78px">
|
<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.userOrEmail' placeholder="email or username">
|
||||||
<strong>User</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" name="username" class="tight-form-input last" required ng-model='formModel.userOrEmail' placeholder="email or username" style="width: 253px">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-submit-button-row">
|
<div class="gf-form-button-row">
|
||||||
<button type="submit" class="btn" ng-click="sendResetEmail();" ng-class="{'btn-inverse': !sendResetForm.$valid, 'btn-primary': sendResetForm.$valid}">
|
<button type="submit" class="btn btn-large" ng-click="sendResetEmail();" ng-class="{'btn-inverse': !sendResetForm.$valid, 'btn-primary': sendResetForm.$valid}">
|
||||||
Send reset instructions
|
Send reset instructions
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h5 ng-if="mode === 'email-sent'" style="text-align: center; padding: 20px;">
|
<h5 style="text-align: center; padding: 20px;" ng-if="mode === 'email-sent'">
|
||||||
An email with a reset link as been sent to the email address, you should receive it shortly.
|
An email with a reset link as been sent to the email address, you should receive it shortly.
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
<form name="resetForm" class="login-form" ng-show="mode === 'reset'">
|
<form name="resetForm" class="login-form gf-form-group" ng-show="mode === 'reset'">
|
||||||
<div class="tight-form">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-10">New Password</span>
|
||||||
<li class="tight-form-item" style="width: 125px">
|
<input type="password" name="NewPassword" class="gf-form-input max-width-14" required ng-minlength="4" ng-model='formModel.newPassword' placeholder="password" watch-change="formModel.newPassword = inputValue;">
|
||||||
<strong>New Password</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="password" name="NewPassword" class="tight-form-input last" required ng-minlength="4" ng-model='formModel.newPassword' placeholder="password" style="width: 207px" watch-change="formModel.newPassword = inputValue;">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tight-form last">
|
<div class="gf-form">
|
||||||
<ul class="tight-form-list">
|
<span class="gf-form-label width-10">Confirm Password</span>
|
||||||
<li class="tight-form-item" style="width: 125px">
|
<input type="password" name="ConfirmPassword" class="gf-form-input max-width-14" required ng-minlength="4" ng-model='formModel.confirmPassword' placeholder="confirm password">
|
||||||
<strong>Confirm Password</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="password" name="ConfirmPassword" class="tight-form-input last" required ng-minlength="4" ng-model='formModel.confirmPassword' placeholder="confirm password" style="width: 207px">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-left: 141px; width: 207px;">
|
<div style="margin-left: 141px; width: 207px;">
|
||||||
<password-strength password="formModel.newPassword"></password-strength>
|
<password-strength password="formModel.newPassword"></password-strength>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-submit-button-row">
|
<div class="gf-form-button-row">
|
||||||
<button type="submit" class="btn" ng-click="submitReset();" ng-class="{'btn-inverse': !resetForm.$valid, 'btn-primary': resetForm.$valid}">
|
<button type="submit" class="btn" ng-click="submitReset();" ng-class="{'btn-inverse': !resetForm.$valid, 'btn-primary': resetForm.$valid}">
|
||||||
Reset Password
|
Reset Password
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" style="margin-top: 40px">
|
<div class="row" style="margin-top: 20px">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<a href="login">
|
<a href="login">Back to login</a>
|
||||||
Back to login
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,64 +1,47 @@
|
|||||||
|
<div class="tabbed-view-header">
|
||||||
<div class="gf-box-header">
|
<h2 class="tabbed-view-title">
|
||||||
<div class="gf-box-title">
|
|
||||||
<i class="fa fa-th-list"></i>
|
|
||||||
Row settings
|
Row settings
|
||||||
</div>
|
</h2>
|
||||||
|
|
||||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
<button class="tabbed-view-close-btn" ng-click="dismiss();">
|
||||||
<div ng-repeat="tab in ['General']" data-title="{{tab}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button class="gf-box-header-close-btn" ng-click="dismiss();">
|
|
||||||
<i class="fa fa-remove"></i>
|
<i class="fa fa-remove"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-box-body">
|
<div class="tabbed-view-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="page-heading">
|
||||||
|
<h5>Row details</h5>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form-group">
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
|
||||||
<div class="editor-row">
|
<div class="gf-form">
|
||||||
<div class="section">
|
<span class="gf-form-label width-6">Title</span>
|
||||||
<h5>Row details</h5>
|
<input type="text" class="gf-form-input max-width-14" ng-model='row.title'></input>
|
||||||
<div class="tight-form last">
|
</div>
|
||||||
<ul class="tight-form-list">
|
<div class="gf-form">
|
||||||
<li class="tight-form-item">
|
<span class="gf-form-label width-6">Height</span>
|
||||||
Title
|
<input type="text" class="gf-form-input max-width-8" ng-model='row.height'></input>
|
||||||
</li>
|
<editor-checkbox text="Show Title" model="row.showTitle"></editor-checkbox>
|
||||||
<li>
|
</div>
|
||||||
<input type="text" class="input-xlarge tight-form-input" ng-model='row.title'></input>
|
</div>
|
||||||
</li>
|
|
||||||
<li class="tight-form-item">
|
|
||||||
Height
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<input type="text" class="input-small tight-form-input" ng-model='row.height'></input>
|
|
||||||
</li>
|
|
||||||
<li class="tight-form-item last">
|
|
||||||
<label class="checkbox-label" for="row.showTitle">Show Title</label>
|
|
||||||
<input class="cr1" id="row.showTitle" type="checkbox" ng-model="row.showTitle" ng-checked="row.showTitle">
|
|
||||||
<label for="row.showTitle" class="cr1"></label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section">
|
<div class="col-md-4">
|
||||||
<h5>Templating options</h5>
|
<div class="page-heading">
|
||||||
<div class="tight-form last">
|
<h5>Templating options</h5>
|
||||||
<ul class="tight-form-list">
|
</div>
|
||||||
<li class="tight-form-item">
|
<div class="gf-form-group">
|
||||||
Repeat Row
|
<div class="gf-form">
|
||||||
</li>
|
<span class="gf-form-label">Repeat Row</span>
|
||||||
<li>
|
<div class="gf-form-select-wrapper max-width-10">
|
||||||
<select class="input-small tight-form-input last" ng-model="row.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
|
<select class="gf-form-input" ng-model="row.repeat" ng-options="f.name as f.name for f in dashboard.templating.list">
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
</select>
|
</div>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="variable-link-wrapper">
|
<div class="variable-link-wrapper">
|
||||||
<a ng-click="vm.show()" class="variable-value-link tight-form-item">
|
<a ng-click="vm.show()" class="variable-value-link">
|
||||||
{{vm.linkText}}
|
{{vm.linkText}}
|
||||||
<span ng-repeat="tag in vm.selectedTags" bs-tooltip='tag.valuesText' data-placement="bottom">
|
<span ng-repeat="tag in vm.selectedTags" bs-tooltip='tag.valuesText' data-placement="bottom">
|
||||||
<span class="label-tag"tag-color-from-name="tag.text">
|
<span class="label-tag"tag-color-from-name="tag.text">
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<i class="fa fa-caret-down"></i>
|
<i class="fa fa-caret-down"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<input type="text" class="tight-form-clear-input input-small" style="display: none" ng-keydown="vm.keyDown($event)" ng-model="vm.search.query" ng-change="vm.queryChanged()" ></input>
|
<input type="text" class="hidden-input input-small" style="display: none" ng-keydown="vm.keyDown($event)" ng-model="vm.search.query" ng-change="vm.queryChanged()" ></input>
|
||||||
|
|
||||||
<div class="variable-value-dropdown" ng-if="vm.dropdownVisible" ng-class="{'multi': vm.variable.multi, 'single': !vm.variable.multi}">
|
<div class="variable-value-dropdown" ng-if="vm.dropdownVisible" ng-class="{'multi': vm.variable.multi, 'single': !vm.variable.multi}">
|
||||||
<div class="variable-options-wrapper">
|
<div class="variable-options-wrapper">
|
||||||
|
2
public/app/plugins/datasource/cloudwatch/annotation_query.d.ts
vendored
Normal file
2
public/app/plugins/datasource/cloudwatch/annotation_query.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
declare var test: any;
|
||||||
|
export default test;
|
105
public/app/plugins/datasource/cloudwatch/annotation_query.js
Normal file
105
public/app/plugins/datasource/cloudwatch/annotation_query.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
define([
|
||||||
|
'lodash',
|
||||||
|
],
|
||||||
|
function (_) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function CloudWatchAnnotationQuery(datasource, annotation, $q, templateSrv) {
|
||||||
|
this.datasource = datasource;
|
||||||
|
this.annotation = annotation;
|
||||||
|
this.$q = $q;
|
||||||
|
this.templateSrv = templateSrv;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloudWatchAnnotationQuery.prototype.process = function(from, to) {
|
||||||
|
var self = this;
|
||||||
|
var usePrefixMatch = this.annotation.prefixMatching;
|
||||||
|
var region = this.templateSrv.replace(this.annotation.region);
|
||||||
|
var namespace = this.templateSrv.replace(this.annotation.namespace);
|
||||||
|
var metricName = this.templateSrv.replace(this.annotation.metricName);
|
||||||
|
var dimensions = this.datasource.convertDimensionFormat(this.annotation.dimensions);
|
||||||
|
var statistics = _.map(this.annotation.statistics, function(s) { return self.templateSrv.replace(s); });
|
||||||
|
var defaultPeriod = usePrefixMatch ? '' : '300';
|
||||||
|
var period = this.annotation.period || defaultPeriod;
|
||||||
|
period = parseInt(period, 10);
|
||||||
|
var actionPrefix = this.annotation.actionPrefix || '';
|
||||||
|
var alarmNamePrefix = this.annotation.alarmNamePrefix || '';
|
||||||
|
|
||||||
|
var d = this.$q.defer();
|
||||||
|
var allQueryPromise;
|
||||||
|
if (usePrefixMatch) {
|
||||||
|
allQueryPromise = [
|
||||||
|
this.datasource.performDescribeAlarms(region, actionPrefix, alarmNamePrefix, [], '').then(function(alarms) {
|
||||||
|
alarms.MetricAlarms = self.filterAlarms(alarms, namespace, metricName, dimensions, statistics, period);
|
||||||
|
return alarms;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
if (!region || !namespace || !metricName || _.isEmpty(statistics)) { return this.$q.when([]); }
|
||||||
|
|
||||||
|
allQueryPromise = _.map(statistics, function(statistic) {
|
||||||
|
return self.datasource.performDescribeAlarmsForMetric(region, namespace, metricName, dimensions, statistic, period);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.$q.all(allQueryPromise).then(function(alarms) {
|
||||||
|
var eventList = [];
|
||||||
|
|
||||||
|
var start = self.datasource.convertToCloudWatchTime(from, false);
|
||||||
|
var end = self.datasource.convertToCloudWatchTime(to, true);
|
||||||
|
_.chain(alarms)
|
||||||
|
.pluck('MetricAlarms')
|
||||||
|
.flatten()
|
||||||
|
.each(function(alarm) {
|
||||||
|
if (!alarm) {
|
||||||
|
d.resolve(eventList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.datasource.performDescribeAlarmHistory(region, alarm.AlarmName, start, end).then(function(history) {
|
||||||
|
_.each(history.AlarmHistoryItems, function(h) {
|
||||||
|
var event = {
|
||||||
|
annotation: self.annotation,
|
||||||
|
time: Date.parse(h.Timestamp),
|
||||||
|
title: h.AlarmName,
|
||||||
|
tags: [h.HistoryItemType],
|
||||||
|
text: h.HistorySummary
|
||||||
|
};
|
||||||
|
|
||||||
|
eventList.push(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
d.resolve(eventList);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return d.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
CloudWatchAnnotationQuery.prototype.filterAlarms = function(alarms, namespace, metricName, dimensions, statistics, period) {
|
||||||
|
return _.filter(alarms.MetricAlarms, function(alarm) {
|
||||||
|
if (!_.isEmpty(namespace) && alarm.Namespace !== namespace) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_.isEmpty(metricName) && alarm.MetricName !== metricName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var sd = function(d) {
|
||||||
|
return d.Name;
|
||||||
|
};
|
||||||
|
var isSameDimensions = JSON.stringify(_.sortBy(alarm.Dimensions, sd)) === JSON.stringify(_.sortBy(dimensions, sd));
|
||||||
|
if (!_.isEmpty(dimensions) && !isSameDimensions) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_.isEmpty(statistics) && !_.contains(statistics, alarm.Statistic)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_.isNaN(period) && alarm.Period !== period) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return CloudWatchAnnotationQuery;
|
||||||
|
});
|
@ -3,8 +3,9 @@ define([
|
|||||||
'lodash',
|
'lodash',
|
||||||
'moment',
|
'moment',
|
||||||
'app/core/utils/datemath',
|
'app/core/utils/datemath',
|
||||||
|
'./annotation_query',
|
||||||
],
|
],
|
||||||
function (angular, _, moment, dateMath) {
|
function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
@ -15,9 +16,10 @@ function (angular, _, moment, dateMath) {
|
|||||||
this.proxyUrl = instanceSettings.url;
|
this.proxyUrl = instanceSettings.url;
|
||||||
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
|
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
this.query = function(options) {
|
this.query = function(options) {
|
||||||
var start = convertToCloudWatchTime(options.range.from, false);
|
var start = self.convertToCloudWatchTime(options.range.from, false);
|
||||||
var end = convertToCloudWatchTime(options.range.to, true);
|
var end = self.convertToCloudWatchTime(options.range.to, true);
|
||||||
|
|
||||||
var queries = [];
|
var queries = [];
|
||||||
options = angular.copy(options);
|
options = angular.copy(options);
|
||||||
@ -30,7 +32,7 @@ function (angular, _, moment, dateMath) {
|
|||||||
query.region = templateSrv.replace(target.region, options.scopedVars);
|
query.region = templateSrv.replace(target.region, options.scopedVars);
|
||||||
query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
|
query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
|
||||||
query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
|
query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
|
||||||
query.dimensions = convertDimensionFormat(target.dimensions, options.scopedVars);
|
query.dimensions = self.convertDimensionFormat(target.dimensions, options.scopedVars);
|
||||||
query.statistics = target.statistics;
|
query.statistics = target.statistics;
|
||||||
|
|
||||||
var range = end - start;
|
var range = end - start;
|
||||||
@ -117,7 +119,7 @@ function (angular, _, moment, dateMath) {
|
|||||||
parameters: {
|
parameters: {
|
||||||
namespace: templateSrv.replace(namespace),
|
namespace: templateSrv.replace(namespace),
|
||||||
metricName: templateSrv.replace(metricName),
|
metricName: templateSrv.replace(metricName),
|
||||||
dimensions: convertDimensionFormat(filterDimensions, {}),
|
dimensions: this.convertDimensionFormat(filterDimensions, {}),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -206,6 +208,14 @@ function (angular, _, moment, dateMath) {
|
|||||||
return $q.when([]);
|
return $q.when([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.performDescribeAlarms = function(region, actionPrefix, alarmNamePrefix, alarmNames, stateValue) {
|
||||||
|
return this.awsRequest({
|
||||||
|
region: region,
|
||||||
|
action: 'DescribeAlarms',
|
||||||
|
parameters: { actionPrefix: actionPrefix, alarmNamePrefix: alarmNamePrefix, alarmNames: alarmNames, stateValue: stateValue }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.performDescribeAlarmsForMetric = function(region, namespace, metricName, dimensions, statistic, period) {
|
this.performDescribeAlarmsForMetric = function(region, namespace, metricName, dimensions, statistic, period) {
|
||||||
return this.awsRequest({
|
return this.awsRequest({
|
||||||
region: region,
|
region: region,
|
||||||
@ -223,55 +233,8 @@ function (angular, _, moment, dateMath) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.annotationQuery = function(options) {
|
this.annotationQuery = function(options) {
|
||||||
var annotation = options.annotation;
|
var annotationQuery = new CloudWatchAnnotationQuery(this, options.annotation, $q, templateSrv);
|
||||||
var region = templateSrv.replace(annotation.region);
|
return annotationQuery.process(options.range.from, options.range.to);
|
||||||
var namespace = templateSrv.replace(annotation.namespace);
|
|
||||||
var metricName = templateSrv.replace(annotation.metricName);
|
|
||||||
var dimensions = convertDimensionFormat(annotation.dimensions);
|
|
||||||
var statistics = _.map(annotation.statistics, function(s) { return templateSrv.replace(s); });
|
|
||||||
var period = annotation.period || '300';
|
|
||||||
period = parseInt(period, 10);
|
|
||||||
|
|
||||||
if (!region || !namespace || !metricName || _.isEmpty(statistics)) { return $q.when([]); }
|
|
||||||
|
|
||||||
var d = $q.defer();
|
|
||||||
var self = this;
|
|
||||||
var allQueryPromise = _.map(statistics, function(statistic) {
|
|
||||||
return self.performDescribeAlarmsForMetric(region, namespace, metricName, dimensions, statistic, period);
|
|
||||||
});
|
|
||||||
$q.all(allQueryPromise).then(function(alarms) {
|
|
||||||
var eventList = [];
|
|
||||||
|
|
||||||
var start = convertToCloudWatchTime(options.range.from, false);
|
|
||||||
var end = convertToCloudWatchTime(options.range.to, true);
|
|
||||||
_.chain(alarms)
|
|
||||||
.pluck('MetricAlarms')
|
|
||||||
.flatten()
|
|
||||||
.each(function(alarm) {
|
|
||||||
if (!alarm) {
|
|
||||||
d.resolve(eventList);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.performDescribeAlarmHistory(region, alarm.AlarmName, start, end).then(function(history) {
|
|
||||||
_.each(history.AlarmHistoryItems, function(h) {
|
|
||||||
var event = {
|
|
||||||
annotation: annotation,
|
|
||||||
time: Date.parse(h.Timestamp),
|
|
||||||
title: h.AlarmName,
|
|
||||||
tags: [h.HistoryItemType],
|
|
||||||
text: h.HistorySummary
|
|
||||||
};
|
|
||||||
|
|
||||||
eventList.push(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
d.resolve(eventList);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return d.promise;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.testDatasource = function() {
|
this.testDatasource = function() {
|
||||||
@ -347,21 +310,21 @@ function (angular, _, moment, dateMath) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToCloudWatchTime(date, roundUp) {
|
this.convertToCloudWatchTime = function(date, roundUp) {
|
||||||
if (_.isString(date)) {
|
if (_.isString(date)) {
|
||||||
date = dateMath.parse(date, roundUp);
|
date = dateMath.parse(date, roundUp);
|
||||||
}
|
}
|
||||||
return Math.round(date.valueOf() / 1000);
|
return Math.round(date.valueOf() / 1000);
|
||||||
}
|
};
|
||||||
|
|
||||||
function convertDimensionFormat(dimensions, scopedVars) {
|
this.convertDimensionFormat = function(dimensions, scopedVars) {
|
||||||
return _.map(dimensions, function(value, key) {
|
return _.map(dimensions, function(value, key) {
|
||||||
return {
|
return {
|
||||||
Name: templateSrv.replace(key, scopedVars),
|
Name: templateSrv.replace(key, scopedVars),
|
||||||
Value: templateSrv.replace(value, scopedVars)
|
Value: templateSrv.replace(value, scopedVars)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1 +1,19 @@
|
|||||||
<cloudwatch-query-parameter target="ctrl.annotation" datasource="ctrl.datasource"></cloudwatch-query-parameter>
|
<cloudwatch-query-parameter target="ctrl.annotation" datasource="ctrl.datasource"></cloudwatch-query-parameter>
|
||||||
|
<div class="editor-row">
|
||||||
|
<div class="section">
|
||||||
|
<h5>Prefix matching</h5>
|
||||||
|
<div class="editor-option">
|
||||||
|
<editor-checkbox text="Enable" model="ctrl.annotation.prefixMatching"></editor-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-option" ng-if="ctrl.annotation.prefixMatching">
|
||||||
|
<label class="small">Action</label>
|
||||||
|
<input type="text" class="input-small" ng-model='ctrl.annotation.actionPrefix'></input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-option" ng-if="ctrl.annotation.prefixMatching">
|
||||||
|
<label class="small">Alarm Name</label>
|
||||||
|
<input type="text" class="input-small" ng-model='ctrl.annotation.alarmNamePrefix'></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
import "../datasource";
|
||||||
|
import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
|
||||||
|
import moment from 'moment';
|
||||||
|
import helpers from 'test/specs/helpers';
|
||||||
|
import {CloudWatchDatasource} from "../datasource";
|
||||||
|
import CloudWatchAnnotationQuery from '../annotation_query';
|
||||||
|
|
||||||
|
describe('CloudWatchAnnotationQuery', function() {
|
||||||
|
var ctx = new helpers.ServiceTestContext();
|
||||||
|
var instanceSettings = {
|
||||||
|
jsonData: {defaultRegion: 'us-east-1', access: 'proxy'},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(angularMocks.module('grafana.core'));
|
||||||
|
beforeEach(angularMocks.module('grafana.services'));
|
||||||
|
beforeEach(angularMocks.module('grafana.controllers'));
|
||||||
|
beforeEach(ctx.providePhase(['templateSrv', 'backendSrv']));
|
||||||
|
|
||||||
|
beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
|
||||||
|
ctx.$q = $q;
|
||||||
|
ctx.$httpBackend = $httpBackend;
|
||||||
|
ctx.$rootScope = $rootScope;
|
||||||
|
ctx.ds = $injector.instantiate(CloudWatchDatasource, {instanceSettings: instanceSettings});
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('When performing annotationQuery', function() {
|
||||||
|
var parameter = {
|
||||||
|
annotation: {
|
||||||
|
region: 'us-east-1',
|
||||||
|
namespace: 'AWS/EC2',
|
||||||
|
metricName: 'CPUUtilization',
|
||||||
|
dimensions: {
|
||||||
|
InstanceId: 'i-12345678'
|
||||||
|
},
|
||||||
|
statistics: ['Average'],
|
||||||
|
period: 300
|
||||||
|
},
|
||||||
|
range: {
|
||||||
|
from: moment(1443438674760),
|
||||||
|
to: moment(1443460274760)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var alarmResponse = {
|
||||||
|
MetricAlarms: [
|
||||||
|
{
|
||||||
|
AlarmName: 'test_alarm_name'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
var historyResponse = {
|
||||||
|
AlarmHistoryItems: [
|
||||||
|
{
|
||||||
|
Timestamp: '2015-01-01T00:00:00.000Z',
|
||||||
|
HistoryItemType: 'StateUpdate',
|
||||||
|
AlarmName: 'test_alarm_name',
|
||||||
|
HistoryData: '{}',
|
||||||
|
HistorySummary: 'test_history_summary'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
beforeEach(function() {
|
||||||
|
ctx.backendSrv.datasourceRequest = function(params) {
|
||||||
|
switch (params.data.action) {
|
||||||
|
case 'DescribeAlarmsForMetric':
|
||||||
|
return ctx.$q.when({data: alarmResponse});
|
||||||
|
case 'DescribeAlarmHistory':
|
||||||
|
return ctx.$q.when({data: historyResponse});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
it('should return annotation list', function(done) {
|
||||||
|
var annotationQuery = new CloudWatchAnnotationQuery(ctx.ds, parameter.annotation, ctx.$q, ctx.templateSrv);
|
||||||
|
annotationQuery.process(parameter.range.from, parameter.range.to).then(function(result) {
|
||||||
|
expect(result[0].title).to.be('test_alarm_name');
|
||||||
|
expect(result[0].text).to.be('test_history_summary');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
ctx.$rootScope.$apply();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -187,59 +187,4 @@ describe('CloudWatchDatasource', function() {
|
|||||||
expect(scenario.request.data.action).to.be('ListMetrics');
|
expect(scenario.request.data.action).to.be('ListMetrics');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When performing annotationQuery', function() {
|
|
||||||
var parameter = {
|
|
||||||
annotation: {
|
|
||||||
region: 'us-east-1',
|
|
||||||
namespace: 'AWS/EC2',
|
|
||||||
metricName: 'CPUUtilization',
|
|
||||||
dimensions: {
|
|
||||||
InstanceId: 'i-12345678'
|
|
||||||
},
|
|
||||||
statistics: ['Average'],
|
|
||||||
period: 300
|
|
||||||
},
|
|
||||||
range: {
|
|
||||||
from: moment(1443438674760),
|
|
||||||
to: moment(1443460274760)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var alarmResponse = {
|
|
||||||
MetricAlarms: [
|
|
||||||
{
|
|
||||||
AlarmName: 'test_alarm_name'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
var historyResponse = {
|
|
||||||
AlarmHistoryItems: [
|
|
||||||
{
|
|
||||||
Timestamp: '2015-01-01T00:00:00.000Z',
|
|
||||||
HistoryItemType: 'StateUpdate',
|
|
||||||
AlarmName: 'test_alarm_name',
|
|
||||||
HistoryData: '{}',
|
|
||||||
HistorySummary: 'test_history_summary'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
beforeEach(function() {
|
|
||||||
ctx.backendSrv.datasourceRequest = function(params) {
|
|
||||||
switch (params.data.action) {
|
|
||||||
case 'DescribeAlarmsForMetric':
|
|
||||||
return ctx.$q.when({data: alarmResponse});
|
|
||||||
case 'DescribeAlarmHistory':
|
|
||||||
return ctx.$q.when({data: historyResponse});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
it('should return annotation list', function(done) {
|
|
||||||
ctx.ds.annotationQuery(parameter).then(function(result) {
|
|
||||||
expect(result[0].title).to.be('test_alarm_name');
|
|
||||||
expect(result[0].text).to.be('test_history_summary');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
ctx.$rootScope.$apply();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user