mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into influxdb-policy-selector
This commit is contained in:
commit
3b5a583903
@ -5,3 +5,6 @@ if [ $? -gt 0 ]; then
|
|||||||
echo "Some files aren't formatted, please run 'go fmt ./pkg/...' to format your source code before committing"
|
echo "Some files aren't formatted, please run 'go fmt ./pkg/...' to format your source code before committing"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
grunt test
|
||||||
|
@ -25,7 +25,7 @@ Name | Description
|
|||||||
------------ | -------------
|
------------ | -------------
|
||||||
Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards.
|
Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards.
|
||||||
Default | Default data source means that it will be pre-selected for new panels.
|
Default | Default data source means that it will be pre-selected for new panels.
|
||||||
Url | The http protocol, ip and port of you graphite-web or graphite-api install.
|
Url | The http protocol, ip and port of your graphite-web or graphite-api install.
|
||||||
Access | Proxy = access via Grafana backend, Direct = access directory from browser.
|
Access | Proxy = access via Grafana backend, Direct = access directory from browser.
|
||||||
|
|
||||||
|
|
||||||
|
@ -189,8 +189,8 @@ func Register(r *macaron.Macaron) {
|
|||||||
r.Get("/:id/items", ValidateOrgPlaylist, wrap(GetPlaylistItems))
|
r.Get("/:id/items", ValidateOrgPlaylist, wrap(GetPlaylistItems))
|
||||||
r.Get("/:id/dashboards", ValidateOrgPlaylist, wrap(GetPlaylistDashboards))
|
r.Get("/:id/dashboards", ValidateOrgPlaylist, wrap(GetPlaylistDashboards))
|
||||||
r.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, wrap(DeletePlaylist))
|
r.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, wrap(DeletePlaylist))
|
||||||
r.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistQuery{}), ValidateOrgPlaylist, wrap(UpdatePlaylist))
|
r.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistCommand{}), ValidateOrgPlaylist, wrap(UpdatePlaylist))
|
||||||
r.Post("/", reqEditorRole, bind(m.CreatePlaylistQuery{}), wrap(CreatePlaylist))
|
r.Post("/", reqEditorRole, bind(m.CreatePlaylistCommand{}), wrap(CreatePlaylist))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
|
@ -2,11 +2,12 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/log"
|
"github.com/grafana/grafana/pkg/log"
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ValidateOrgPlaylist(c *middleware.Context) {
|
func ValidateOrgPlaylist(c *middleware.Context) {
|
||||||
@ -33,8 +34,8 @@ func SearchPlaylists(c *middleware.Context) Response {
|
|||||||
limit = 1000
|
limit = 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
searchQuery := m.PlaylistQuery{
|
searchQuery := m.GetPlaylistsQuery{
|
||||||
Title: query,
|
Name: query,
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
OrgId: c.OrgId,
|
OrgId: c.OrgId,
|
||||||
}
|
}
|
||||||
@ -59,7 +60,7 @@ func GetPlaylist(c *middleware.Context) Response {
|
|||||||
|
|
||||||
dto := &m.PlaylistDTO{
|
dto := &m.PlaylistDTO{
|
||||||
Id: cmd.Result.Id,
|
Id: cmd.Result.Id,
|
||||||
Title: cmd.Result.Title,
|
Name: cmd.Result.Name,
|
||||||
Interval: cmd.Result.Interval,
|
Interval: cmd.Result.Interval,
|
||||||
OrgId: cmd.Result.OrgId,
|
OrgId: cmd.Result.OrgId,
|
||||||
Items: playlistDTOs,
|
Items: playlistDTOs,
|
||||||
@ -159,7 +160,7 @@ func GetPlaylistDashboards(c *middleware.Context) Response {
|
|||||||
func DeletePlaylist(c *middleware.Context) Response {
|
func DeletePlaylist(c *middleware.Context) Response {
|
||||||
id := c.ParamsInt64(":id")
|
id := c.ParamsInt64(":id")
|
||||||
|
|
||||||
cmd := m.DeletePlaylistQuery{Id: id}
|
cmd := m.DeletePlaylistCommand{Id: id, OrgId: c.OrgId}
|
||||||
if err := bus.Dispatch(&cmd); err != nil {
|
if err := bus.Dispatch(&cmd); err != nil {
|
||||||
return ApiError(500, "Failed to delete playlist", err)
|
return ApiError(500, "Failed to delete playlist", err)
|
||||||
}
|
}
|
||||||
@ -167,28 +168,28 @@ func DeletePlaylist(c *middleware.Context) Response {
|
|||||||
return Json(200, "")
|
return Json(200, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePlaylist(c *middleware.Context, query m.CreatePlaylistQuery) Response {
|
func CreatePlaylist(c *middleware.Context, cmd m.CreatePlaylistCommand) Response {
|
||||||
query.OrgId = c.OrgId
|
cmd.OrgId = c.OrgId
|
||||||
err := bus.Dispatch(&query)
|
|
||||||
if err != nil {
|
if err := bus.Dispatch(&cmd); err != nil {
|
||||||
return ApiError(500, "Failed to create playlist", err)
|
return ApiError(500, "Failed to create playlist", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Json(200, query.Result)
|
return Json(200, cmd.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdatePlaylist(c *middleware.Context, query m.UpdatePlaylistQuery) Response {
|
func UpdatePlaylist(c *middleware.Context, cmd m.UpdatePlaylistCommand) Response {
|
||||||
err := bus.Dispatch(&query)
|
cmd.OrgId = c.OrgId
|
||||||
|
|
||||||
|
if err := bus.Dispatch(&cmd); err != nil {
|
||||||
|
return ApiError(500, "Failed to save playlist", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistDTOs, err := LoadPlaylistItemDTOs(cmd.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ApiError(500, "Failed to save playlist", err)
|
return ApiError(500, "Failed to save playlist", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
playlistDTOs, err := LoadPlaylistItemDTOs(query.Id)
|
cmd.Result.Items = playlistDTOs
|
||||||
if err != nil {
|
return Json(200, cmd.Result)
|
||||||
return ApiError(500, "Failed to save playlist", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
query.Result.Items = playlistDTOs
|
|
||||||
|
|
||||||
return Json(200, query.Result)
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package login
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"crypto/subtle"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -56,7 +57,7 @@ func loginUsingGrafanaDB(query *LoginUserQuery) error {
|
|||||||
user := userQuery.Result
|
user := userQuery.Result
|
||||||
|
|
||||||
passwordHashed := util.EncodePassword(query.Password, user.Salt)
|
passwordHashed := util.EncodePassword(query.Password, user.Salt)
|
||||||
if passwordHashed != user.Password {
|
if subtle.ConstantTimeCompare([]byte(passwordHashed), []byte(user.Password)) != 1 {
|
||||||
return ErrInvalidCredentials
|
return ErrInvalidCredentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,14 +13,14 @@ var (
|
|||||||
// Playlist model
|
// Playlist model
|
||||||
type Playlist struct {
|
type Playlist struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Title string `json:"title"`
|
Name string `json:"name"`
|
||||||
Interval string `json:"interval"`
|
Interval string `json:"interval"`
|
||||||
OrgId int64 `json:"-"`
|
OrgId int64 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlaylistDTO struct {
|
type PlaylistDTO struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Title string `json:"title"`
|
Name string `json:"name"`
|
||||||
Interval string `json:"interval"`
|
Interval string `json:"interval"`
|
||||||
OrgId int64 `json:"-"`
|
OrgId int64 `json:"-"`
|
||||||
Items []PlaylistItemDTO `json:"items"`
|
Items []PlaylistItemDTO `json:"items"`
|
||||||
@ -71,35 +71,47 @@ type PlaylistDashboardDto struct {
|
|||||||
//
|
//
|
||||||
// COMMANDS
|
// COMMANDS
|
||||||
//
|
//
|
||||||
type PlaylistQuery struct {
|
|
||||||
Title string
|
|
||||||
Limit int
|
|
||||||
OrgId int64
|
|
||||||
|
|
||||||
Result Playlists
|
type UpdatePlaylistCommand struct {
|
||||||
}
|
OrgId int64 `json:"-"`
|
||||||
|
Id int64 `json:"id" binding:"Required"`
|
||||||
type UpdatePlaylistQuery struct {
|
Name string `json:"name" binding:"Required"`
|
||||||
Id int64
|
Type string `json:"type"`
|
||||||
Title string
|
Interval string `json:"interval"`
|
||||||
Type string
|
Data []int64 `json:"data"`
|
||||||
Interval string
|
Items []PlaylistItemDTO `json:"items"`
|
||||||
Items []PlaylistItemDTO
|
|
||||||
|
|
||||||
Result *PlaylistDTO
|
Result *PlaylistDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreatePlaylistQuery struct {
|
type CreatePlaylistCommand struct {
|
||||||
Title string
|
Name string `json:"name" binding:"Required"`
|
||||||
Type string
|
Type string `json:"type"`
|
||||||
Interval string
|
Interval string `json:"interval"`
|
||||||
Data []int64
|
Data []int64 `json:"data"`
|
||||||
OrgId int64
|
Items []PlaylistItemDTO `json:"items"`
|
||||||
Items []PlaylistItemDTO
|
|
||||||
|
|
||||||
|
OrgId int64 `json:"-"`
|
||||||
Result *Playlist
|
Result *Playlist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeletePlaylistCommand struct {
|
||||||
|
Id int64
|
||||||
|
OrgId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// QUERIES
|
||||||
|
//
|
||||||
|
|
||||||
|
type GetPlaylistsQuery struct {
|
||||||
|
Name string
|
||||||
|
Limit int
|
||||||
|
OrgId int64
|
||||||
|
|
||||||
|
Result Playlists
|
||||||
|
}
|
||||||
|
|
||||||
type GetPlaylistByIdQuery struct {
|
type GetPlaylistByIdQuery struct {
|
||||||
Id int64
|
Id int64
|
||||||
Result *Playlist
|
Result *Playlist
|
||||||
@ -114,7 +126,3 @@ type GetPlaylistDashboardsQuery struct {
|
|||||||
DashboardIds []int64
|
DashboardIds []int64
|
||||||
Result *PlaylistDashboards
|
Result *PlaylistDashboards
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeletePlaylistQuery struct {
|
|
||||||
Id int64
|
|
||||||
}
|
|
||||||
|
@ -3,20 +3,23 @@ package migrations
|
|||||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
|
|
||||||
func addPlaylistMigrations(mg *Migrator) {
|
func addPlaylistMigrations(mg *Migrator) {
|
||||||
playlistV1 := Table{
|
mg.AddMigration("Drop old table playlist table", NewDropTableMigration("playlist"))
|
||||||
|
mg.AddMigration("Drop old table playlist_item table", NewDropTableMigration("playlist_item"))
|
||||||
|
|
||||||
|
playlistV2 := Table{
|
||||||
Name: "playlist",
|
Name: "playlist",
|
||||||
Columns: []*Column{
|
Columns: []*Column{
|
||||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
|
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||||
{Name: "interval", Type: DB_NVarchar, Length: 255, Nullable: false},
|
{Name: "interval", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||||
{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// create table
|
// create table
|
||||||
mg.AddMigration("create playlist table v1", NewAddTableMigration(playlistV1))
|
mg.AddMigration("create playlist table v2", NewAddTableMigration(playlistV2))
|
||||||
|
|
||||||
playlistItemV1 := Table{
|
playlistItemV2 := Table{
|
||||||
Name: "playlist_item",
|
Name: "playlist_item",
|
||||||
Columns: []*Column{
|
Columns: []*Column{
|
||||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
@ -28,5 +31,5 @@ func addPlaylistMigrations(mg *Migrator) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mg.AddMigration("create playlist item table v1", NewAddTableMigration(playlistItemV1))
|
mg.AddMigration("create playlist item table v2", NewAddTableMigration(playlistItemV2))
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
"github.com/go-xorm/xorm"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
@ -18,13 +19,13 @@ func init() {
|
|||||||
bus.AddHandler("sql", GetPlaylistItem)
|
bus.AddHandler("sql", GetPlaylistItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePlaylist(query *m.CreatePlaylistQuery) error {
|
func CreatePlaylist(cmd *m.CreatePlaylistCommand) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
playlist := m.Playlist{
|
playlist := m.Playlist{
|
||||||
Title: query.Title,
|
Name: cmd.Name,
|
||||||
Interval: query.Interval,
|
Interval: cmd.Interval,
|
||||||
OrgId: query.OrgId,
|
OrgId: cmd.OrgId,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x.Insert(&playlist)
|
_, err = x.Insert(&playlist)
|
||||||
@ -32,7 +33,7 @@ func CreatePlaylist(query *m.CreatePlaylistQuery) error {
|
|||||||
fmt.Printf("%v", playlist.Id)
|
fmt.Printf("%v", playlist.Id)
|
||||||
|
|
||||||
playlistItems := make([]m.PlaylistItem, 0)
|
playlistItems := make([]m.PlaylistItem, 0)
|
||||||
for _, item := range query.Items {
|
for _, item := range cmd.Items {
|
||||||
playlistItems = append(playlistItems, m.PlaylistItem{
|
playlistItems = append(playlistItems, m.PlaylistItem{
|
||||||
PlaylistId: playlist.Id,
|
PlaylistId: playlist.Id,
|
||||||
Type: item.Type,
|
Type: item.Type,
|
||||||
@ -44,40 +45,40 @@ func CreatePlaylist(query *m.CreatePlaylistQuery) error {
|
|||||||
|
|
||||||
_, err = x.Insert(&playlistItems)
|
_, err = x.Insert(&playlistItems)
|
||||||
|
|
||||||
query.Result = &playlist
|
cmd.Result = &playlist
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdatePlaylist(query *m.UpdatePlaylistQuery) error {
|
func UpdatePlaylist(cmd *m.UpdatePlaylistCommand) error {
|
||||||
var err error
|
var err error
|
||||||
x.Logger.SetLevel(5)
|
|
||||||
playlist := m.Playlist{
|
playlist := m.Playlist{
|
||||||
Id: query.Id,
|
Id: cmd.Id,
|
||||||
Title: query.Title,
|
OrgId: cmd.OrgId,
|
||||||
Interval: query.Interval,
|
Name: cmd.Name,
|
||||||
|
Interval: cmd.Interval,
|
||||||
}
|
}
|
||||||
|
|
||||||
existingPlaylist := x.Where("id = ?", query.Id).Find(m.Playlist{})
|
existingPlaylist := x.Where("id = ? AND org_id = ?", cmd.Id, cmd.OrgId).Find(m.Playlist{})
|
||||||
|
|
||||||
if existingPlaylist == nil {
|
if existingPlaylist == nil {
|
||||||
return m.ErrPlaylistNotFound
|
return m.ErrPlaylistNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
query.Result = &m.PlaylistDTO{
|
cmd.Result = &m.PlaylistDTO{
|
||||||
Id: playlist.Id,
|
Id: playlist.Id,
|
||||||
OrgId: playlist.OrgId,
|
OrgId: playlist.OrgId,
|
||||||
Title: playlist.Title,
|
Name: playlist.Name,
|
||||||
Interval: playlist.Interval,
|
Interval: playlist.Interval,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = x.Id(query.Id).Cols("id", "title", "timespan").Update(&playlist)
|
_, err = x.Id(cmd.Id).Cols("id", "name", "interval").Update(&playlist)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rawSql := "DELETE FROM playlist_item WHERE playlist_id = ?"
|
rawSql := "DELETE FROM playlist_item WHERE playlist_id = ?"
|
||||||
_, err = x.Exec(rawSql, query.Id)
|
_, err = x.Exec(rawSql, cmd.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -85,7 +86,7 @@ func UpdatePlaylist(query *m.UpdatePlaylistQuery) error {
|
|||||||
|
|
||||||
playlistItems := make([]m.PlaylistItem, 0)
|
playlistItems := make([]m.PlaylistItem, 0)
|
||||||
|
|
||||||
for _, item := range query.Items {
|
for _, item := range cmd.Items {
|
||||||
playlistItems = append(playlistItems, m.PlaylistItem{
|
playlistItems = append(playlistItems, m.PlaylistItem{
|
||||||
PlaylistId: playlist.Id,
|
PlaylistId: playlist.Id,
|
||||||
Type: item.Type,
|
Type: item.Type,
|
||||||
@ -113,33 +114,33 @@ func GetPlaylist(query *m.GetPlaylistByIdQuery) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeletePlaylist(query *m.DeletePlaylistQuery) error {
|
func DeletePlaylist(cmd *m.DeletePlaylistCommand) error {
|
||||||
if query.Id == 0 {
|
if cmd.Id == 0 {
|
||||||
return m.ErrCommandValidationFailed
|
return m.ErrCommandValidationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
return inTransaction(func(sess *xorm.Session) error {
|
return inTransaction(func(sess *xorm.Session) error {
|
||||||
var rawPlaylistSql = "DELETE FROM playlist WHERE id = ?"
|
var rawPlaylistSql = "DELETE FROM playlist WHERE id = ? and org_id = ?"
|
||||||
_, err := sess.Exec(rawPlaylistSql, query.Id)
|
_, err := sess.Exec(rawPlaylistSql, cmd.Id, cmd.OrgId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var rawItemSql = "DELETE FROM playlist_item WHERE playlist_id = ?"
|
var rawItemSql = "DELETE FROM playlist_item WHERE playlist_id = ?"
|
||||||
_, err2 := sess.Exec(rawItemSql, query.Id)
|
_, err2 := sess.Exec(rawItemSql, cmd.Id)
|
||||||
|
|
||||||
return err2
|
return err2
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchPlaylists(query *m.PlaylistQuery) error {
|
func SearchPlaylists(query *m.GetPlaylistsQuery) error {
|
||||||
var playlists = make(m.Playlists, 0)
|
var playlists = make(m.Playlists, 0)
|
||||||
|
|
||||||
sess := x.Limit(query.Limit)
|
sess := x.Limit(query.Limit)
|
||||||
|
|
||||||
if query.Title != "" {
|
if query.Name != "" {
|
||||||
sess.Where("title LIKE ?", query.Title)
|
sess.Where("name LIKE ?", query.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
sess.Where("org_id = ?", query.OrgId)
|
sess.Where("org_id = ?", query.OrgId)
|
||||||
|
@ -139,7 +139,8 @@ export class GrafanaCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function grafanaAppDirective() {
|
/** @ngInject */
|
||||||
|
export function grafanaAppDirective(playlistSrv) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
controller: GrafanaCtrl,
|
controller: GrafanaCtrl,
|
||||||
@ -165,22 +166,33 @@ export function grafanaAppDirective() {
|
|||||||
|
|
||||||
// handle document clicks that should hide things
|
// handle document clicks that should hide things
|
||||||
elem.click(function(evt) {
|
elem.click(function(evt) {
|
||||||
if ($(evt.target).parents().length === 0) {
|
var target = $(evt.target);
|
||||||
|
if (target.parents().length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.parents('.dash-playlist-actions').length === 0) {
|
||||||
|
playlistSrv.stop();
|
||||||
|
}
|
||||||
|
|
||||||
// hide search
|
// hide search
|
||||||
if (elem.find('.search-container').length > 0) {
|
if (elem.find('.search-container').length > 0) {
|
||||||
if ($(evt.target).parents('.search-container').length === 0) {
|
if (target.parents('.search-container').length === 0) {
|
||||||
scope.appEvent('hide-dash-search');
|
scope.appEvent('hide-dash-search');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// hide sidemenu
|
// hide sidemenu
|
||||||
if (!ignoreSideMenuHide && elem.find('.sidemenu').length > 0) {
|
if (!ignoreSideMenuHide && elem.find('.sidemenu').length > 0) {
|
||||||
if ($(evt.target).parents('.sidemenu').length === 0) {
|
if (target.parents('.sidemenu').length === 0) {
|
||||||
scope.$apply(() => scope.contextSrv.toggleSideMenu());
|
scope.$apply(() => scope.contextSrv.toggleSideMenu());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hide popovers
|
||||||
|
var popover = elem.find('.popover');
|
||||||
|
if (popover.length > 0 && target.parents('.graph-legend').length === 0) {
|
||||||
|
popover.hide();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<span class="icon-circle top-nav-icon">
|
<span class="icon-circle top-nav-icon">
|
||||||
<i ng-class="ctrl.icon"></i>
|
<i ng-class="ctrl.icon"></i>
|
||||||
</span>
|
</span>
|
||||||
<a ng-href="{{ctl.titleUrl}}" class="top-nav-title">
|
<a ng-href="{{ctrl.titleUrl}}" class="top-nav-title">
|
||||||
{{ctrl.title}}
|
{{ctrl.title}}
|
||||||
</a>
|
</a>
|
||||||
<i ng-show="ctrl.subnav" class="top-nav-breadcrumb-icon fa fa-angle-right"></i>
|
<i ng-show="ctrl.subnav" class="top-nav-breadcrumb-icon fa fa-angle-right"></i>
|
||||||
|
@ -41,7 +41,7 @@ export class SideMenuCtrl {
|
|||||||
this.orgMenu = [
|
this.orgMenu = [
|
||||||
{section: 'You', cssClass: 'dropdown-menu-title'},
|
{section: 'You', cssClass: 'dropdown-menu-title'},
|
||||||
{text: 'Preferences', url: this.getUrl('/profile')},
|
{text: 'Preferences', url: this.getUrl('/profile')},
|
||||||
{text: 'Account', url: this.getUrl('/profile')},
|
{text: 'Profile', url: this.getUrl('/profile')},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.isSignedIn) {
|
if (this.isSignedIn) {
|
||||||
|
@ -355,6 +355,12 @@ function($, _) {
|
|||||||
kbn.valueFormats.pps = kbn.formatBuilders.decimalSIPrefix('pps');
|
kbn.valueFormats.pps = kbn.formatBuilders.decimalSIPrefix('pps');
|
||||||
kbn.valueFormats.bps = kbn.formatBuilders.decimalSIPrefix('bps');
|
kbn.valueFormats.bps = kbn.formatBuilders.decimalSIPrefix('bps');
|
||||||
kbn.valueFormats.Bps = kbn.formatBuilders.decimalSIPrefix('Bps');
|
kbn.valueFormats.Bps = kbn.formatBuilders.decimalSIPrefix('Bps');
|
||||||
|
kbn.valueFormats.KBs = kbn.formatBuilders.decimalSIPrefix('Bs', 1);
|
||||||
|
kbn.valueFormats.Kbits = kbn.formatBuilders.decimalSIPrefix('bits', 1);
|
||||||
|
kbn.valueFormats.MBs = kbn.formatBuilders.decimalSIPrefix('Bs', 2);
|
||||||
|
kbn.valueFormats.Mbits = kbn.formatBuilders.decimalSIPrefix('bits', 2);
|
||||||
|
kbn.valueFormats.GBs = kbn.formatBuilders.decimalSIPrefix('Bs', 3);
|
||||||
|
kbn.valueFormats.Gbits = kbn.formatBuilders.decimalSIPrefix('bits', 3);
|
||||||
|
|
||||||
// Throughput
|
// Throughput
|
||||||
kbn.valueFormats.ops = kbn.formatBuilders.simpleCountUnit('ops');
|
kbn.valueFormats.ops = kbn.formatBuilders.simpleCountUnit('ops');
|
||||||
@ -595,6 +601,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: 'kilobytes/sec', value: 'KBs'},
|
||||||
|
{text: 'megabites/sec', value: 'Mbits'},
|
||||||
|
{text: 'megabytes/sec', value: 'MBs'},
|
||||||
|
{text: 'gigabytes/sec', value: 'GBs'},
|
||||||
|
{text: 'gigabites/sec', value: 'Gbits'},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
<topnav title="Apps" icon="fa fa-fw fa-cubes" subnav="true">
|
<navbar title="Apps" title-url="apps" icon="fa fa-fw fa-cubes" subnav="true">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li ><a href="apps">Overview</a></li>
|
<li class="active" ><a href="apps/edit/{{ctrl.current.type}}">{{ctrl.appModel.name}}</a></li>
|
||||||
<li class="active" ><a href="apps/edit/{{ctrl.current.type}}">Edit</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</topnav>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="apps-side-box">
|
<div class="apps-side-box">
|
||||||
|
@ -2,11 +2,11 @@ define([
|
|||||||
'./dashboardCtrl',
|
'./dashboardCtrl',
|
||||||
'./dashboardLoaderSrv',
|
'./dashboardLoaderSrv',
|
||||||
'./dashnav/dashnav',
|
'./dashnav/dashnav',
|
||||||
|
'./submenu/submenu',
|
||||||
'./saveDashboardAsCtrl',
|
'./saveDashboardAsCtrl',
|
||||||
'./rowCtrl',
|
'./rowCtrl',
|
||||||
'./shareModalCtrl',
|
'./shareModalCtrl',
|
||||||
'./shareSnapshotCtrl',
|
'./shareSnapshotCtrl',
|
||||||
'./submenuCtrl',
|
|
||||||
'./dashboardSrv',
|
'./dashboardSrv',
|
||||||
'./keybindings',
|
'./keybindings',
|
||||||
'./viewStateSrv',
|
'./viewStateSrv',
|
||||||
|
30
public/app/features/dashboard/submenu/submenu.html
Normal file
30
public/app/features/dashboard/submenu/submenu.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<div class="submenu-controls">
|
||||||
|
<div class="tight-form borderless">
|
||||||
|
|
||||||
|
<ul class="tight-form-list" ng-if="ctrl.dashboard.templating.list.length > 0">
|
||||||
|
<li ng-repeat="variable in ctrl.variables" class="submenu-item">
|
||||||
|
<span class="template-variable tight-form-item" ng-show="!variable.hideLabel" style="padding-right: 5px">
|
||||||
|
{{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.annotations.list.length > 0">
|
||||||
|
<li ng-repeat="annotation in ctrl.dashboard.annotations.list" class="submenu-item annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">
|
||||||
|
<a ng-click="ctrl.disableAnnotation(annotation)">
|
||||||
|
<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">
|
||||||
|
<dash-links-container links="ctrl.dashboard.links"></dash-links-container>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
46
public/app/features/dashboard/submenu/submenu.ts
Normal file
46
public/app/features/dashboard/submenu/submenu.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
|
|
||||||
|
export class SubmenuCtrl {
|
||||||
|
annotations: any;
|
||||||
|
variables: any;
|
||||||
|
dashboard: any;
|
||||||
|
|
||||||
|
constructor(private $rootScope, private templateValuesSrv, private dynamicDashboardSrv) {
|
||||||
|
this.annotations = this.dashboard.templating.list;
|
||||||
|
this.variables = this.dashboard.templating.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
disableAnnotation(annotation) {
|
||||||
|
annotation.enable = !annotation.enable;
|
||||||
|
this.$rootScope.$broadcast('refresh');
|
||||||
|
}
|
||||||
|
|
||||||
|
getValuesForTag(variable, tagKey) {
|
||||||
|
return this.templateValuesSrv.getValuesForTag(variable, tagKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
variableUpdated(variable) {
|
||||||
|
this.templateValuesSrv.variableUpdated(variable).then(() => {
|
||||||
|
this.dynamicDashboardSrv.update(this.dashboard);
|
||||||
|
this.$rootScope.$emit('template-variable-value-updated');
|
||||||
|
this.$rootScope.$broadcast('refresh');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submenuDirective() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
templateUrl: 'app/features/dashboard/submenu/submenu.html',
|
||||||
|
controller: SubmenuCtrl,
|
||||||
|
bindToController: true,
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
scope: {
|
||||||
|
dashboard: "=",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('grafana.directives').directive('dashboardSubmenu', submenuDirective);
|
@ -1,39 +0,0 @@
|
|||||||
define([
|
|
||||||
'angular',
|
|
||||||
],
|
|
||||||
function (angular) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var module = angular.module('grafana.controllers');
|
|
||||||
|
|
||||||
module.controller('SubmenuCtrl', function($scope, $q, $rootScope, templateValuesSrv, dynamicDashboardSrv) {
|
|
||||||
|
|
||||||
$scope.init = function() {
|
|
||||||
$scope.panel = $scope.pulldown;
|
|
||||||
$scope.row = $scope.pulldown;
|
|
||||||
$scope.annotations = $scope.dashboard.templating.list;
|
|
||||||
$scope.variables = $scope.dashboard.templating.list;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.disableAnnotation = function (annotation) {
|
|
||||||
annotation.enable = !annotation.enable;
|
|
||||||
$rootScope.$broadcast('refresh');
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.getValuesForTag = function(variable, tagKey) {
|
|
||||||
return templateValuesSrv.getValuesForTag(variable, tagKey);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.variableUpdated = function(variable) {
|
|
||||||
templateValuesSrv.variableUpdated(variable).then(function() {
|
|
||||||
dynamicDashboardSrv.update($scope.dashboard);
|
|
||||||
$rootScope.$emit('template-variable-value-updated');
|
|
||||||
$rootScope.$broadcast('refresh');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.init();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1,6 +1,6 @@
|
|||||||
define([
|
define([
|
||||||
'./playlists_ctrl',
|
'./playlists_ctrl',
|
||||||
'./playlistSrv',
|
'./playlist_srv',
|
||||||
'./playlist_edit_ctrl',
|
'./playlist_edit_ctrl',
|
||||||
'./playlist_routes'
|
'./playlist_routes'
|
||||||
], function () {});
|
], function () {});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<navbar title="Playlists" title-url="playlists" icon="fa fa-fw fa-list" subnav="true">
|
<navbar title="Playlists" title-url="playlists" icon="fa fa-fw fa-list" subnav="true">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li ng-class="{active: isNew()}" ng-show="isNew()"><a href="datasources/create">New</a></li>
|
<li ng-class="{active: isNew()}" ng-show="isNew()"><a href="datasources/create">New</a></li>
|
||||||
<li class="active" ng-show="!isNew()"><a href="playlists/edit/{{playlist.id}}">{{playlist.title}}</a></li>
|
<li class="active" ng-show="!isNew()"><a href="playlists/edit/{{playlist.id}}">{{playlist.name}}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</navbar>
|
</navbar>
|
||||||
|
|
||||||
@ -20,7 +20,7 @@
|
|||||||
Name
|
Name
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input type="text" required ng-model="playlist.title" class="input-xlarge tight-form-input">
|
<input type="text" required ng-model="playlist.name" class="input-xlarge tight-form-input">
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<topnav icon="fa fa-fw fa-list" title="Playlists"></topnav>
|
<navbar icon="fa fa-fw fa-list" title="Playlists"></navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-wide">
|
<div class="page-wide">
|
||||||
|
|
||||||
<button type="submit" class="btn btn-inverse pull-right" ng-click="createPlaylist()">
|
<a class="btn btn-inverse pull-right" href="playlists/create">
|
||||||
<i class="fa fa-plus"></i>
|
<i class="fa fa-plus"></i>
|
||||||
New playlist
|
New playlist
|
||||||
</button>
|
</a>
|
||||||
|
|
||||||
<h2>Saved playlists</h2>
|
<h2>Saved playlists</h2>
|
||||||
|
|
||||||
@ -21,7 +21,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tr ng-repeat="playlist in playlists">
|
<tr ng-repeat="playlist in playlists">
|
||||||
<td>
|
<td>
|
||||||
<a href="playlists/edit/{{playlist.id}}">{{playlist.title}}</a>
|
<a href="playlists/edit/{{playlist.id}}">{{playlist.name}}</a>
|
||||||
</td>
|
</td>
|
||||||
<td >
|
<td >
|
||||||
<a href="playlists/play/{{playlist.id}}">playlists/play/{{playlist.id}}</a>
|
<a href="playlists/play/{{playlist.id}}">playlists/play/{{playlist.id}}</a>
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
define([
|
|
||||||
'angular',
|
|
||||||
'lodash',
|
|
||||||
'app/core/utils/kbn',
|
|
||||||
],
|
|
||||||
function (angular, _, kbn) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var module = angular.module('grafana.services');
|
|
||||||
|
|
||||||
module.service('playlistSrv', function($location, $rootScope, $timeout) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
this.next = function() {
|
|
||||||
$timeout.cancel(self.cancelPromise);
|
|
||||||
|
|
||||||
angular.element(window).unbind('resize');
|
|
||||||
var dash = self.dashboards[self.index % self.dashboards.length];
|
|
||||||
|
|
||||||
$location.url('dashboard/' + dash.uri);
|
|
||||||
|
|
||||||
self.index++;
|
|
||||||
self.cancelPromise = $timeout(self.next, self.interval);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.prev = function() {
|
|
||||||
self.index = Math.max(self.index - 2, 0);
|
|
||||||
self.next();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.start = function(dashboards, interval) {
|
|
||||||
self.stop();
|
|
||||||
|
|
||||||
self.index = 0;
|
|
||||||
self.interval = kbn.interval_to_ms(interval);
|
|
||||||
|
|
||||||
self.dashboards = dashboards;
|
|
||||||
$rootScope.playlistSrv = this;
|
|
||||||
|
|
||||||
self.cancelPromise = $timeout(self.next, self.interval);
|
|
||||||
self.next();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stop = function() {
|
|
||||||
self.index = 0;
|
|
||||||
|
|
||||||
if (self.cancelPromise) {
|
|
||||||
$timeout.cancel(self.cancelPromise);
|
|
||||||
}
|
|
||||||
|
|
||||||
$rootScope.playlistSrv = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -13,7 +13,9 @@ function (angular, config, _) {
|
|||||||
$scope.foundPlaylistItems = [];
|
$scope.foundPlaylistItems = [];
|
||||||
$scope.searchQuery = '';
|
$scope.searchQuery = '';
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$scope.playlist = {};
|
$scope.playlist = {
|
||||||
|
interval: '10m',
|
||||||
|
};
|
||||||
$scope.playlistItems = [];
|
$scope.playlistItems = [];
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
@ -68,7 +70,6 @@ function (angular, config, _) {
|
|||||||
|
|
||||||
$scope.playlistItems.push(playlistItem);
|
$scope.playlistItems.push(playlistItem);
|
||||||
$scope.filterFoundPlaylistItems();
|
$scope.filterFoundPlaylistItems();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.removePlaylistItem = function(playlistItem) {
|
$scope.removePlaylistItem = function(playlistItem) {
|
||||||
|
@ -23,19 +23,10 @@ function (angular) {
|
|||||||
controller : 'PlaylistEditCtrl'
|
controller : 'PlaylistEditCtrl'
|
||||||
})
|
})
|
||||||
.when('/playlists/play/:id', {
|
.when('/playlists/play/:id', {
|
||||||
templateUrl: 'app/partials/dashboard.html',
|
|
||||||
controller : 'LoadDashboardCtrl',
|
|
||||||
resolve: {
|
resolve: {
|
||||||
init: function(backendSrv, playlistSrv, $route) {
|
init: function(playlistSrv, $route) {
|
||||||
var playlistId = $route.current.params.id;
|
var playlistId = $route.current.params.id;
|
||||||
|
playlistSrv.start(playlistId);
|
||||||
return backendSrv.get('/api/playlists/' + playlistId)
|
|
||||||
.then(function(playlist) {
|
|
||||||
return backendSrv.get('/api/playlists/' + playlistId + '/dashboards')
|
|
||||||
.then(function(dashboards) {
|
|
||||||
playlistSrv.start(dashboards, playlist.interval);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
67
public/app/features/playlist/playlist_srv.ts
Normal file
67
public/app/features/playlist/playlist_srv.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import angular from 'angular';
|
||||||
|
import coreModule from '../../core/core_module';
|
||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
|
||||||
|
class PlaylistSrv {
|
||||||
|
private cancelPromise: any;
|
||||||
|
private dashboards: any;
|
||||||
|
private index: number;
|
||||||
|
private interval: any;
|
||||||
|
private playlistId: number;
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor(private $rootScope: any, private $location: any, private $timeout: any, private backendSrv: any) { }
|
||||||
|
|
||||||
|
next() {
|
||||||
|
this.$timeout.cancel(this.cancelPromise);
|
||||||
|
|
||||||
|
var playedAllDashboards = this.index > this.dashboards.length - 1;
|
||||||
|
|
||||||
|
if (playedAllDashboards) {
|
||||||
|
this.start(this.playlistId);
|
||||||
|
} else {
|
||||||
|
var dash = this.dashboards[this.index];
|
||||||
|
|
||||||
|
this.$location.url('dashboard/' + dash.uri);
|
||||||
|
|
||||||
|
this.index++;
|
||||||
|
this.cancelPromise = this.$timeout(() => this.next(), this.interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev() {
|
||||||
|
this.index = Math.max(this.index - 2, 0);
|
||||||
|
this.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
start(playlistId) {
|
||||||
|
this.stop();
|
||||||
|
|
||||||
|
this.index = 0;
|
||||||
|
this.playlistId = playlistId;
|
||||||
|
this.$rootScope.playlistSrv = this;
|
||||||
|
|
||||||
|
this.backendSrv.get(`/api/playlists/${playlistId}`).then(playlist => {
|
||||||
|
this.backendSrv.get(`/api/playlists/${playlistId}/dashboards`).then(dashboards => {
|
||||||
|
this.dashboards = dashboards;
|
||||||
|
this.interval = kbn.interval_to_ms(playlist.interval);
|
||||||
|
this.next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.index = 0;
|
||||||
|
this.playlistId = 0;
|
||||||
|
|
||||||
|
if (this.cancelPromise) {
|
||||||
|
this.$timeout.cancel(this.cancelPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$rootScope.playlistSrv = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.service('playlistSrv', PlaylistSrv);
|
@ -13,12 +13,7 @@ function (angular, _) {
|
|||||||
$scope.playlists = result;
|
$scope.playlists = result;
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.removePlaylist = function(playlist) {
|
$scope.removePlaylistConfirmed = function(playlist) {
|
||||||
var modalScope = $scope.$new(true);
|
|
||||||
|
|
||||||
modalScope.playlist = playlist;
|
|
||||||
modalScope.removePlaylist = function() {
|
|
||||||
modalScope.dismiss();
|
|
||||||
_.remove($scope.playlists, {id: playlist.id});
|
_.remove($scope.playlists, {id: playlist.id});
|
||||||
|
|
||||||
backendSrv.delete('/api/playlists/' + playlist.id)
|
backendSrv.delete('/api/playlists/' + playlist.id)
|
||||||
@ -30,14 +25,19 @@ function (angular, _) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.appEvent('show-modal', {
|
$scope.removePlaylist = function(playlist) {
|
||||||
src: './app/features/playlist/partials/playlist-remove.html',
|
|
||||||
scope: modalScope
|
$scope.appEvent('confirm-modal', {
|
||||||
|
title: 'Confirm delete playlist',
|
||||||
|
text: 'Are you sure you want to delete playlist ' + playlist.name + '?',
|
||||||
|
yesText: "Delete",
|
||||||
|
icon: "fa-warning",
|
||||||
|
onConfirm: function() {
|
||||||
|
$scope.removePlaylistConfirmed(playlist);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.createPlaylist = function() {
|
|
||||||
$location.path('/playlists/create');
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<topnav title="Profile" title-url="profile" icon="fa fa-user" subnav="true">
|
<navbar title="Profile" title-url="profile" icon="fa fa-fw fa-user" subnav="true">
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li class="active"><a href="profile/password">Change password</a></li>
|
<li class="active"><a href="profile/password">Change password</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</topnav>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page">
|
<div class="page">
|
||||||
@ -47,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<button type="submit" class="pull-right btn btn-success" ng-click="changePassword()">Change Password</button>
|
<div class="pull-right">
|
||||||
|
<button type="submit" class="btn btn-success" ng-click="changePassword()">Change Password</button>
|
||||||
|
|
||||||
|
<a class="btn btn-inverse" href="profile">Cancel</a>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<topnav title="Account" title-url="profile" icon="fa fa-fw fa-user">
|
<navbar title="Profile" title-url="profile" icon="fa fa-fw fa-user">
|
||||||
</topnav>
|
</navbar>
|
||||||
|
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="page-wide">
|
<div class="page-wide">
|
||||||
|
|
||||||
<h1>Account & Preferences</h1>
|
<h1>Profile</h1>
|
||||||
|
|
||||||
<section class="simple-box">
|
<section class="simple-box">
|
||||||
<h3 class="simple-box-header">Preferences</h3>
|
<h3 class="simple-box-header">Preferences</h3>
|
||||||
|
@ -6,8 +6,7 @@
|
|||||||
<div dash-search-view></div>
|
<div dash-search-view></div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
<div ng-if="submenuEnabled" ng-include="'app/partials/submenu.html'">
|
<dashboard-submenu ng-if="submenuEnabled" dashboard="dashboard"></dashboard-submenu>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
<div class="submenu-controls" ng-controller="SubmenuCtrl">
|
|
||||||
<div class="tight-form borderless">
|
|
||||||
|
|
||||||
<ul class="tight-form-list" ng-if="dashboard.templating.list.length > 0">
|
|
||||||
<li ng-repeat="variable in variables" class="submenu-item">
|
|
||||||
<span class="template-variable tight-form-item" ng-show="!variable.hideLabel" style="padding-right: 5px">
|
|
||||||
{{variable.label || variable.name}}:
|
|
||||||
</span>
|
|
||||||
<value-select-dropdown variable="variable" on-updated="variableUpdated(variable)" get-values-for-tag="getValuesForTag(variable, tagKey)"></value-select-dropdown>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul class="tight-form-list" ng-if="dashboard.annotations.list.length > 0">
|
|
||||||
<li ng-repeat="annotation in dashboard.annotations.list" class="submenu-item annotation-segment" ng-class="{'annotation-disabled': !annotation.enable}">
|
|
||||||
<a ng-click="disableAnnotation(annotation)">
|
|
||||||
<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="dashboard.links.length > 0">
|
|
||||||
<dash-links-container links="dashboard.links"></dash-links-container>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -45,7 +45,7 @@ function (angular, _, $) {
|
|||||||
popoverScope.series = seriesInfo;
|
popoverScope.series = seriesInfo;
|
||||||
popoverSrv.show({
|
popoverSrv.show({
|
||||||
element: el,
|
element: el,
|
||||||
templateUrl: 'app/plugins/panels/graph/legend.popover.html',
|
templateUrl: 'app/plugins/panel/graph/legend.popover.html',
|
||||||
scope: popoverScope
|
scope: popoverScope
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ function (SingleStatCtrl, _, $) {
|
|||||||
elem = inner;
|
elem = inner;
|
||||||
$panelContainer = elem.parents('.panel-container');
|
$panelContainer = elem.parents('.panel-container');
|
||||||
firstRender = false;
|
firstRender = false;
|
||||||
|
hookupDrilldownLinkTooltip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +187,7 @@ function (SingleStatCtrl, _, $) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hookupDrilldownLinkTooltip() {
|
||||||
// drilldown link tooltip
|
// drilldown link tooltip
|
||||||
var drilldownTooltip = $('<div id="tooltip" class="">hello</div>"');
|
var drilldownTooltip = $('<div id="tooltip" class="">hello</div>"');
|
||||||
|
|
||||||
@ -194,8 +196,10 @@ function (SingleStatCtrl, _, $) {
|
|||||||
drilldownTooltip.detach();
|
drilldownTooltip.detach();
|
||||||
});
|
});
|
||||||
|
|
||||||
elem.click(function() {
|
elem.click(function(evt) {
|
||||||
if (!linkInfo) { return; }
|
if (!linkInfo) { return; }
|
||||||
|
// ignore title clicks in title
|
||||||
|
if ($(evt).parents('.panel-header').length > 0) { return; }
|
||||||
|
|
||||||
if (linkInfo.target === '_blank') {
|
if (linkInfo.target === '_blank') {
|
||||||
var redirectWindow = window.open(linkInfo.href, '_blank');
|
var redirectWindow = window.open(linkInfo.href, '_blank');
|
||||||
@ -218,10 +222,10 @@ function (SingleStatCtrl, _, $) {
|
|||||||
if (!linkInfo) { return;}
|
if (!linkInfo) { return;}
|
||||||
|
|
||||||
drilldownTooltip.text('click to go to: ' + linkInfo.title);
|
drilldownTooltip.text('click to go to: ' + linkInfo.title);
|
||||||
|
|
||||||
drilldownTooltip.place_tt(e.pageX+20, e.pageY-15);
|
drilldownTooltip.place_tt(e.pageX+20, e.pageY-15);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
<body ng-cloak>
|
<body ng-cloak>
|
||||||
<grafana-app>
|
<grafana-app>
|
||||||
|
|
||||||
<aside class="sidemenu-wrapper">
|
<aside class="sidemenu-wrapper">
|
||||||
<sidemenu ng-if="contextSrv.sidemenu"></sidemenu>
|
<sidemenu ng-if="contextSrv.sidemenu"></sidemenu>
|
||||||
</aside>
|
</aside>
|
||||||
|
3
symlink_git_hooks.sh
Executable file
3
symlink_git_hooks.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#/bin/bash
|
||||||
|
|
||||||
|
ln -s .hooks/* .git/hooks/
|
Loading…
Reference in New Issue
Block a user