Merge branch 'master' into walmartlabs-master
This commit is contained in:
+6
-1
@@ -3,14 +3,19 @@
|
||||
## Enhancements
|
||||
* **Elasticsearch**: Added filter aggregation label [#8420](https://github.com/grafana/grafana/pull/8420), thx [@tianzk](github.com/tianzk)
|
||||
* **Sensu**: Added option for source and handler [#8405](https://github.com/grafana/grafana/pull/8405), thx [@joemiller](github.com/joemiller)
|
||||
* **CSV**: Configurable csv export datetime format [#8058](https://github.com/grafana/grafana/issues/8058), thx [@cederigo](github.com/cederigo)
|
||||
|
||||
# 4.3.2 (upcoming patch release)
|
||||
# 4.3.2 (2017-05-31)
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* **InfluxDB**: Fixed issue with query editor not showing ALIAS BY input field when in text editor mode [#8459](https://github.com/grafana/grafana/issues/8459)
|
||||
* **Graph Log Scale**: Fixed issue with log scale going below x-axis [#8244](https://github.com/grafana/grafana/issues/8244)
|
||||
* **Playlist**: Fixed dashboard play order issue [#7688](https://github.com/grafana/grafana/issues/7688)
|
||||
* **Elasticsearch**: Fixed table query issue with ES 2.x [#8467](https://github.com/grafana/grafana/issues/8467), thx [@goldeelox](https://github.com/goldeelox)
|
||||
|
||||
## Changes
|
||||
* **Lazy Loading Of Panels**: Panels are no longer loaded as they are scrolled into view, this was reverted due to Chrome bug, might be reintroduced when Chrome fixes it's JS blocking behavior on scroll. [#8500](https://github.com/grafana/grafana/issues/8500)
|
||||
|
||||
# 4.3.1 (2017-05-23)
|
||||
|
||||
|
||||
@@ -52,12 +52,22 @@ Here you can specify the name of the alert rule and how often the scheduler shou
|
||||
### Conditions
|
||||
|
||||
Currently the only condition type that exists is a `Query` condition that allows you to
|
||||
specify a query letter, time range and an aggregation function. The letter refers to
|
||||
a query you already have added in the **Metrics** tab. The result from the query and the aggregation function is
|
||||
a single value that is then used in the threshold check. The query used in an alert rule cannot
|
||||
contain any template variables. Currently we only support `AND` and `OR` operators between conditions and they are executed serially.
|
||||
specify a query letter, time range and an aggregation function.
|
||||
|
||||
|
||||
### Query condition example
|
||||
|
||||
```sql
|
||||
avg() OF query(A, 5m, now) IS BELOW 14
|
||||
```
|
||||
|
||||
- `avg()` Controls how the values for **each** serie should be reduced to a value that can be compared against the threshold. Click on the function to change it to another aggregation function.
|
||||
- `query(A, 5m, now)` The letter defines what query to execute from the **Metrics** tab. The second two parameters defines the time range, `5m, now` means 5 minutes from now to now. You can also do `10m, now-2m` to define a time range that will be 10 minutes from now to 2 minutes from now. This is useful if you want to ignore the last 2 minutes of data.
|
||||
- `IS BELOW 14` Defines the type of threshold and the threshold value. You can click on `IS BELOW` to change the type of threshold.
|
||||
|
||||
The query used in an alert rule cannot contain any template variables. Currently we only support `AND` and `OR` operators between conditions and they are executed serially.
|
||||
For example, we have 3 conditions in the following order:
|
||||
`condition:A(evaluates to: TRUE) OR condition:B(evaluates to: FALSE) AND condition:C(evaluates to: TRUE)`
|
||||
*condition:A(evaluates to: TRUE) OR condition:B(evaluates to: FALSE) AND condition:C(evaluates to: TRUE)*
|
||||
so the result will be calculated as ((TRUE OR FALSE) AND TRUE) = TRUE.
|
||||
|
||||
We plan to add other condition types in the future, like `Other Alert`, where you can include the state
|
||||
|
||||
@@ -92,9 +92,10 @@ The Elasticsearch data source supports two types of queries you can use in the *
|
||||
Query | Description
|
||||
------------ | -------------
|
||||
*{"find": "fields", "type": "keyword"} | Returns a list of field names with the index type `keyword`.
|
||||
*{"find": "terms", "field": "@hostname"}* | Returns a list of values for a field using term aggregation. Query will user current dashboard time range as time range for query.
|
||||
*{"find": "terms", "field": "@hostname", "size": 1000}* | Returns a list of values for a field using term aggregation. Query will user current dashboard time range as time range for query.
|
||||
*{"find": "terms", "field": "@hostname", "query": '<lucene query>'}* | Returns a list of values for a field using term aggregation & and a specified lucene query filter. Query will use current dashboard time range as time range for query.
|
||||
|
||||
There is a default size limit of 500 on terms queries. Set the size property in your query to set a custom limit.
|
||||
You can use other variables inside the query. Example query definition for a variable named `$host`.
|
||||
|
||||
```
|
||||
|
||||
@@ -65,7 +65,7 @@ Each field in the dashboard JSON is explained below with its usage:
|
||||
| **timezone** | timezone of dashboard, i.e. `utc` or `browser` |
|
||||
| **editable** | whether a dashboard is editable or not |
|
||||
| **hideControls** | whether row controls on the left in green are hidden or not |
|
||||
| **graphTooltip** | TODO |
|
||||
| **graphTooltip** | 0 for no shared crosshair or tooltip (default), 1 for shared crosshair, 2 for shared crosshair AND shared tooltip |
|
||||
| **rows** | row metadata, see [rows section](#rows) for details |
|
||||
| **time** | time range for dashboard, i.e. last 6 hours, last 7 days, etc |
|
||||
| **timepicker** | timepicker metadata, see [timepicker section](#timepicker) for details |
|
||||
|
||||
@@ -261,6 +261,7 @@ func (hs *HttpServer) registerRoutes() {
|
||||
r.Post("/tsdb/query", bind(dtos.MetricRequest{}), wrap(QueryMetrics))
|
||||
r.Get("/tsdb/testdata/scenarios", wrap(GetTestDataScenarios))
|
||||
r.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, wrap(GenerateSqlTestData))
|
||||
r.Get("/tsdb/testdata/random-walk", wrap(GetTestDataRandomWalk))
|
||||
|
||||
// metrics
|
||||
r.Get("/metrics", wrap(GetInternalMetrics))
|
||||
|
||||
@@ -90,6 +90,7 @@ func init() {
|
||||
"CacheHitPercent", "CachePercentUsed", "CachePercentDirty", "ReadBytes", "ReadTime", "WriteBytes", "WriteTime", "QueuedWrites"},
|
||||
"AWS/SWF": {"DecisionTaskScheduleToStartTime", "DecisionTaskStartToCloseTime", "DecisionTasksCompleted", "StartedDecisionTasksTimedOutOnClose", "WorkflowStartToCloseTime", "WorkflowsCanceled", "WorkflowsCompleted", "WorkflowsContinuedAsNew", "WorkflowsFailed", "WorkflowsTerminated", "WorkflowsTimedOut",
|
||||
"ActivityTaskScheduleToCloseTime", "ActivityTaskScheduleToStartTime", "ActivityTaskStartToCloseTime", "ActivityTasksCanceled", "ActivityTasksCompleted", "ActivityTasksFailed", "ScheduledActivityTasksTimedOutOnClose", "ScheduledActivityTasksTimedOutOnStart", "StartedActivityTasksTimedOutOnClose", "StartedActivityTasksTimedOutOnHeartbeat"},
|
||||
"AWS/VPN": {"TunnelState", "TunnelDataIn", "TunnelDataOut"},
|
||||
"AWS/WAF": {"AllowedRequests", "BlockedRequests", "CountedRequests"},
|
||||
"AWS/WorkSpaces": {"Available", "Unhealthy", "ConnectionAttempt", "ConnectionSuccess", "ConnectionFailure", "SessionLaunchTime", "InSessionLatency", "SessionDisconnect"},
|
||||
"KMS": {"SecondsUntilKeyMaterialExpiration"},
|
||||
@@ -131,6 +132,7 @@ func init() {
|
||||
"AWS/SQS": {"QueueName"},
|
||||
"AWS/StorageGateway": {"GatewayId", "GatewayName", "VolumeId"},
|
||||
"AWS/SWF": {"Domain", "WorkflowTypeName", "WorkflowTypeVersion", "ActivityTypeName", "ActivityTypeVersion"},
|
||||
"AWS/VPN": {"VpnId", "TunnelIpAddress"},
|
||||
"AWS/WAF": {"Rule", "WebACL"},
|
||||
"AWS/WorkSpaces": {"DirectoryId", "WorkspaceId"},
|
||||
"KMS": {"KeyId"},
|
||||
|
||||
@@ -28,6 +28,7 @@ var (
|
||||
ErrEmailNotAllowed = errors.New("Required email domain not fulfilled")
|
||||
ErrSignUpNotAllowed = errors.New("Signup is not allowed for this adapter")
|
||||
ErrUsersQuotaReached = errors.New("Users quota reached")
|
||||
ErrNoEmail = errors.New("Login provider didn't return an email address")
|
||||
)
|
||||
|
||||
func GenStateString() string {
|
||||
@@ -63,7 +64,7 @@ func OAuthLogin(ctx *middleware.Context) {
|
||||
if setting.OAuthService.OAuthInfos[name].HostedDomain == "" {
|
||||
ctx.Redirect(connect.AuthCodeURL(state, oauth2.AccessTypeOnline))
|
||||
} else {
|
||||
ctx.Redirect(connect.AuthCodeURL(state, oauth2.SetParam("hd", setting.OAuthService.OAuthInfos[name].HostedDomain), oauth2.AccessTypeOnline))
|
||||
ctx.Redirect(connect.AuthCodeURL(state, oauth2.SetAuthURLParam("hd", setting.OAuthService.OAuthInfos[name].HostedDomain), oauth2.AccessTypeOnline))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -134,6 +135,12 @@ func OAuthLogin(ctx *middleware.Context) {
|
||||
|
||||
ctx.Logger.Debug("OAuthLogin got user info", "userInfo", userInfo)
|
||||
|
||||
// validate that we got at least an email address
|
||||
if userInfo.Email == "" {
|
||||
redirectWithError(ctx, ErrNoEmail)
|
||||
return
|
||||
}
|
||||
|
||||
// validate that the email is allowed to login to grafana
|
||||
if !connect.IsEmailAllowed(userInfo.Email) {
|
||||
redirectWithError(ctx, ErrEmailNotAllowed)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/metrics"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@@ -144,3 +145,29 @@ func GenerateSqlTestData(c *middleware.Context) Response {
|
||||
|
||||
return Json(200, &util.DynMap{"message": "OK"})
|
||||
}
|
||||
|
||||
// GET /api/tsdb/testdata/random-walk
|
||||
func GetTestDataRandomWalk(c *middleware.Context) Response {
|
||||
from := c.Query("from")
|
||||
to := c.Query("to")
|
||||
intervalMs := c.QueryInt64("intervalMs")
|
||||
|
||||
timeRange := tsdb.NewTimeRange(from, to)
|
||||
request := &tsdb.Request{TimeRange: timeRange}
|
||||
|
||||
request.Queries = append(request.Queries, &tsdb.Query{
|
||||
RefId: "A",
|
||||
IntervalMs: intervalMs,
|
||||
Model: simplejson.NewFromAny(&util.DynMap{
|
||||
"scenario": "random_walk",
|
||||
}),
|
||||
DataSource: &models.DataSource{Type: "grafana-testdata-datasource"},
|
||||
})
|
||||
|
||||
resp, err := tsdb.HandleRequest(context.Background(), request)
|
||||
if err != nil {
|
||||
return ApiError(500, "Metric request error", err)
|
||||
}
|
||||
|
||||
return Json(200, &resp)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func addAlertMigrations(mg *Migrator) {
|
||||
{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "message", Type: DB_Text, Nullable: false},
|
||||
{Name: "state", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "state", Type: DB_NVarchar, Length: 190, Nullable: false},
|
||||
{Name: "settings", Type: DB_Text, Nullable: false},
|
||||
{Name: "frequency", Type: DB_BigInt, Nullable: false},
|
||||
{Name: "handler", Type: DB_BigInt, Nullable: false},
|
||||
@@ -70,7 +70,7 @@ func addAlertMigrations(mg *Migrator) {
|
||||
mg.AddMigration("Update alert table charset", NewTableCharsetMigration("alert", []*Column{
|
||||
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "message", Type: DB_Text, Nullable: false},
|
||||
{Name: "state", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "state", Type: DB_NVarchar, Length: 190, Nullable: false},
|
||||
{Name: "settings", Type: DB_Text, Nullable: false},
|
||||
{Name: "severity", Type: DB_Text, Nullable: false},
|
||||
{Name: "execution_error", Type: DB_Text, Nullable: false},
|
||||
|
||||
@@ -8,7 +8,7 @@ func addDashboardMigration(mg *Migrator) {
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "version", Type: DB_Int, Nullable: false},
|
||||
{Name: "slug", Type: DB_NVarchar, Length: 190, Nullable: false},
|
||||
{Name: "slug", Type: DB_NVarchar, Length: 189, Nullable: false},
|
||||
{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "data", Type: DB_Text, Nullable: false},
|
||||
{Name: "account_id", Type: DB_BigInt, Nullable: false},
|
||||
@@ -56,7 +56,7 @@ func addDashboardMigration(mg *Migrator) {
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "version", Type: DB_Int, Nullable: false},
|
||||
{Name: "slug", Type: DB_NVarchar, Length: 190, Nullable: false},
|
||||
{Name: "slug", Type: DB_NVarchar, Length: 189, Nullable: false},
|
||||
{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "data", Type: DB_Text, Nullable: false},
|
||||
{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
@@ -114,7 +114,7 @@ func addDashboardMigration(mg *Migrator) {
|
||||
|
||||
// add column to store plugin_id
|
||||
mg.AddMigration("Add column plugin_id in dashboard", NewAddColumnMigration(dashboardV2, &Column{
|
||||
Name: "plugin_id", Type: DB_NVarchar, Nullable: true, Length: 255,
|
||||
Name: "plugin_id", Type: DB_NVarchar, Nullable: true, Length: 189,
|
||||
}))
|
||||
|
||||
mg.AddMigration("Add index for plugin_id in dashboard", NewAddIndexMigration(dashboardV2, &Index{
|
||||
@@ -127,9 +127,9 @@ func addDashboardMigration(mg *Migrator) {
|
||||
}))
|
||||
|
||||
mg.AddMigration("Update dashboard table charset", NewTableCharsetMigration("dashboard", []*Column{
|
||||
{Name: "slug", Type: DB_NVarchar, Length: 190, Nullable: false},
|
||||
{Name: "slug", Type: DB_NVarchar, Length: 189, Nullable: false},
|
||||
{Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "plugin_id", Type: DB_NVarchar, Nullable: true, Length: 255},
|
||||
{Name: "plugin_id", Type: DB_NVarchar, Nullable: true, Length: 189},
|
||||
{Name: "data", Type: DB_MediumText, Nullable: false},
|
||||
}))
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ func addTempUserMigrations(mg *Migrator) {
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
{Name: "version", Type: DB_Int, Nullable: false},
|
||||
{Name: "email", Type: DB_NVarchar, Length: 255},
|
||||
{Name: "email", Type: DB_NVarchar, Length: 190},
|
||||
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
{Name: "role", Type: DB_NVarchar, Length: 20, Nullable: true},
|
||||
{Name: "code", Type: DB_NVarchar, Length: 255},
|
||||
{Name: "code", Type: DB_NVarchar, Length: 190},
|
||||
{Name: "status", Type: DB_Varchar, Length: 20},
|
||||
{Name: "invited_by_user_id", Type: DB_BigInt, Nullable: true},
|
||||
{Name: "email_sent", Type: DB_Bool},
|
||||
@@ -37,10 +37,10 @@ func addTempUserMigrations(mg *Migrator) {
|
||||
addTableIndicesMigrations(mg, "v1-7", tempUserV1)
|
||||
|
||||
mg.AddMigration("Update temp_user table charset", NewTableCharsetMigration("temp_user", []*Column{
|
||||
{Name: "email", Type: DB_NVarchar, Length: 255},
|
||||
{Name: "email", Type: DB_NVarchar, Length: 190},
|
||||
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true},
|
||||
{Name: "role", Type: DB_NVarchar, Length: 20, Nullable: true},
|
||||
{Name: "code", Type: DB_NVarchar, Length: 255},
|
||||
{Name: "code", Type: DB_NVarchar, Length: 190},
|
||||
{Name: "status", Type: DB_Varchar, Length: 20},
|
||||
{Name: "remote_addr", Type: DB_Varchar, Length: 255, Nullable: true},
|
||||
}))
|
||||
|
||||
@@ -2,7 +2,11 @@ package social
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
)
|
||||
|
||||
func isEmailAllowed(email string, allowedDomains []string) bool {
|
||||
@@ -18,3 +22,25 @@ func isEmailAllowed(email string, allowedDomains []string) bool {
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
func HttpGet(client *http.Client, url string) ([]byte, error) {
|
||||
r, err := client.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf(string(body))
|
||||
}
|
||||
|
||||
log.Trace("HTTP GET %s: %s %s", url, r.Status, string(body))
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
+19
-34
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@@ -84,22 +83,14 @@ func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) {
|
||||
IsConfirmed bool `json:"is_confirmed"`
|
||||
}
|
||||
|
||||
emailsUrl := fmt.Sprintf(s.apiUrl + "/emails")
|
||||
r, err := client.Get(emailsUrl)
|
||||
body, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/emails"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("Error getting email address: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var records []Record
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, records)
|
||||
err = json.Unmarshal(body, &records)
|
||||
if err != nil {
|
||||
var data struct {
|
||||
Values []Record `json:"values"`
|
||||
@@ -107,7 +98,7 @@ func (s *GenericOAuth) FetchPrivateEmail(client *http.Client) (string, error) {
|
||||
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("Error getting email address: %s", err)
|
||||
}
|
||||
|
||||
records = data.Values
|
||||
@@ -129,18 +120,16 @@ func (s *GenericOAuth) FetchTeamMemberships(client *http.Client) ([]int, error)
|
||||
Id int `json:"id"`
|
||||
}
|
||||
|
||||
membershipUrl := fmt.Sprintf(s.apiUrl + "/teams")
|
||||
r, err := client.Get(membershipUrl)
|
||||
body, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/teams"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting team memberships: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var records []Record
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
|
||||
return nil, err
|
||||
err = json.Unmarshal(body, &records)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting team memberships: %s", err)
|
||||
}
|
||||
|
||||
var ids = make([]int, len(records))
|
||||
@@ -156,18 +145,16 @@ func (s *GenericOAuth) FetchOrganizations(client *http.Client) ([]string, error)
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(s.apiUrl + "/orgs")
|
||||
r, err := client.Get(url)
|
||||
body, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/orgs"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting organizations: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var records []Record
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
|
||||
return nil, err
|
||||
err = json.Unmarshal(body, &records)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting organizations: %s", err)
|
||||
}
|
||||
|
||||
var logins = make([]string, len(records))
|
||||
@@ -188,16 +175,14 @@ func (s *GenericOAuth) UserInfo(client *http.Client) (*BasicUserInfo, error) {
|
||||
Attributes map[string][]string `json:"attributes"`
|
||||
}
|
||||
|
||||
var err error
|
||||
r, err := client.Get(s.apiUrl)
|
||||
body, err := HttpGet(client, s.apiUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
userInfo := &BasicUserInfo{
|
||||
|
||||
+20
-28
@@ -85,18 +85,16 @@ func (s *SocialGithub) FetchPrivateEmail(client *http.Client) (string, error) {
|
||||
Verified bool `json:"verified"`
|
||||
}
|
||||
|
||||
emailsUrl := fmt.Sprintf(s.apiUrl + "/emails")
|
||||
r, err := client.Get(emailsUrl)
|
||||
body, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/emails"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("Error getting email address: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var records []Record
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
|
||||
return "", err
|
||||
err = json.Unmarshal(body, &records)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error getting email address: %s", err)
|
||||
}
|
||||
|
||||
var email = ""
|
||||
@@ -114,18 +112,16 @@ func (s *SocialGithub) FetchTeamMemberships(client *http.Client) ([]int, error)
|
||||
Id int `json:"id"`
|
||||
}
|
||||
|
||||
membershipUrl := fmt.Sprintf(s.apiUrl + "/teams")
|
||||
r, err := client.Get(membershipUrl)
|
||||
body, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/teams"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting team memberships: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var records []Record
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
|
||||
return nil, err
|
||||
err = json.Unmarshal(body, &records)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting team memberships: %s", err)
|
||||
}
|
||||
|
||||
var ids = make([]int, len(records))
|
||||
@@ -141,18 +137,16 @@ func (s *SocialGithub) FetchOrganizations(client *http.Client) ([]string, error)
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(s.apiUrl + "/orgs")
|
||||
r, err := client.Get(url)
|
||||
body, err := HttpGet(client, fmt.Sprintf(s.apiUrl+"/orgs"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting organizations: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var records []Record
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
|
||||
return nil, err
|
||||
err = json.Unmarshal(body, &records)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting organizations: %s", err)
|
||||
}
|
||||
|
||||
var logins = make([]string, len(records))
|
||||
@@ -170,16 +164,14 @@ func (s *SocialGithub) UserInfo(client *http.Client) (*BasicUserInfo, error) {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
var err error
|
||||
r, err := client.Get(s.apiUrl)
|
||||
body, err := HttpGet(client, s.apiUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
userInfo := &BasicUserInfo{
|
||||
|
||||
@@ -2,6 +2,7 @@ package social
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@@ -34,16 +35,17 @@ func (s *SocialGoogle) UserInfo(client *http.Client) (*BasicUserInfo, error) {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
var err error
|
||||
|
||||
r, err := client.Get(s.apiUrl)
|
||||
body, err := HttpGet(client, s.apiUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
return &BasicUserInfo{
|
||||
Name: data.Name,
|
||||
Email: data.Email,
|
||||
|
||||
@@ -2,6 +2,7 @@ package social
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@@ -57,16 +58,14 @@ func (s *SocialGrafanaCom) UserInfo(client *http.Client) (*BasicUserInfo, error)
|
||||
Orgs []OrgRecord `json:"orgs"`
|
||||
}
|
||||
|
||||
var err error
|
||||
r, err := client.Get(s.url + "/api/oauth2/user")
|
||||
body, err := HttpGet(client, s.url+"/api/oauth2/user")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error getting user info: %s", err)
|
||||
}
|
||||
|
||||
userInfo := &BasicUserInfo{
|
||||
|
||||
@@ -105,10 +105,14 @@ export function grafanaAppDirective(playlistSrv, contextSrv) {
|
||||
if (pageClass) {
|
||||
body.removeClass(pageClass);
|
||||
}
|
||||
pageClass = data.$$route.pageClass;
|
||||
if (pageClass) {
|
||||
body.addClass(pageClass);
|
||||
|
||||
if (data.$$route) {
|
||||
pageClass = data.$$route.pageClass;
|
||||
if (pageClass) {
|
||||
body.addClass(pageClass);
|
||||
}
|
||||
}
|
||||
|
||||
$("#tooltip, .tooltip").remove();
|
||||
|
||||
// check for kiosk url param
|
||||
@@ -194,6 +198,15 @@ export function grafanaAppDirective(playlistSrv, contextSrv) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// hide menus
|
||||
var openMenus = body.find('.navbar-page-btn--open');
|
||||
if (openMenus.length > 0) {
|
||||
if (target.parents('.navbar-page-btn--open').length === 0) {
|
||||
openMenus.removeClass('navbar-page-btn--open');
|
||||
}
|
||||
}
|
||||
|
||||
// hide sidemenu
|
||||
if (!ignoreSideMenuHide && !contextSrv.pinned && body.find('.sidemenu').length > 0) {
|
||||
if (target.parents('.sidemenu').length === 0) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="modal-body">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-header-title">
|
||||
<i class="fa fa-keyboard"></i>
|
||||
<i class="fa fa-keyboard-o"></i>
|
||||
<span class="p-l-1">Shortcuts</span>
|
||||
</h2>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<div class="modal-content help-modal">
|
||||
|
||||
<p class="small" style="position: absolute; top: 48px; right: 10px">
|
||||
<p class="small" style="position: absolute; top: 13px; right: 44px">
|
||||
<span class="shortcut-table-key">mod</span> =
|
||||
<span class="muted">CTRL on windows or linux and CMD key on Mac</span>
|
||||
</p>
|
||||
|
||||
@@ -8,11 +8,36 @@
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</a>
|
||||
|
||||
<a href="{{ctrl.titleUrl}}" class="navbar-page-btn" ng-show="ctrl.title">
|
||||
<i class="{{ctrl.icon}}" ng-show="ctrl.icon"></i>
|
||||
<img ng-src="{{ctrl.iconUrl}}" ng-show="ctrl.iconUrl"></i>
|
||||
{{ctrl.title}}
|
||||
<a class="navbar-page-btn" ng-click="ctrl.showSearch()">
|
||||
<i class="fa fa-search"></i>
|
||||
</a>
|
||||
|
||||
<div ng-transclude></div>
|
||||
<div ng-if="::!ctrl.hasMenu">
|
||||
<a href="{{::ctrl.section.url}}" class="navbar-page-btn">
|
||||
<i class="{{::ctrl.section.icon}}" ng-show="::ctrl.section.icon"></i>
|
||||
<img ng-src="{{::ctrl.section.iconUrl}}" ng-show="::ctrl.section.iconUrl"></i>
|
||||
{{::ctrl.section.title}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="dropdown navbar-section-wrapper" ng-if="::ctrl.hasMenu">
|
||||
<a href="{{::ctrl.section.url}}" class="navbar-page-btn" data-toggle="dropdown">
|
||||
<i class="{{::ctrl.section.icon}}" ng-show="::ctrl.section.icon"></i>
|
||||
<img ng-src="{{::ctrl.section.iconUrl}}" ng-show="::ctrl.section.iconUrl"></i>
|
||||
{{::ctrl.section.title}}
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu--navbar">
|
||||
<li ng-repeat="navItem in ::ctrl.model.menu" ng-class="{active: navItem.active}">
|
||||
<a class="pointer" ng-href="{{::navItem.url}}" ng-click="ctrl.navItemClicked(navItem, $event)">
|
||||
<i class="{{::navItem.icon}}" ng-show="::navItem.icon"></i>
|
||||
{{::navItem.title}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-transclude></div>
|
||||
</div>
|
||||
|
||||
<dashboard-search></dashboard-search>
|
||||
|
||||
@@ -4,10 +4,28 @@ import config from 'app/core/config';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import coreModule from '../../core_module';
|
||||
import {NavModel, NavModelItem} from '../../nav_model_srv';
|
||||
|
||||
export class NavbarCtrl {
|
||||
model: NavModel;
|
||||
section: NavModelItem;
|
||||
hasMenu: boolean;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private contextSrv) {
|
||||
constructor(private $scope, private $rootScope, private contextSrv) {
|
||||
this.section = this.model.section;
|
||||
this.hasMenu = this.model.menu.length > 0;
|
||||
}
|
||||
|
||||
showSearch() {
|
||||
this.$rootScope.appEvent('show-dash-search');
|
||||
}
|
||||
|
||||
navItemClicked(navItem, evt) {
|
||||
if (navItem.clickHandler) {
|
||||
navItem.clickHandler();
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +38,9 @@ export function navbarDirective() {
|
||||
transclude: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
title: "@",
|
||||
titleUrl: "@",
|
||||
iconUrl: "@",
|
||||
model: "=",
|
||||
},
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
ctrl.icon = attrs.icon;
|
||||
link: function(scope, elem) {
|
||||
elem.addClass('navbar');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
|
||||
<div class="search-backdrop" ng-if="ctrl.isOpen"></div>
|
||||
|
||||
<div class="search-container" ng-if="ctrl.isOpen">
|
||||
|
||||
<div class="search-field-wrapper">
|
||||
<span style="position: relative;">
|
||||
<input type="text" placeholder="Find dashboards by name" give-focus="ctrl.giveSearchFocus" tabindex="1"
|
||||
ng-keydown="ctrl.keyDown($event)" ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.search()" />
|
||||
</span>
|
||||
<div class="search-field-icon pointer" ng-click="ctrl.closeSearch()">
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
|
||||
<input type="text" placeholder="Find dashboards by name" give-focus="ctrl.giveSearchFocus" tabindex="1"
|
||||
ng-keydown="ctrl.keyDown($event)"
|
||||
ng-model="ctrl.query.query"
|
||||
ng-model-options="{ debounce: 500 }"
|
||||
spellcheck='false'
|
||||
ng-change="ctrl.search()"
|
||||
ng-blur="ctrl.searchInputBlur()"
|
||||
/>
|
||||
|
||||
<div class="search-switches">
|
||||
<i class="fa fa-filter"></i>
|
||||
<a class="pointer" href="javascript:void 0;" ng-click="ctrl.showStarred()" tabindex="2">
|
||||
@@ -24,54 +37,55 @@
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="search-field-spacer"></div>
|
||||
</div>
|
||||
|
||||
<div class="search-results-container" ng-if="ctrl.tagsMode">
|
||||
<div ng-repeat="tag in ctrl.results" class="pointer" style="width: 180px; float: left;"
|
||||
ng-class="{'selected': $index === ctrl.selectedIndex }"
|
||||
ng-click="ctrl.filterByTag(tag.term, $event)">
|
||||
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span>{{tag.term}} ({{tag.count}})</span>
|
||||
<div class="search-dropdown" ng-class="{'search-dropdown--fade-in': ctrl.openCompleted}">
|
||||
<div class="search-results-container" ng-if="ctrl.tagsMode">
|
||||
<div ng-repeat="tag in ctrl.results" class="pointer" style="width: 180px; float: left;"
|
||||
ng-class="{'selected': $index === ctrl.selectedIndex }"
|
||||
ng-click="ctrl.filterByTag(tag.term, $event)">
|
||||
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span>{{tag.term}} ({{tag.count}})</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-results-container" ng-if="!ctrl.tagsMode">
|
||||
<h6 ng-hide="ctrl.results.length">No dashboards matching your query were found.</h6>
|
||||
|
||||
<a class="search-item pointer search-item-{{row.type}}" bindonce ng-repeat="row in ctrl.results"
|
||||
ng-class="{'selected': $index == ctrl.selectedIndex}" ng-href="{{row.url}}">
|
||||
|
||||
<span class="search-result-tags">
|
||||
<span ng-click="ctrl.filterByTag(tag, $event)" ng-repeat="tag in row.tags" tag-color-from-name="tag" class="label label-tag">
|
||||
{{tag}}
|
||||
</span>
|
||||
<i class="fa" ng-class="{'fa-star': row.isStarred, 'fa-star-o': !row.isStarred}"></i>
|
||||
</span>
|
||||
|
||||
<span class="search-result-link">
|
||||
<i class="fa search-result-icon"></i>
|
||||
<span bo-text="row.title"></span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="search-button-row">
|
||||
<a class="btn btn-secondary" href="dashboard/new" ng-show="ctrl.contextSrv.isEditor" ng-click="ctrl.isOpen = false;">
|
||||
<i class="fa fa-plus"></i> New Dashboard
|
||||
</a>
|
||||
|
||||
<a class="btn btn-inverse" href="dashboard/new/?editview=import" ng-show="ctrl.contextSrv.isEditor" ng-click="ctrl.isOpen = false;">
|
||||
<i class="fa fa-upload"></i> Import Dashboard
|
||||
</a>
|
||||
|
||||
<a class="search-button-row-explore-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
|
||||
Find <img src="public/img/icn-dashboard-tiny.svg" width="14" /> dashboards on Grafana.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-results-container" ng-if="!ctrl.tagsMode">
|
||||
<h6 ng-hide="ctrl.results.length">No dashboards matching your query were found.</h6>
|
||||
|
||||
<a class="search-item pointer search-item-{{row.type}}" bindonce ng-repeat="row in ctrl.results"
|
||||
ng-class="{'selected': $index == ctrl.selectedIndex}" ng-href="{{row.url}}">
|
||||
|
||||
<span class="search-result-tags">
|
||||
<span ng-click="ctrl.filterByTag(tag, $event)" ng-repeat="tag in row.tags" tag-color-from-name="tag" class="label label-tag">
|
||||
{{tag}}
|
||||
</span>
|
||||
<i class="fa" ng-class="{'fa-star': row.isStarred, 'fa-star-o': !row.isStarred}"></i>
|
||||
</span>
|
||||
|
||||
<span class="search-result-link">
|
||||
<i class="fa search-result-icon"></i>
|
||||
<span bo-text="row.title"></span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="search-button-row">
|
||||
<a class="btn btn-inverse pull-left" href="dashboard/new" ng-show="ctrl.contextSrv.isEditor" ng-click="ctrl.isOpen = false;">
|
||||
<i class="fa fa-plus"></i>
|
||||
Create New
|
||||
</a>
|
||||
|
||||
<a class="btn btn-inverse pull-left" href="dashboard/new/?editview=import" ng-show="ctrl.contextSrv.isEditor" ng-click="ctrl.isOpen = false;">
|
||||
<i class="fa fa-upload"></i>
|
||||
Import
|
||||
</a>
|
||||
|
||||
<a class="pull-right search-button-row-explore-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
|
||||
Find <img src="public/img/icn-dashboard-tiny.svg" width="14" /> dashboards on Grafana.com
|
||||
</a>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ export class SearchCtrl {
|
||||
showImport: boolean;
|
||||
dismiss: any;
|
||||
ignoreClose: any;
|
||||
// triggers fade animation class
|
||||
openCompleted: boolean;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private $location, private $timeout, private backendSrv, private contextSrv, private $rootScope) {
|
||||
@@ -27,6 +29,7 @@ export class SearchCtrl {
|
||||
|
||||
closeSearch() {
|
||||
this.isOpen = this.ignoreClose;
|
||||
this.openCompleted = false;
|
||||
}
|
||||
|
||||
openSearch(evt, payload) {
|
||||
@@ -56,6 +59,7 @@ export class SearchCtrl {
|
||||
}
|
||||
|
||||
this.$timeout(() => {
|
||||
this.openCompleted = true;
|
||||
this.ignoreClose = false;
|
||||
this.giveSearchFocus = this.giveSearchFocus + 1;
|
||||
this.search();
|
||||
@@ -169,6 +173,7 @@ export function searchDirective() {
|
||||
controller: SearchCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ define([
|
||||
function (angular, coreModule) {
|
||||
'use strict';
|
||||
|
||||
coreModule.default.controller('ErrorCtrl', function($scope, contextSrv) {
|
||||
coreModule.default.controller('ErrorCtrl', function($scope, contextSrv, navModelSrv) {
|
||||
|
||||
$scope.navModel = navModelSrv.getNotFoundNav();
|
||||
|
||||
var showSideMenu = contextSrv.sidemenu;
|
||||
contextSrv.sidemenu = false;
|
||||
|
||||
@@ -45,6 +45,7 @@ import {assignModelProperties} from './utils/model_utils';
|
||||
import {contextSrv} from './services/context_srv';
|
||||
import {KeybindingSrv} from './services/keybindingSrv';
|
||||
import {helpModal} from './components/help/help';
|
||||
import {NavModelSrv, NavModel} from './nav_model_srv';
|
||||
|
||||
|
||||
export {
|
||||
@@ -69,4 +70,6 @@ export {
|
||||
contextSrv,
|
||||
KeybindingSrv,
|
||||
helpModal,
|
||||
NavModelSrv,
|
||||
NavModel,
|
||||
};
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
define([
|
||||
'jquery',
|
||||
'angular',
|
||||
'../core_module',
|
||||
],
|
||||
function ($, coreModule) {
|
||||
function ($, angular, coreModule) {
|
||||
'use strict';
|
||||
|
||||
var editViewMap = {
|
||||
'settings': { src: 'public/app/features/dashboard/partials/settings.html'},
|
||||
'annotations': { src: 'public/app/features/annotations/partials/editor.html'},
|
||||
'history': { src: 'public/app/features/dashboard/history/partials/history.html'},
|
||||
'templating': { src: 'public/app/features/templating/partials/editor.html'},
|
||||
'import': { src: '<dash-import></dash-import>' }
|
||||
'history': { html: '<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>'},
|
||||
'timepicker': { src: 'public/app/features/dashboard/timepicker/dropdown.html' },
|
||||
'import': { html: '<dash-import></dash-import>' }
|
||||
};
|
||||
|
||||
coreModule.default.directive('dashEditorView', function($compile, $location, $rootScope) {
|
||||
@@ -18,47 +20,53 @@ function ($, coreModule) {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem) {
|
||||
var editorScope;
|
||||
var lastEditor;
|
||||
var lastEditView;
|
||||
|
||||
function hideEditorPane() {
|
||||
function hideEditorPane(hideToShowOtherView) {
|
||||
if (editorScope) {
|
||||
scope.appEvent('dash-editor-hidden', lastEditor);
|
||||
editorScope.dismiss();
|
||||
editorScope.dismiss(hideToShowOtherView);
|
||||
scope.appEvent('dash-editor-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function showEditorPane(evt, payload, editview) {
|
||||
if (editview) {
|
||||
scope.contextSrv.editview = editViewMap[editview];
|
||||
payload.src = scope.contextSrv.editview.src;
|
||||
function showEditorPane(evt, options) {
|
||||
if (options.editview) {
|
||||
options.src = editViewMap[options.editview].src;
|
||||
options.html = editViewMap[options.editview].html;
|
||||
}
|
||||
|
||||
if (lastEditor === payload.src) {
|
||||
hideEditorPane();
|
||||
if (lastEditView === options.editview) {
|
||||
hideEditorPane(false);
|
||||
return;
|
||||
}
|
||||
|
||||
hideEditorPane();
|
||||
hideEditorPane(true);
|
||||
|
||||
lastEditor = payload.src;
|
||||
editorScope = payload.scope ? payload.scope.$new() : scope.$new();
|
||||
lastEditView = options.editview;
|
||||
editorScope = options.scope ? options.scope.$new() : scope.$new();
|
||||
|
||||
editorScope.dismiss = function() {
|
||||
editorScope.dismiss = function(hideToShowOtherView) {
|
||||
editorScope.$destroy();
|
||||
elem.empty();
|
||||
lastEditor = null;
|
||||
lastEditView = null;
|
||||
editorScope = null;
|
||||
elem.removeClass('dash-edit-view--open');
|
||||
|
||||
if (editview) {
|
||||
if (!hideToShowOtherView) {
|
||||
setTimeout(function() {
|
||||
elem.empty();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
if (options.editview) {
|
||||
var urlParams = $location.search();
|
||||
if (editview === urlParams.editview) {
|
||||
if (options.editview === urlParams.editview) {
|
||||
delete urlParams.editview;
|
||||
$location.search(urlParams);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (editview === 'import') {
|
||||
if (options.editview === 'import') {
|
||||
var modalScope = $rootScope.$new();
|
||||
modalScope.$on("$destroy", function() {
|
||||
editorScope.dismiss();
|
||||
@@ -73,29 +81,42 @@ function ($, coreModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
var view = payload.src;
|
||||
if (view.indexOf('.html') > 0) {
|
||||
view = $('<div class="tabbed-view" ng-include="' + "'" + view + "'" + '"></div>');
|
||||
var view;
|
||||
if (options.src) {
|
||||
view = angular.element(document.createElement('div'));
|
||||
view.html('<div class="tabbed-view" ng-include="' + "'" + options.src + "'" + '"></div>');
|
||||
} else {
|
||||
view = angular.element(document.createElement('div'));
|
||||
view.addClass('tabbed-view');
|
||||
view.html(options.html);
|
||||
}
|
||||
|
||||
elem.append(view);
|
||||
$compile(elem.contents())(editorScope);
|
||||
$compile(view)(editorScope);
|
||||
|
||||
setTimeout(function() {
|
||||
elem.empty();
|
||||
elem.append(view);
|
||||
setTimeout(function() {
|
||||
elem.addClass('dash-edit-view--open');
|
||||
}, 10);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
scope.$watch("dashboardViewState.state.editview", function(newValue, oldValue) {
|
||||
if (newValue) {
|
||||
showEditorPane(null, {}, newValue);
|
||||
showEditorPane(null, {editview: newValue});
|
||||
} else if (oldValue) {
|
||||
scope.contextSrv.editview = null;
|
||||
if (lastEditor === editViewMap[oldValue]) {
|
||||
if (lastEditView === oldValue) {
|
||||
hideEditorPane();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scope.contextSrv.editview = null;
|
||||
scope.$on("$destroy", hideEditorPane);
|
||||
scope.onAppEvent('hide-dash-editor', hideEditorPane);
|
||||
scope.onAppEvent('hide-dash-editor', function() {
|
||||
hideEditorPane(false);
|
||||
});
|
||||
|
||||
scope.onAppEvent('show-dash-editor', showEditorPane);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
///<reference path="../headers/common.d.ts" />
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
export interface NavModelItem {
|
||||
title: string;
|
||||
url: string;
|
||||
icon?: string;
|
||||
iconUrl?: string;
|
||||
}
|
||||
|
||||
export interface NavModel {
|
||||
section: NavModelItem;
|
||||
menu: NavModelItem[];
|
||||
}
|
||||
|
||||
export class NavModelSrv {
|
||||
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private contextSrv) {
|
||||
}
|
||||
|
||||
getAlertingNav(subPage) {
|
||||
return {
|
||||
section: {
|
||||
title: 'Alerting',
|
||||
url: 'plugins',
|
||||
icon: 'icon-gf icon-gf-alert'
|
||||
},
|
||||
menu: [
|
||||
{title: 'Alert List', active: subPage === 0, url: 'alerting/list', icon: 'fa fa-list-ul'},
|
||||
{title: 'Notification channels', active: subPage === 1, url: 'alerting/notifications', icon: 'fa fa-bell-o'},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
getDatasourceNav(subPage) {
|
||||
return {
|
||||
section: {
|
||||
title: 'Data Sources',
|
||||
url: 'datasources',
|
||||
icon: 'icon-gf icon-gf-datasources'
|
||||
},
|
||||
menu: [
|
||||
{title: 'List view', active: subPage === 0, url: 'datasources', icon: 'fa fa-list-ul'},
|
||||
{title: 'Add data source', active: subPage === 1, url: 'datasources/new', icon: 'fa fa-plus'},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
getPlaylistsNav(subPage) {
|
||||
return {
|
||||
section: {
|
||||
title: 'Playlists',
|
||||
url: 'playlists',
|
||||
icon: 'fa fa-fw fa-film'
|
||||
},
|
||||
menu: [
|
||||
{title: 'List view', active: subPage === 0, url: 'playlists', icon: 'fa fa-list-ul'},
|
||||
{title: 'Add Playlist', active: subPage === 1, url: 'playlists/create', icon: 'fa fa-plus'},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
getProfileNav() {
|
||||
return {
|
||||
section: {
|
||||
title: 'User Profile',
|
||||
url: 'profile',
|
||||
icon: 'fa fa-fw fa-user'
|
||||
},
|
||||
menu: []
|
||||
};
|
||||
}
|
||||
|
||||
getNotFoundNav() {
|
||||
return {
|
||||
section: {
|
||||
title: 'Page',
|
||||
url: '',
|
||||
icon: 'fa fa-fw fa-warning'
|
||||
},
|
||||
menu: []
|
||||
};
|
||||
}
|
||||
|
||||
getOrgNav(subPage) {
|
||||
return {
|
||||
section: {
|
||||
title: 'Organization',
|
||||
url: 'org',
|
||||
icon: 'icon-gf icon-gf-users'
|
||||
},
|
||||
menu: [
|
||||
{title: 'Preferences', active: subPage === 0, url: 'org', icon: 'fa fa-fw fa-cog'},
|
||||
{title: 'Org Users', active: subPage === 1, url: 'org/users', icon: 'fa fa-fw fa-users'},
|
||||
{title: 'API Keys', active: subPage === 2, url: 'org/apikeys', icon: 'fa fa-fw fa-key'},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
getAdminNav(subPage) {
|
||||
return {
|
||||
section: {
|
||||
title: 'Admin',
|
||||
url: 'admin',
|
||||
icon: 'fa fa-fw fa-cogs'
|
||||
},
|
||||
menu: [
|
||||
{title: 'Users', active: subPage === 0, url: 'admin/users', icon: 'fa fa-fw fa-user'},
|
||||
{title: 'Orgs', active: subPage === 1, url: 'admin/orgs', icon: 'fa fa-fw fa-users'},
|
||||
{title: 'Server Settings', active: subPage === 2, url: 'admin/settings', icon: 'fa fa-fw fa-cogs'},
|
||||
{title: 'Server Stats', active: subPage === 2, url: 'admin/stats', icon: 'fa fa-fw fa-line-chart'},
|
||||
{title: 'Style Guide', active: subPage === 2, url: 'styleguide', icon: 'fa fa-fw fa-key'},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
getPluginsNav() {
|
||||
return {
|
||||
section: {
|
||||
title: 'Plugins',
|
||||
url: 'plugins',
|
||||
icon: 'icon-gf icon-gf-apps'
|
||||
},
|
||||
menu: []
|
||||
};
|
||||
}
|
||||
|
||||
getDashboardNav(dashboard, dashNavCtrl) {
|
||||
// special handling for snapshots
|
||||
if (dashboard.meta.isSnapshot) {
|
||||
return {
|
||||
section: {
|
||||
title: dashboard.title,
|
||||
icon: 'icon-gf icon-gf-snapshot'
|
||||
},
|
||||
menu: [
|
||||
{
|
||||
title: 'Go to original dashboard',
|
||||
icon: 'fa fa-fw fa-external-link',
|
||||
url: dashboard.snapshot.originalUrl,
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
var menu = [];
|
||||
|
||||
if (dashboard.meta.canEdit) {
|
||||
menu.push({
|
||||
title: 'Settings',
|
||||
icon: 'fa fa-fw fa-cog',
|
||||
clickHandler: () => dashNavCtrl.openEditView('settings')
|
||||
});
|
||||
|
||||
menu.push({
|
||||
title: 'Templating',
|
||||
icon: 'fa fa-fw fa-code',
|
||||
clickHandler: () => dashNavCtrl.openEditView('templating')
|
||||
});
|
||||
|
||||
menu.push({
|
||||
title: 'Annotations',
|
||||
icon: 'fa fa-fw fa-bolt',
|
||||
clickHandler: () => dashNavCtrl.openEditView('annotations')
|
||||
});
|
||||
|
||||
if (dashboard.version > 0) {
|
||||
menu.push({
|
||||
title: 'Version History',
|
||||
icon: 'fa fa-fw fa-history',
|
||||
clickHandler: () => dashNavCtrl.openEditView('history')
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({
|
||||
title: 'View JSON',
|
||||
icon: 'fa fa-fw fa-eye',
|
||||
clickHandler: () => dashNavCtrl.viewJson()
|
||||
});
|
||||
}
|
||||
|
||||
if (this.contextSrv.isEditor && !dashboard.editable) {
|
||||
menu.push({
|
||||
title: 'Make Editable',
|
||||
icon: 'fa fa-fw fa-edit',
|
||||
clickHandler: () => dashNavCtrl.makeEditable()
|
||||
});
|
||||
}
|
||||
|
||||
menu.push({
|
||||
title: 'Shortcuts',
|
||||
icon: 'fa fa-fw fa-keyboard-o',
|
||||
clickHandler: () => dashNavCtrl.showHelpModal()
|
||||
});
|
||||
|
||||
if (this.contextSrv.isEditor) {
|
||||
menu.push({
|
||||
title: 'Save As ...',
|
||||
icon: 'fa fa-fw fa-save',
|
||||
clickHandler: () => dashNavCtrl.saveDashboardAs()
|
||||
});
|
||||
}
|
||||
|
||||
if (dashboard.meta.canSave) {
|
||||
menu.push({
|
||||
title: 'Delete',
|
||||
icon: 'fa fa-fw fa-trash',
|
||||
clickHandler: () => dashNavCtrl.deleteDashboard()
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
section: {
|
||||
title: dashboard.title,
|
||||
icon: 'icon-gf icon-gf-dashboard'
|
||||
},
|
||||
menu: menu
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.service('navModelSrv', NavModelSrv);
|
||||
@@ -103,11 +103,13 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
.when('/admin', {
|
||||
templateUrl: 'public/app/features/admin/partials/admin_home.html',
|
||||
controller : 'AdminHomeCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadAdminBundle,
|
||||
})
|
||||
.when('/admin/settings', {
|
||||
templateUrl: 'public/app/features/admin/partials/settings.html',
|
||||
controller : 'AdminSettingsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadAdminBundle,
|
||||
})
|
||||
.when('/admin/users', {
|
||||
@@ -129,11 +131,13 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
.when('/admin/orgs', {
|
||||
templateUrl: 'public/app/features/admin/partials/orgs.html',
|
||||
controller : 'AdminListOrgsCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadAdminBundle,
|
||||
})
|
||||
.when('/admin/orgs/edit/:id', {
|
||||
templateUrl: 'public/app/features/admin/partials/edit_org.html',
|
||||
controller : 'AdminEditOrgCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadAdminBundle,
|
||||
})
|
||||
.when('/admin/stats', {
|
||||
|
||||
@@ -113,10 +113,6 @@ export class KeybindingSrv {
|
||||
scope.appEvent('shift-time-forward');
|
||||
});
|
||||
|
||||
this.bind('mod+i', () => {
|
||||
scope.appEvent('quick-snapshot');
|
||||
});
|
||||
|
||||
// edit panel
|
||||
this.bind('e', () => {
|
||||
if (dashboard.meta.focusPanelId && dashboard.meta.canEdit) {
|
||||
@@ -225,7 +221,7 @@ export class KeybindingSrv {
|
||||
}
|
||||
|
||||
scope.appEvent('hide-dash-editor');
|
||||
scope.exitFullscreen();
|
||||
scope.appEvent('panel-change-view', {fullscreen: false, edit: false});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,9 +87,11 @@ export default class TimeSeries {
|
||||
if (override.fill !== void 0) { this.lines.fill = translateFillOption(override.fill); }
|
||||
if (override.stack !== void 0) { this.stack = override.stack; }
|
||||
if (override.linewidth !== void 0) {
|
||||
this.lines.lineWidth = override.linewidth;
|
||||
this.lines.lineWidth = this.dashes.show ? 0: override.linewidth;
|
||||
this.dashes.lineWidth = override.linewidth;
|
||||
}
|
||||
if (override.dashLength !== void 0) { this.dashes.dashLength[0] = override.dashLength; }
|
||||
if (override.spaceLength !== void 0) { this.dashes.dashLength[1] = override.spaceLength; }
|
||||
if (override.nullPointMode !== void 0) { this.nullPointMode = override.nullPointMode; }
|
||||
if (override.pointradius !== void 0) { this.points.radius = override.pointradius; }
|
||||
if (override.steppedLine !== void 0) { this.lines.steps = override.steppedLine; }
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
declare var window: any;
|
||||
|
||||
export function exportSeriesListToCsv(seriesList) {
|
||||
var text = 'sep=;\nSeries;Time;Value\n';
|
||||
const DEFAULT_DATETIME_FORMAT: String = 'YYYY-MM-DDTHH:mm:ssZ';
|
||||
|
||||
export function exportSeriesListToCsv(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT) {
|
||||
var text = 'Series;Time;Value\n';
|
||||
_.each(seriesList, function(series) {
|
||||
_.each(series.datapoints, function(dp) {
|
||||
text += series.alias + ';' + new Date(dp[1]).toISOString() + ';' + dp[0] + '\n';
|
||||
text += series.alias + ';' + moment(dp[1]).format(dateTimeFormat) + ';' + dp[0] + '\n';
|
||||
});
|
||||
});
|
||||
saveSaveBlob(text, 'grafana_data_export.csv');
|
||||
}
|
||||
|
||||
export function exportSeriesListToCsvColumns(seriesList) {
|
||||
var text = 'sep=;\nTime;';
|
||||
export function exportSeriesListToCsvColumns(seriesList, dateTimeFormat = DEFAULT_DATETIME_FORMAT) {
|
||||
var text = 'Time;';
|
||||
// add header
|
||||
_.each(seriesList, function(series) {
|
||||
text += series.alias + ';';
|
||||
@@ -30,7 +33,7 @@ export function exportSeriesListToCsvColumns(seriesList) {
|
||||
var cIndex = 0;
|
||||
dataArr.push([]);
|
||||
_.each(series.datapoints, function(dp) {
|
||||
dataArr[0][cIndex] = new Date(dp[1]).toISOString();
|
||||
dataArr[0][cIndex] = moment(dp[1]).format(dateTimeFormat);
|
||||
dataArr[sIndex][cIndex] = dp[0];
|
||||
cIndex++;
|
||||
});
|
||||
@@ -50,7 +53,7 @@ export function exportSeriesListToCsvColumns(seriesList) {
|
||||
}
|
||||
|
||||
export function exportTableDataToCsv(table) {
|
||||
var text = 'sep=;\n';
|
||||
var text = '';
|
||||
// add header
|
||||
_.each(table.columns, function(column) {
|
||||
text += (column.title || column.text) + ';';
|
||||
|
||||
@@ -6,9 +6,11 @@ import './adminEditUserCtrl';
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
class AdminSettingsCtrl {
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject **/
|
||||
constructor($scope, backendSrv) {
|
||||
constructor($scope, backendSrv, navModelSrv) {
|
||||
this.navModel = navModelSrv.getAdminNav();
|
||||
|
||||
backendSrv.get('/api/admin/settings').then(function(settings) {
|
||||
$scope.settings = settings;
|
||||
@@ -18,16 +20,22 @@ class AdminSettingsCtrl {
|
||||
}
|
||||
|
||||
class AdminHomeCtrl {
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject **/
|
||||
constructor() {
|
||||
constructor(navModelSrv) {
|
||||
this.navModel = navModelSrv.getAdminNav();
|
||||
}
|
||||
}
|
||||
|
||||
export class AdminStatsCtrl {
|
||||
stats: any;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(backendSrv: any) {
|
||||
constructor(backendSrv: any, navModelSrv) {
|
||||
this.navModel = navModelSrv.getAdminNav();
|
||||
|
||||
backendSrv.get('/api/admin/stats').then(stats => {
|
||||
this.stats = stats;
|
||||
});
|
||||
|
||||
@@ -6,9 +6,11 @@ function (angular) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('AdminEditOrgCtrl', function($scope, $routeParams, backendSrv, $location) {
|
||||
module.controller('AdminEditOrgCtrl', function($scope, $routeParams, backendSrv, $location, navModelSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.navModel = navModelSrv.getAdminNav();
|
||||
|
||||
if ($routeParams.id) {
|
||||
$scope.getOrg($routeParams.id);
|
||||
$scope.getOrgUsers($routeParams.id);
|
||||
|
||||
@@ -7,10 +7,11 @@ function (angular, _) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('AdminEditUserCtrl', function($scope, $routeParams, backendSrv, $location) {
|
||||
module.controller('AdminEditUserCtrl', function($scope, $routeParams, backendSrv, $location, navModelSrv) {
|
||||
$scope.user = {};
|
||||
$scope.newOrg = { name: '', role: 'Editor' };
|
||||
$scope.permissions = {};
|
||||
$scope.navModel = navModelSrv.getAdminNav();
|
||||
|
||||
$scope.init = function() {
|
||||
if ($routeParams.id) {
|
||||
|
||||
@@ -6,9 +6,10 @@ function (angular) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('AdminListOrgsCtrl', function($scope, backendSrv) {
|
||||
module.controller('AdminListOrgsCtrl', function($scope, backendSrv, navModelSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.navModel = navModelSrv.getAdminNav();
|
||||
$scope.getOrgs();
|
||||
};
|
||||
|
||||
|
||||
@@ -8,9 +8,11 @@ export default class AdminListUsersCtrl {
|
||||
totalPages: number;
|
||||
showPaging = false;
|
||||
query: any;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private backendSrv) {
|
||||
constructor(private $scope, private backendSrv, private navModelSrv) {
|
||||
this.navModel = navModelSrv.getAdminNav();
|
||||
this.query = '';
|
||||
this.getUsers();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
<a href="admin/orgs" class="navbar-page-btn">
|
||||
<i class="icon-gf icon-gf-users"></i>
|
||||
Orgs
|
||||
</a>
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
<a href="admin/users" class="navbar-page-btn">
|
||||
<i class="icon-gf icon-gf-users"></i>
|
||||
Users
|
||||
</a>
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
<a href="admin/users" class="navbar-page-btn">
|
||||
<i class="icon-gf icon-gf-users"></i>
|
||||
Users
|
||||
</a>
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
<a href="admin/orgs" class="navbar-page-btn">
|
||||
<i class="icon-gf icon-gf-users"></i>
|
||||
Orgs
|
||||
</a>
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-cogs" title="Admin" title-url="admin">
|
||||
<a href="admin/users" class="navbar-page-btn">
|
||||
<i class="icon-gf icon-gf-users"></i>
|
||||
Users
|
||||
</a>
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
@@ -14,12 +9,14 @@
|
||||
Add new user
|
||||
</a>
|
||||
</div>
|
||||
<div class="search-field-wrapper pull-right width-18">
|
||||
|
||||
<div class="gf-form pull-right gf-form-group">
|
||||
<label class="gf-form-label">Search</label>
|
||||
<span style="position: relative;">
|
||||
<input type="text" placeholder="Find user by name/login/email" tabindex="1" give-focus="true"
|
||||
ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.getUsers()" />
|
||||
<input class="gf-form-input width-15" type="text" placeholder="Find user by name/login/email" tabindex="1" give-focus="true" ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.getUsers()" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="admin-list-table">
|
||||
<table class="filter-table form-inline">
|
||||
<thead>
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import coreModule from '../../core/core_module';
|
||||
import appEvents from '../../core/app_events';
|
||||
import moment from 'moment';
|
||||
|
||||
import {coreModule, appEvents} from 'app/core/core';
|
||||
import alertDef from './alert_def';
|
||||
|
||||
export class AlertListCtrl {
|
||||
|
||||
alerts: any;
|
||||
stateFilters = [
|
||||
{text: 'All', value: null},
|
||||
@@ -17,13 +16,15 @@ export class AlertListCtrl {
|
||||
{text: 'No Data', value: 'no_data'},
|
||||
{text: 'Paused', value: 'paused'},
|
||||
];
|
||||
|
||||
filters = {
|
||||
state: 'ALL'
|
||||
};
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv, private $location, private $scope) {
|
||||
constructor(private backendSrv, private $location, private $scope, navModelSrv) {
|
||||
this.navModel = navModelSrv.getAlertingNav(0);
|
||||
|
||||
var params = $location.search();
|
||||
this.filters.state = params.state || null;
|
||||
this.loadAlerts();
|
||||
|
||||
@@ -7,6 +7,7 @@ import {appEvents, coreModule} from 'app/core/core';
|
||||
|
||||
export class AlertNotificationEditCtrl {
|
||||
theForm: any;
|
||||
navModel: any;
|
||||
testSeverity = "critical";
|
||||
notifiers: any;
|
||||
notifierTemplateId: string;
|
||||
@@ -23,7 +24,9 @@ export class AlertNotificationEditCtrl {
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $routeParams, private backendSrv, private $location, private $templateCache) {
|
||||
constructor(private $routeParams, private backendSrv, private $location, private $templateCache, navModelSrv) {
|
||||
this.navModel = navModelSrv.getAlertingNav();
|
||||
|
||||
this.backendSrv.get(`/api/alert-notifiers`).then(notifiers => {
|
||||
this.notifiers = notifiers;
|
||||
|
||||
|
||||
@@ -2,16 +2,19 @@
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import coreModule from '../../core/core_module';
|
||||
import config from 'app/core/config';
|
||||
|
||||
export class AlertNotificationsListCtrl {
|
||||
import {coreModule} from 'app/core/core';
|
||||
|
||||
|
||||
export class AlertNotificationsListCtrl {
|
||||
notifications: any;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv, private $scope) {
|
||||
constructor(private backendSrv, private $scope, navModelSrv) {
|
||||
this.loadNotifications();
|
||||
this.navModel = navModelSrv.getAlertingNav(1);
|
||||
}
|
||||
|
||||
loadNotifications() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-alert" title="Alerting" title-url="alerting">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container" >
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-alert" title="Alerting" title-url="alerting">
|
||||
<a href="alerting/notifications" class="navbar-page-btn">
|
||||
<i class="fa fa-fw fa-rss"></i>
|
||||
Notification channels
|
||||
</a>
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-alert" title="Alerting" title-url="alerting">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container" >
|
||||
<div class="page-header">
|
||||
|
||||
@@ -19,6 +19,7 @@ define([
|
||||
'./upload',
|
||||
'./import/dash_import',
|
||||
'./export/export_modal',
|
||||
'./export_data/export_data_modal',
|
||||
'./dash_list_ctrl',
|
||||
'./ad_hoc_filters',
|
||||
'./row/row_ctrl',
|
||||
|
||||
@@ -1,95 +1,62 @@
|
||||
<navbar>
|
||||
<navbar model="ctrl.navModel">
|
||||
|
||||
<a class="pointer navbar-page-btn" ng-if="::!dashboardMeta.isSnapshot" ng-click="openSearch()">
|
||||
<i class="icon-gf icon-gf-dashboard"></i>
|
||||
<span>{{dashboard.title}}</span>
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</a>
|
||||
|
||||
<a class="pointer navbar-page-btn" ng-if="::dashboardMeta.isSnapshot" bs-tooltip="titleTooltip" data-placement="bottom" ng-click="openSearch()">
|
||||
<i class="icon-gf icon-gf-snapshot"></i>
|
||||
<span>
|
||||
{{dashboard.title}}
|
||||
<em class="small"> (snapshot)</em>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<ul class="nav dash-playlist-actions" ng-if="playlistSrv">
|
||||
<ul class="nav dash-playlist-actions" ng-if="ctrl.playlistSrv.isPlaying">
|
||||
<li>
|
||||
<a ng-click="playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
|
||||
<a ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="playlistSrv.stop()"><i class="fa fa-stop"></i></a>
|
||||
<a ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
|
||||
<a ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav pull-left dashnav-action-icons">
|
||||
<li ng-show="::dashboardMeta.canStar">
|
||||
<a class="pointer" ng-click="starDashboard()">
|
||||
<i class="fa" ng-class="{'fa-star-o': !dashboardMeta.isStarred, 'fa-star': dashboardMeta.isStarred}" style="color: orange;"></i>
|
||||
<li ng-show="::ctrl.dashboard.meta.canStar">
|
||||
<a class="pointer" ng-click="ctrl.starDashboard()">
|
||||
<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}" style="color: orange;"></i>
|
||||
</a>
|
||||
</li>
|
||||
<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>
|
||||
<li ng-show="::ctrl.dashboard.meta.canShare" class="dropdown">
|
||||
<a class="pointer" ng-click="ctrl.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">
|
||||
<li>
|
||||
<a class="pointer" ng-click="shareDashboard(0)">
|
||||
<a class="pointer" ng-click="ctrl.shareDashboard(0)">
|
||||
<i class="fa fa-link"></i> Link to Dashboard
|
||||
<div class="dropdown-desc">Share an internal link to the current dashboard. Some configuration options available.</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="pointer" ng-click="shareDashboard(1)">
|
||||
<a class="pointer" ng-click="ctrl.shareDashboard(1)">
|
||||
<i class="icon-gf icon-gf-snapshot"></i>Snapshot
|
||||
<div class="dropdown-desc">Interactive, publically accessible dashboard. Sensitive data is stripped out.</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="pointer" ng-click="shareDashboard(2)">
|
||||
<li>
|
||||
<a class="pointer" ng-click="ctrl.shareDashboard(2)">
|
||||
<i class="fa fa-cloud-upload"></i>Export
|
||||
<div class="dropdown-desc">Export the dashboard to a JSON file for others and to share on Grafana.com</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li ng-show="::dashboardMeta.canSave">
|
||||
<a ng-show="dashboard.version === 0" ng-click="saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom"><i class="fa fa-save"></i></a>
|
||||
<a ng-show="dashboard.version > 0" ng-click="saveDashboard()" bs-tooltip="'Save to changelog <br> CTRL+S'" data-placement="bottom"><i class="fa fa-save"></i></a>
|
||||
<li ng-show="::ctrl.dashboard.meta.canSave">
|
||||
<a ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom"><i class="fa fa-save"></i></a>
|
||||
</li>
|
||||
<li ng-if="dashboard.snapshot.originalUrl">
|
||||
<a ng-href="{{dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom"><i class="fa fa-link"></i></a>
|
||||
</li>
|
||||
<li ng-if="::showSettingsMenu" class="dropdown">
|
||||
<a class="pointer" ng-click="hideTooltip($event)" bs-tooltip="'Manage dashboard'" data-placement="bottom" data-toggle="dropdown"><i class="fa fa-cog"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li ng-if="dashboardMeta.canEdit"><a class="pointer" ng-click="openEditView('settings');">Settings</a></li>
|
||||
<li ng-if="dashboardMeta.canEdit"><a class="pointer" ng-click="openEditView('annotations');">Annotations</a></li>
|
||||
<li ng-if="dashboardMeta.canEdit && dashboard.version > 0 && !dashboardMeta.isHome"><a class="pointer" ng-click="openEditView('history');">Changelog</a></li>
|
||||
<li ng-if="dashboardMeta.canEdit"><a class="pointer" ng-click="openEditView('templating');">Templating</a></li>
|
||||
<li ng-if="dashboardMeta.canEdit"><a class="pointer" ng-click="viewJson();">View JSON</a></li>
|
||||
<li ng-if="contextSrv.isEditor && !dashboard.editable"><a class="pointer" ng-click="makeEditable();">Make Editable</a></li>
|
||||
<li ng-if="contextSrv.isEditor"><a class="pointer" ng-click="saveDashboardAs();">Save As...</a></li>
|
||||
<li class="dropdown-menu-item-with-shortcut">
|
||||
<a class="pointer" ng-click="showHelpModal();">
|
||||
Shortcuts <span class="dropdown-menu-item-shortcut">?</span>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="dashboardMeta.canSave"><a class="pointer" ng-click="deleteDashboard();">Delete dashboard</a></li>
|
||||
</ul>
|
||||
<li ng-if="::ctrl.dashboard.snapshot.originalUrl">
|
||||
<a ng-href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom"><i class="fa fa-link"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav pull-right">
|
||||
<li ng-show="dashboard.meta.fullscreen" class="dashnav-back-to-dashboard">
|
||||
<a ng-click="exitFullscreen()">
|
||||
<li ng-show="ctrl.dashboard.meta.fullscreen" class="dashnav-back-to-dashboard">
|
||||
<a ng-click="ctrl.exitFullscreen()">
|
||||
Back to dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="dashboard">
|
||||
<gf-time-picker dashboard="dashboard"></gf-time-picker>
|
||||
<li>
|
||||
<gf-time-picker dashboard="ctrl.dashboard"></gf-time-picker>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -3,92 +3,98 @@
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import angular from 'angular';
|
||||
|
||||
import {appEvents, NavModel} from 'app/core/core';
|
||||
import {DashboardModel} from '../model';
|
||||
import {DashboardExporter} from '../export/exporter';
|
||||
|
||||
export class DashNavCtrl {
|
||||
dashboard: DashboardModel;
|
||||
navModel: NavModel;
|
||||
titleTooltip: string;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $rootScope, dashboardSrv, $location, playlistSrv, backendSrv, $timeout, datasourceSrv) {
|
||||
constructor(
|
||||
private $scope,
|
||||
private $rootScope,
|
||||
private dashboardSrv,
|
||||
private $location,
|
||||
private playlistSrv,
|
||||
private backendSrv,
|
||||
private $timeout,
|
||||
private datasourceSrv,
|
||||
private navModelSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.onAppEvent('save-dashboard', $scope.saveDashboard);
|
||||
$scope.onAppEvent('delete-dashboard', $scope.deleteDashboard);
|
||||
$scope.onAppEvent('quick-snapshot', $scope.quickSnapshot);
|
||||
this.navModel = navModelSrv.getDashboardNav(this.dashboard, this);
|
||||
|
||||
$scope.showSettingsMenu = $scope.dashboardMeta.canEdit || $scope.contextSrv.isEditor;
|
||||
appEvents.on('save-dashboard', this.saveDashboard.bind(this), $scope);
|
||||
appEvents.on('delete-dashboard', this.deleteDashboard.bind(this), $scope);
|
||||
|
||||
if ($scope.dashboardMeta.isSnapshot) {
|
||||
$scope.showSettingsMenu = false;
|
||||
var meta = $scope.dashboardMeta;
|
||||
$scope.titleTooltip = 'Created: ' + moment(meta.created).calendar();
|
||||
if (this.dashboard.meta.isSnapshot) {
|
||||
var meta = this.dashboard.meta;
|
||||
this.titleTooltip = 'Created: ' + moment(meta.created).calendar();
|
||||
if (meta.expires) {
|
||||
$scope.titleTooltip += '<br>Expires: ' + moment(meta.expires).fromNow() + '<br>';
|
||||
this.titleTooltip += '<br>Expires: ' + moment(meta.expires).fromNow() + '<br>';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.openEditView = function(editview) {
|
||||
var search = _.extend($location.search(), {editview: editview});
|
||||
$location.search(search);
|
||||
};
|
||||
openEditView(editview) {
|
||||
var search = _.extend(this.$location.search(), {editview: editview});
|
||||
this.$location.search(search);
|
||||
}
|
||||
|
||||
$scope.showHelpModal = function() {
|
||||
$scope.appEvent('show-modal', {templateHtml: '<help-modal></help-modal>'});
|
||||
};
|
||||
showHelpModal() {
|
||||
appEvents.emit('show-modal', {templateHtml: '<help-modal></help-modal>'});
|
||||
}
|
||||
|
||||
$scope.starDashboard = function() {
|
||||
if ($scope.dashboardMeta.isStarred) {
|
||||
backendSrv.delete('/api/user/stars/dashboard/' + $scope.dashboard.id).then(function() {
|
||||
$scope.dashboardMeta.isStarred = false;
|
||||
});
|
||||
} else {
|
||||
backendSrv.post('/api/user/stars/dashboard/' + $scope.dashboard.id).then(function() {
|
||||
$scope.dashboardMeta.isStarred = true;
|
||||
starDashboard() {
|
||||
if (this.dashboard.meta.isStarred) {
|
||||
return this.backendSrv.delete('/api/user/stars/dashboard/' + this.dashboard.id).then(() => {
|
||||
this.dashboard.meta.isStarred = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$scope.shareDashboard = function(tabIndex) {
|
||||
var modalScope = $scope.$new();
|
||||
this.backendSrv.post('/api/user/stars/dashboard/' + this.dashboard.id).then(() => {
|
||||
this.dashboard.meta.isStarred = true;
|
||||
});
|
||||
}
|
||||
|
||||
shareDashboard(tabIndex) {
|
||||
var modalScope = this.$scope.$new();
|
||||
modalScope.tabIndex = tabIndex;
|
||||
modalScope.dashboard = this.dashboard;
|
||||
|
||||
$scope.appEvent('show-modal', {
|
||||
appEvents.emit('show-modal', {
|
||||
src: 'public/app/features/dashboard/partials/shareModal.html',
|
||||
scope: modalScope
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.quickSnapshot = function() {
|
||||
$scope.shareDashboard(1);
|
||||
};
|
||||
|
||||
$scope.openSearch = function() {
|
||||
$scope.appEvent('show-dash-search');
|
||||
};
|
||||
|
||||
$scope.hideTooltip = function(evt) {
|
||||
hideTooltip(evt) {
|
||||
angular.element(evt.currentTarget).tooltip('hide');
|
||||
$scope.appEvent('hide-dash-search');
|
||||
};
|
||||
}
|
||||
|
||||
$scope.makeEditable = function() {
|
||||
$scope.dashboard.editable = true;
|
||||
makeEditable() {
|
||||
this.dashboard.editable = true;
|
||||
|
||||
return dashboardSrv.saveDashboard({makeEditable: true, overwrite: false}).then(function() {
|
||||
return this.dashboardSrv.saveDashboard({makeEditable: true, overwrite: false}).then(() => {
|
||||
// force refresh whole page
|
||||
window.location.href = window.location.href;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.saveDashboard = function(options) {
|
||||
return dashboardSrv.saveDashboard(options);
|
||||
};
|
||||
exitFullscreen() {
|
||||
this.$rootScope.appEvent('panel-change-view', {fullscreen: false, edit: false});
|
||||
}
|
||||
|
||||
$scope.deleteDashboard = function() {
|
||||
saveDashboard(options) {
|
||||
return this.dashboardSrv.saveDashboard(options);
|
||||
}
|
||||
|
||||
deleteDashboard() {
|
||||
var confirmText = "";
|
||||
var text2 = $scope.dashboard.title;
|
||||
var alerts = $scope.dashboard.rows.reduce((memo, row) => {
|
||||
var text2 = this.dashboard.title;
|
||||
var alerts = this.dashboard.rows.reduce((memo, row) => {
|
||||
memo += row.panels.filter(panel => panel.alert).length;
|
||||
return memo;
|
||||
}, 0);
|
||||
@@ -98,60 +104,37 @@ export class DashNavCtrl {
|
||||
text2 = `This dashboad contains ${alerts} alerts. Deleting this dashboad will also delete those alerts`;
|
||||
}
|
||||
|
||||
$scope.appEvent('confirm-modal', {
|
||||
appEvents.emit('confirm-modal', {
|
||||
title: 'Delete',
|
||||
text: 'Do you want to delete this dashboard?',
|
||||
text2: text2,
|
||||
icon: 'fa-trash',
|
||||
confirmText: confirmText,
|
||||
yesText: 'Delete',
|
||||
onConfirm: function() {
|
||||
$scope.dashboardMeta.canSave = false;
|
||||
$scope.deleteDashboardConfirmed();
|
||||
onConfirm: () => {
|
||||
this.dashboard.meta.canSave = false;
|
||||
this.deleteDashboardConfirmed();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.deleteDashboardConfirmed = function() {
|
||||
backendSrv.delete('/api/dashboards/db/' + $scope.dashboardMeta.slug).then(function() {
|
||||
$scope.appEvent('alert-success', ['Dashboard Deleted', $scope.dashboard.title + ' has been deleted']);
|
||||
$location.url('/');
|
||||
deleteDashboardConfirmed() {
|
||||
this.backendSrv.delete('/api/dashboards/db/' + this.dashboard.meta.slug).then(() => {
|
||||
appEvents.emit('alert-success', ['Dashboard Deleted', this.dashboard.title + ' has been deleted']);
|
||||
this.$location.url('/');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.saveDashboardAs = function() {
|
||||
return dashboardSrv.saveDashboardAs();
|
||||
};
|
||||
saveDashboardAs() {
|
||||
return this.dashboardSrv.saveDashboardAs();
|
||||
}
|
||||
|
||||
$scope.viewJson = function() {
|
||||
var clone = $scope.dashboard.getSaveModelClone();
|
||||
viewJson() {
|
||||
var clone = this.dashboard.getSaveModelClone();
|
||||
var html = angular.toJson(clone, true);
|
||||
var uri = "data:application/json;charset=utf-8," + encodeURIComponent(html);
|
||||
var newWindow = window.open(uri);
|
||||
};
|
||||
|
||||
$scope.snapshot = function() {
|
||||
$scope.dashboard.snapshot = true;
|
||||
$rootScope.$broadcast('refresh');
|
||||
|
||||
$timeout(function() {
|
||||
$scope.dashboard.snapshot = false;
|
||||
$scope.appEvent('dashboard-snapshot-cleanup');
|
||||
}, 1000);
|
||||
|
||||
};
|
||||
|
||||
$scope.editJson = function() {
|
||||
var clone = $scope.dashboard.getSaveModelClone();
|
||||
$scope.appEvent('show-json-editor', { object: clone });
|
||||
};
|
||||
|
||||
$scope.stopPlaylist = function() {
|
||||
playlistSrv.stop(1);
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function dashNavDirective() {
|
||||
@@ -159,7 +142,10 @@ export function dashNavDirective() {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/dashboard/dashnav/dashnav.html',
|
||||
controller: DashNavCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
transclude: true,
|
||||
scope: { dashboard: "=" }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<div class="modal-body">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-header-title">
|
||||
Export CSV
|
||||
</h2>
|
||||
|
||||
<a class="modal-header-close" ng-click="ctrl.dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="modal-content">
|
||||
<div class="p-t-2">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-10">Mode</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input" ng-model="ctrl.asRows" ng-options="f.value as f.text for f in [{text: 'Series as rows', value: true}, {text: 'Series as columns', value: false}]">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-10">Date Time Format</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.dateTimeFormat">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row text-center">
|
||||
<a class="btn btn-success" ng-click="ctrl.export();">Export</a>
|
||||
<a class="btn-text" ng-click="ctrl.dismiss();">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
import * as fileExport from 'app/core/utils/file_export';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
export class ExportDataModalCtrl {
|
||||
private data: any[];
|
||||
asRows: Boolean = true;
|
||||
dateTimeFormat: String = 'YYYY-MM-DDTHH:mm:ssZ';
|
||||
/** @ngInject */
|
||||
constructor(private $scope) { }
|
||||
|
||||
export() {
|
||||
if (this.asRows) {
|
||||
fileExport.exportSeriesListToCsv(this.data, this.dateTimeFormat);
|
||||
} else {
|
||||
fileExport.exportSeriesListToCsvColumns(this.data, this.dateTimeFormat);
|
||||
}
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
appEvents.emit('hide-modal');
|
||||
}
|
||||
}
|
||||
|
||||
export function exportDataModal() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/dashboard/export_data/export_data_modal.html',
|
||||
controller: ExportDataModalCtrl,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
data: '<' // The difference to '=' is that the bound properties are not watched
|
||||
},
|
||||
bindToController: true
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('grafana.directives').directive('exportDataModal', exportDataModal);
|
||||
@@ -1,161 +1,157 @@
|
||||
<div>
|
||||
<div class="tabbed-view-header">
|
||||
<h2 class="tabbed-view-title">
|
||||
Changelog
|
||||
</h2>
|
||||
<div class="tabbed-view-header">
|
||||
<h2 class="tabbed-view-title">
|
||||
Changelog
|
||||
</h2>
|
||||
|
||||
<ul class="gf-tabs">
|
||||
<li class="gf-tabs-item" >
|
||||
<a class="gf-tabs-link" ng-click="ctrl.mode = 'list';" ng-class="{active: ctrl.mode === 'list'}">
|
||||
List
|
||||
</a>
|
||||
</li>
|
||||
<li class="gf-tabs-item" ng-show="ctrl.mode === 'compare'">
|
||||
<span ng-if="ctrl.isOriginalCurrent()" class="active gf-tabs-link">
|
||||
Version {{ctrl.selected[0]}} <i class="fa fa-arrows-h"></i> Current
|
||||
</span>
|
||||
<span ng-if="!ctrl.isOriginalCurrent()" class="active gf-tabs-link">
|
||||
Version {{ctrl.selected[0]}} <i class="fa fa-arrows-h"></i> Version {{ctrl.selected[1]}}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="gf-tabs">
|
||||
<li class="gf-tabs-item" >
|
||||
<a class="gf-tabs-link" ng-click="ctrl.mode = 'list';" ng-class="{active: ctrl.mode === 'list'}">
|
||||
List
|
||||
</a>
|
||||
</li>
|
||||
<li class="gf-tabs-item" ng-show="ctrl.mode === 'compare'">
|
||||
<span ng-if="ctrl.isOriginalCurrent()" class="active gf-tabs-link">
|
||||
Version {{ctrl.selected[0]}} <i class="fa fa-arrows-h"></i> Current
|
||||
</span>
|
||||
<span ng-if="!ctrl.isOriginalCurrent()" class="active gf-tabs-link">
|
||||
Version {{ctrl.selected[0]}} <i class="fa fa-arrows-h"></i> Version {{ctrl.selected[1]}}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="tabbed-view-close-btn" ng-click="dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
<button class="tabbed-view-close-btn" ng-click="ctrl.dismiss();">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="tabbed-view-body">
|
||||
<div class="tabbed-view-body">
|
||||
|
||||
<div ng-if="ctrl.mode === 'list'">
|
||||
<div ng-if="ctrl.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<em>Fetching history list…</em>
|
||||
</div>
|
||||
<div ng-if="ctrl.mode === 'list'">
|
||||
<div ng-if="ctrl.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<em>Fetching history list…</em>
|
||||
</div>
|
||||
|
||||
<div ng-if="!ctrl.loading">
|
||||
<div class="history-table gf-form">
|
||||
<div class="gf-form-group">
|
||||
<table class="filter-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="width-4"></th>
|
||||
<th class="width-4">Version</th>
|
||||
<th class="width-14">Date</th>
|
||||
<th class="width-10">Updated By</th>
|
||||
<th class="width-30">Notes</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="revision in ctrl.revisions">
|
||||
<td bs-tooltip="ctrl.compareRevisionDisabled(revision.checked) ? 'You can only compare 2 versions at a time' : ''">
|
||||
<gf-form-switch
|
||||
checked="revision.checked"
|
||||
on-change="ctrl.compareRevisionStateChanged(revision)"
|
||||
ng-disabled="ctrl.compareRevisionDisabled(revision.checked)">
|
||||
</gf-form-switch>
|
||||
</td>
|
||||
<td>{{revision.version}}</td>
|
||||
<td>{{ctrl.formatDate(revision.created)}}</td>
|
||||
<td>{{revision.createdBy}}</td>
|
||||
<td>{{revision.message}}</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-inverse btn-small" ng-show="revision.version !== ctrl.dashboard.version" ng-click="ctrl.restore(revision.version)">
|
||||
<i class="fa fa-rotate-right"></i> Restore
|
||||
</a>
|
||||
<a class="btn btn-outline-disabled btn-small" ng-show="revision.version === ctrl.dashboard.version">
|
||||
<i class="fa fa-check"></i> Current
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="!ctrl.loading">
|
||||
<div class="history-table gf-form">
|
||||
<div class="gf-form-group">
|
||||
<table class="filter-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="width-4"></th>
|
||||
<th class="width-4">Version</th>
|
||||
<th class="width-14">Date</th>
|
||||
<th class="width-10">Updated By</th>
|
||||
<th class="width-30">Notes</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="revision in ctrl.revisions">
|
||||
<td bs-tooltip="ctrl.compareRevisionDisabled(revision.checked) ? 'You can only compare 2 versions at a time' : ''">
|
||||
<gf-form-switch checked="revision.checked"
|
||||
on-change="ctrl.compareRevisionStateChanged(revision)"
|
||||
ng-disabled="ctrl.compareRevisionDisabled(revision.checked)">
|
||||
</gf-form-switch>
|
||||
</td>
|
||||
<td>{{revision.version}}</td>
|
||||
<td>{{ctrl.formatDate(revision.created)}}</td>
|
||||
<td>{{revision.createdBy}}</td>
|
||||
<td>{{revision.message}}</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-inverse btn-small" ng-show="revision.version !== ctrl.dashboard.version" ng-click="ctrl.restore(revision.version)">
|
||||
<i class="fa fa-rotate-right"></i> Restore
|
||||
</a>
|
||||
<a class="btn btn-outline-disabled btn-small" ng-show="revision.version === ctrl.dashboard.version">
|
||||
<i class="fa fa-check"></i> Current
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div ng-if="ctrl.appending">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<em>Fetching more entries…</em>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group" ng-show="ctrl.mode === 'list'">
|
||||
<div class="gf-form-button-row">
|
||||
<a type="button"
|
||||
class="btn gf-form-button btn-primary"
|
||||
ng-if="ctrl.revisions.length > 1"
|
||||
ng-class="{disabled: !ctrl.isComparable()}"
|
||||
ng-click="ctrl.getDiff(ctrl.diff)"
|
||||
bs-tooltip="ctrl.isComparable() ? '' : 'Select 2 versions to start comparing'">
|
||||
<i class="fa fa-code-fork" ></i> Compare versions
|
||||
</a>
|
||||
<a type="button"
|
||||
class="btn gf-form-button btn-inverse"
|
||||
ng-if="ctrl.revisions.length >= ctrl.limit"
|
||||
ng-click="ctrl.addToLog()"
|
||||
ng-class="{disabled: ctrl.isLastPage()}"
|
||||
ng-disabled="ctrl.isLastPage()">
|
||||
Show more versions
|
||||
</a>
|
||||
</div>
|
||||
<div class="gf-form-group" ng-show="ctrl.mode === 'list'">
|
||||
<div class="gf-form-button-row">
|
||||
<a type="button"
|
||||
class="btn gf-form-button btn-primary"
|
||||
ng-if="ctrl.revisions.length > 1"
|
||||
ng-class="{disabled: !ctrl.isComparable()}"
|
||||
ng-click="ctrl.getDiff(ctrl.diff)"
|
||||
bs-tooltip="ctrl.isComparable() ? '' : 'Select 2 versions to start comparing'">
|
||||
<i class="fa fa-code-fork" ></i> Compare versions
|
||||
</a>
|
||||
<a type="button"
|
||||
class="btn gf-form-button btn-inverse"
|
||||
ng-if="ctrl.revisions.length >= ctrl.limit"
|
||||
ng-click="ctrl.addToLog()"
|
||||
ng-class="{disabled: ctrl.isLastPage()}"
|
||||
ng-disabled="ctrl.isLastPage()">
|
||||
Show more versions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="history-list" ng-if="ctrl.mode === 'compare'">
|
||||
<div class="page-container">
|
||||
<div class="page-body">
|
||||
<aside class="page-sidebar">
|
||||
<section class="page-sidebar-section">
|
||||
<ul class="ui-list">
|
||||
<li><a ng-class="{active: ctrl.diff === 'basic'}" ng-click="ctrl.getDiff('basic')" href="">Change Summary</a></li>
|
||||
<li><a ng-class="{active: ctrl.diff === 'html'}" ng-click="ctrl.getDiff('html')" href="">JSON Code View</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</aside>
|
||||
<div class="history-list" ng-if="ctrl.mode === 'compare'">
|
||||
<div class="page-container">
|
||||
<div class="page-body">
|
||||
<aside class="page-sidebar">
|
||||
<section class="page-sidebar-section">
|
||||
<ul class="ui-list">
|
||||
<li><a ng-class="{active: ctrl.diff === 'basic'}" ng-click="ctrl.getDiff('basic')" href="">Change Summary</a></li>
|
||||
<li><a ng-class="{active: ctrl.diff === 'html'}" ng-click="ctrl.getDiff('html')" href="">JSON Code View</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<div class="tab-content page-content-with-sidebar">
|
||||
<div ng-if="ctrl.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<em>Fetching changes…</em>
|
||||
</div>
|
||||
<div class="tab-content page-content-with-sidebar">
|
||||
<div ng-if="ctrl.loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<em>Fetching changes…</em>
|
||||
</div>
|
||||
|
||||
<div ng-if="!ctrl.loading" ng-init="new = ctrl.selected[0]; original = ctrl.selected[1]">
|
||||
<a type="button"
|
||||
<div ng-if="!ctrl.loading" ng-init="new = ctrl.selected[0]; original = ctrl.selected[1]">
|
||||
<a type="button"
|
||||
class="btn gf-form-button btn-primary diff-restore-btn"
|
||||
ng-click="ctrl.restore(new)"
|
||||
ng-if="ctrl.isOriginalCurrent()">
|
||||
<i class="fa fa-rotate-right" ></i> Restore to version {{new}}
|
||||
</a>
|
||||
<h4>
|
||||
Comparing Version {{ctrl.selected[0]}}
|
||||
<i class="fa fa-arrows-h"></i>
|
||||
Version {{ctrl.selected[1]}}
|
||||
<cite class="muted" ng-if="ctrl.isOriginalCurrent()">(Current)</cite>
|
||||
</h4>
|
||||
<section ng-if="ctrl.diff === 'basic'">
|
||||
<p class="small muted">
|
||||
<strong>Version {{new}}</strong> updated by
|
||||
<span>{{ctrl.getMeta(new, 'createdBy')}} </span>
|
||||
<span>{{ctrl.formatBasicDate(ctrl.getMeta(new, 'created'))}}</span>
|
||||
<span> - {{ctrl.getMeta(new, 'message')}}</span>
|
||||
</p>
|
||||
<p class="small muted">
|
||||
<strong>Version {{original}}</strong> updated by
|
||||
<span>{{ctrl.getMeta(original, 'createdBy')}} </span>
|
||||
<span>{{ctrl.formatBasicDate(ctrl.getMeta(original, 'created'))}}</span>
|
||||
<span> - {{ctrl.getMeta(original, 'message')}}</span>
|
||||
</p>
|
||||
</section>
|
||||
<div id="delta" diff-delta>
|
||||
<div class="delta-basic" ng-show="ctrl.diff === 'basic'" compile="ctrl.delta.basic"></div>
|
||||
<div class="delta-html" ng-show="ctrl.diff === 'html'" compile="ctrl.delta.html"></div>
|
||||
</div>
|
||||
<i class="fa fa-rotate-right" ></i> Restore to version {{new}}
|
||||
</a>
|
||||
<h4>
|
||||
Comparing Version {{ctrl.selected[0]}}
|
||||
<i class="fa fa-arrows-h"></i>
|
||||
Version {{ctrl.selected[1]}}
|
||||
<cite class="muted" ng-if="ctrl.isOriginalCurrent()">(Current)</cite>
|
||||
</h4>
|
||||
<section ng-if="ctrl.diff === 'basic'">
|
||||
<p class="small muted">
|
||||
<strong>Version {{new}}</strong> updated by
|
||||
<span>{{ctrl.getMeta(new, 'createdBy')}} </span>
|
||||
<span>{{ctrl.formatBasicDate(ctrl.getMeta(new, 'created'))}}</span>
|
||||
<span> - {{ctrl.getMeta(new, 'message')}}</span>
|
||||
</p>
|
||||
<p class="small muted">
|
||||
<strong>Version {{original}}</strong> updated by
|
||||
<span>{{ctrl.getMeta(original, 'createdBy')}} </span>
|
||||
<span>{{ctrl.formatBasicDate(ctrl.getMeta(original, 'created'))}}</span>
|
||||
<span> - {{ctrl.getMeta(original, 'message')}}</span>
|
||||
</p>
|
||||
</section>
|
||||
<div id="delta" diff-delta>
|
||||
<div class="delta-basic" ng-show="ctrl.diff === 'basic'" compile="ctrl.delta.basic"></div>
|
||||
<div class="delta-html" ng-show="ctrl.diff === 'html'" compile="ctrl.delta.html"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -54,6 +54,10 @@ export class HistoryListCtrl {
|
||||
$rootScope.onAppEvent('dashboard-saved', this.onDashboardSaved.bind(this));
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.$rootScope.appEvent('hide-dash-editor');
|
||||
}
|
||||
|
||||
addToLog() {
|
||||
this.start = this.start + this.limit;
|
||||
this.getLog(true);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<gf-dashboard-history dashboard="dashboard"></gf-dashboard-history>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-header-title">
|
||||
<i class="fa fa-save"></i>
|
||||
<span class="p-l-1">Save to changelog</span>
|
||||
<span class="p-l-1">Save Dashboard</span>
|
||||
</h2>
|
||||
|
||||
<a class="modal-header-close" ng-click="dismiss();">
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
<div class="gf-form-button-row text-center">
|
||||
<button type="submit" class="btn btn-success" ng-disabled="saveMessage.$invalid">
|
||||
Save to changelog
|
||||
Save
|
||||
</button>
|
||||
<button class="btn btn-inverse" ng-click="dismiss();">Cancel</button>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,6 @@ export class TimePickerCtrl {
|
||||
$rootScope.onAppEvent('shift-time-backward', () => this.move(-1), $scope);
|
||||
$rootScope.onAppEvent('refresh', () => this.init(), $scope);
|
||||
$rootScope.onAppEvent('dash-editor-hidden', () => this.isOpen = false, $scope);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
@@ -114,7 +113,7 @@ export class TimePickerCtrl {
|
||||
this.refresh.options.unshift({text: 'off'});
|
||||
|
||||
this.$rootScope.appEvent('show-dash-editor', {
|
||||
src: 'public/app/features/dashboard/timepicker/dropdown.html',
|
||||
editview: 'timepicker',
|
||||
scope: this.$scope,
|
||||
cssClass: 'gf-timepicker-dropdown',
|
||||
});
|
||||
|
||||
@@ -20,12 +20,6 @@ function (angular, _, $, config) {
|
||||
self.$scope = $scope;
|
||||
self.dashboard = $scope.dashboard;
|
||||
|
||||
$scope.exitFullscreen = function() {
|
||||
if (self.state.fullscreen) {
|
||||
self.update({ fullscreen: false });
|
||||
}
|
||||
};
|
||||
|
||||
$scope.onAppEvent('$routeUpdate', function() {
|
||||
var urlState = self.getQueryStringState();
|
||||
if (self.needsSync(urlState)) {
|
||||
@@ -41,6 +35,9 @@ function (angular, _, $, config) {
|
||||
self.registerPanel(payload.scope);
|
||||
});
|
||||
|
||||
// this marks changes to location during this digest cycle as not to add history item
|
||||
// dont want url changes like adding orgId to add browser history
|
||||
$location.replace();
|
||||
this.update(this.getQueryStringState());
|
||||
this.expandRowForPanel();
|
||||
}
|
||||
|
||||
@@ -7,11 +7,12 @@ function (angular, config) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('ChangePasswordCtrl', function($scope, backendSrv, $location) {
|
||||
module.controller('ChangePasswordCtrl', function($scope, backendSrv, $location, navModelSrv) {
|
||||
|
||||
$scope.command = {};
|
||||
$scope.authProxyEnabled = config.authProxyEnabled;
|
||||
$scope.ldapEnabled = config.ldapEnabled;
|
||||
$scope.navModel = navModelSrv.getProfileNav();
|
||||
|
||||
$scope.changePassword = function() {
|
||||
if (!$scope.userForm.$valid) { return; }
|
||||
|
||||
@@ -7,8 +7,9 @@ function (angular, config) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('NewOrgCtrl', function($scope, $http, backendSrv) {
|
||||
module.controller('NewOrgCtrl', function($scope, $http, backendSrv, navModelSrv) {
|
||||
|
||||
$scope.navModel = navModelSrv.getOrgNav(0);
|
||||
$scope.newOrg = {name: ''};
|
||||
|
||||
$scope.createOrg = function() {
|
||||
|
||||
@@ -6,8 +6,9 @@ function (angular) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('OrgApiKeysCtrl', function($scope, $http, backendSrv) {
|
||||
module.controller('OrgApiKeysCtrl', function($scope, $http, backendSrv, navModelSrv) {
|
||||
|
||||
$scope.navModel = navModelSrv.getOrgNav(0);
|
||||
$scope.roleTypes = ['Viewer', 'Editor', 'Admin'];
|
||||
$scope.token = { role: 'Viewer' };
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ function (angular) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('OrgDetailsCtrl', function($scope, $http, backendSrv, contextSrv) {
|
||||
module.controller('OrgDetailsCtrl', function($scope, $http, backendSrv, contextSrv, navModelSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.getOrgInfo();
|
||||
$scope.navModel = navModelSrv.getOrgNav(0);
|
||||
};
|
||||
|
||||
$scope.getOrgInfo = function() {
|
||||
|
||||
@@ -11,13 +11,15 @@ export class OrgUsersCtrl {
|
||||
pendingInvites: any;
|
||||
editor: any;
|
||||
showInviteUI: boolean;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private $http, private backendSrv) {
|
||||
constructor(private $scope, private $http, private backendSrv, navModelSrv) {
|
||||
this.user = {
|
||||
loginOrEmail: '',
|
||||
role: 'Viewer',
|
||||
};
|
||||
this.navModel = navModelSrv.getOrgNav(0);
|
||||
|
||||
this.get();
|
||||
this.editor = { index: 0 };
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-users" title="Profile" title-url="profile">
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar title="Organization" icon="icon-gf icon-gf-users">
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container" ng-form="playlistEditForm">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-users" title="Organization" title-url="org">
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-users" title="Organization" title-url="org">
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-users" title="Organization Users" title-url="org/users">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<navbar icon="icon-gf icon-gf-users" title="Profile" title-url="profile">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
<h1>Profile</h1>
|
||||
<h1>User Profile</h1>
|
||||
</div>
|
||||
|
||||
<form name="ctrl.userForm" class="gf-form-group">
|
||||
|
||||
@@ -11,11 +11,13 @@ export class ProfileCtrl {
|
||||
userForm: any;
|
||||
showOrgsList = false;
|
||||
readonlyLoginFields = config.disableLoginForm;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject **/
|
||||
constructor(private backendSrv, private contextSrv, private $location) {
|
||||
constructor(private backendSrv, private contextSrv, private $location, navModelSrv) {
|
||||
this.getUser();
|
||||
this.getUserOrgs();
|
||||
this.navModel = navModelSrv.getProfileNav();
|
||||
}
|
||||
|
||||
getUser() {
|
||||
|
||||
@@ -35,8 +35,6 @@ export class PanelCtrl {
|
||||
containerHeight: any;
|
||||
events: Emitter;
|
||||
timing: any;
|
||||
skippedLastRefresh: boolean;
|
||||
isPanelVisible: any;
|
||||
|
||||
constructor($scope, $injector) {
|
||||
this.$injector = $injector;
|
||||
@@ -77,13 +75,6 @@ export class PanelCtrl {
|
||||
}
|
||||
|
||||
refresh() {
|
||||
if (!this.isPanelVisible() && !this.dashboard.meta.soloMode && !this.dashboard.snapshot) {
|
||||
this.skippedLastRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.skippedLastRefresh = false;
|
||||
|
||||
this.events.emit('refresh', null);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import Drop from 'tether-drop';
|
||||
import {appEvents} from 'app/core/core';
|
||||
|
||||
var module = angular.module('grafana.directives');
|
||||
|
||||
@@ -185,23 +186,9 @@ module.directive('grafanaPanel', function($rootScope, $document) {
|
||||
elem.on('mouseenter', mouseEnter);
|
||||
elem.on('mouseleave', mouseLeave);
|
||||
|
||||
ctrl.isPanelVisible = function () {
|
||||
var position = panelContainer[0].getBoundingClientRect();
|
||||
return (0 < position.top) && (position.top < window.innerHeight);
|
||||
};
|
||||
|
||||
const refreshOnScroll = function () {
|
||||
if (ctrl.skippedLastRefresh) {
|
||||
ctrl.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
$document.on('scroll', refreshOnScroll);
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
elem.off();
|
||||
cornerInfoElem.off();
|
||||
$document.off('scroll', refreshOnScroll);
|
||||
|
||||
if (infoDrop) {
|
||||
infoDrop.destroy();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-list" title="Playlists" title-url="playlists">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container" ng-form="playlistEditForm">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-list" title="Playlists" title-url="playlists">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -16,21 +16,30 @@ export class PlaylistEditCtrl {
|
||||
playlistItems: any = [];
|
||||
dashboardresult: any = [];
|
||||
tagresult: any = [];
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private playlistSrv, private backendSrv, private $location, private $route) {
|
||||
constructor(
|
||||
private $scope,
|
||||
private playlistSrv,
|
||||
private backendSrv,
|
||||
private $location,
|
||||
private $route,
|
||||
private navModelSrv
|
||||
) {
|
||||
|
||||
this.navModel = navModelSrv.getPlaylistsNav(0);
|
||||
|
||||
if ($route.current.params.id) {
|
||||
var playlistId = $route.current.params.id;
|
||||
|
||||
backendSrv.get('/api/playlists/' + playlistId)
|
||||
.then((result) => {
|
||||
this.playlist = result;
|
||||
});
|
||||
backendSrv.get('/api/playlists/' + playlistId).then(result => {
|
||||
this.playlist = result;
|
||||
});
|
||||
|
||||
backendSrv.get('/api/playlists/' + playlistId + '/items')
|
||||
.then((result) => {
|
||||
this.playlistItems = result;
|
||||
});
|
||||
backendSrv.get('/api/playlists/' + playlistId + '/items').then(result => {
|
||||
this.playlistItems = result;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +94,7 @@ export class PlaylistEditCtrl {
|
||||
? this.backendSrv.put('/api/playlists/' + playlist.id, playlist)
|
||||
: this.backendSrv.post('/api/playlists', playlist);
|
||||
|
||||
savePromise
|
||||
savePromise
|
||||
.then(() => {
|
||||
this.$scope.appEvent('alert-success', ['Playlist saved', '']);
|
||||
this.$location.path('/playlists');
|
||||
|
||||
@@ -11,6 +11,7 @@ class PlaylistSrv {
|
||||
private interval: any;
|
||||
private playlistId: number;
|
||||
private startUrl: string;
|
||||
public isPlaying: boolean;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $rootScope: any, private $location: any, private $timeout: any, private backendSrv: any) { }
|
||||
@@ -42,7 +43,7 @@ class PlaylistSrv {
|
||||
this.startUrl = window.location.href;
|
||||
this.index = 0;
|
||||
this.playlistId = playlistId;
|
||||
this.$rootScope.playlistSrv = this;
|
||||
this.isPlaying = true;
|
||||
|
||||
this.backendSrv.get(`/api/playlists/${playlistId}`).then(playlist => {
|
||||
this.backendSrv.get(`/api/playlists/${playlistId}/dashboards`).then(dashboards => {
|
||||
@@ -55,13 +56,12 @@ class PlaylistSrv {
|
||||
|
||||
stop() {
|
||||
this.index = 0;
|
||||
this.isPlaying = false;
|
||||
this.playlistId = 0;
|
||||
|
||||
if (this.cancelPromise) {
|
||||
this.$timeout.cancel(this.cancelPromise);
|
||||
}
|
||||
|
||||
this.$rootScope.playlistSrv = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,20 +6,22 @@ import coreModule from '../../core/core_module';
|
||||
|
||||
export class PlaylistsCtrl {
|
||||
playlists: any;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private $location, private backendSrv) {
|
||||
backendSrv.get('/api/playlists')
|
||||
.then((result) => {
|
||||
this.playlists = result;
|
||||
});
|
||||
constructor(private $scope, private $location, private backendSrv, private navModelSrv) {
|
||||
this.navModel = navModelSrv.getPlaylistsNav(0);
|
||||
|
||||
backendSrv.get('/api/playlists').then(result => {
|
||||
this.playlists = result;
|
||||
});
|
||||
}
|
||||
|
||||
removePlaylistConfirmed(playlist) {
|
||||
_.remove(this.playlists, { id: playlist.id });
|
||||
|
||||
this.backendSrv.delete('/api/playlists/' + playlist.id)
|
||||
.then(() => {
|
||||
.then(() => {
|
||||
this.$scope.appEvent('alert-success', ['Playlist deleted', '']);
|
||||
}, () => {
|
||||
this.$scope.appEvent('alert-error', ['Unable to delete playlist', '']);
|
||||
|
||||
@@ -5,7 +5,11 @@ import {PlaylistEditCtrl} from '../playlist_edit_ctrl';
|
||||
describe('PlaylistEditCtrl', () => {
|
||||
var ctx: any;
|
||||
beforeEach(() => {
|
||||
ctx = new PlaylistEditCtrl(null, null, null, null, { current: { params: {} } });
|
||||
let navModelSrv = {
|
||||
getPlaylistsNav: page => {},
|
||||
};
|
||||
|
||||
ctx = new PlaylistEditCtrl(null, null, null, null, { current: { params: {} } }, navModelSrv);
|
||||
|
||||
ctx.dashboardresult = [
|
||||
{ id: 2, title: 'dashboard: 2' },
|
||||
|
||||
@@ -30,6 +30,7 @@ export class DataSourceEditCtrl {
|
||||
hasDashboards: boolean;
|
||||
editForm: any;
|
||||
gettingStarted: boolean;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(
|
||||
@@ -38,141 +39,144 @@ export class DataSourceEditCtrl {
|
||||
private backendSrv,
|
||||
private $routeParams,
|
||||
private $location,
|
||||
private datasourceSrv) {
|
||||
private datasourceSrv,
|
||||
private navModelSrv,
|
||||
) {
|
||||
|
||||
this.isNew = true;
|
||||
this.datasources = [];
|
||||
this.tabIndex = 0;
|
||||
this.navModel = navModelSrv.getDatasourceNav(0);
|
||||
this.isNew = true;
|
||||
this.datasources = [];
|
||||
this.tabIndex = 0;
|
||||
|
||||
this.loadDatasourceTypes().then(() => {
|
||||
if (this.$routeParams.id) {
|
||||
this.getDatasourceById(this.$routeParams.id);
|
||||
} else {
|
||||
this.initNewDatasourceModel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initNewDatasourceModel() {
|
||||
this.current = angular.copy(defaults);
|
||||
|
||||
// We are coming from getting started
|
||||
if (this.$location.search().gettingstarted) {
|
||||
this.gettingStarted = true;
|
||||
this.current.isDefault = true;
|
||||
this.loadDatasourceTypes().then(() => {
|
||||
if (this.$routeParams.id) {
|
||||
this.getDatasourceById(this.$routeParams.id);
|
||||
} else {
|
||||
this.initNewDatasourceModel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.typeChanged();
|
||||
initNewDatasourceModel() {
|
||||
this.current = angular.copy(defaults);
|
||||
|
||||
// We are coming from getting started
|
||||
if (this.$location.search().gettingstarted) {
|
||||
this.gettingStarted = true;
|
||||
this.current.isDefault = true;
|
||||
}
|
||||
|
||||
loadDatasourceTypes() {
|
||||
if (datasourceTypes.length > 0) {
|
||||
this.types = datasourceTypes;
|
||||
return this.$q.when(null);
|
||||
this.typeChanged();
|
||||
}
|
||||
|
||||
loadDatasourceTypes() {
|
||||
if (datasourceTypes.length > 0) {
|
||||
this.types = datasourceTypes;
|
||||
return this.$q.when(null);
|
||||
}
|
||||
|
||||
return this.backendSrv.get('/api/plugins', {enabled: 1, type: 'datasource'}).then(plugins => {
|
||||
datasourceTypes = plugins;
|
||||
this.types = plugins;
|
||||
});
|
||||
}
|
||||
|
||||
getDatasourceById(id) {
|
||||
this.backendSrv.get('/api/datasources/' + id).then(ds => {
|
||||
this.isNew = false;
|
||||
this.current = ds;
|
||||
if (datasourceCreated) {
|
||||
datasourceCreated = false;
|
||||
this.testDatasource();
|
||||
}
|
||||
return this.typeChanged();
|
||||
});
|
||||
}
|
||||
|
||||
return this.backendSrv.get('/api/plugins', {enabled: 1, type: 'datasource'}).then(plugins => {
|
||||
datasourceTypes = plugins;
|
||||
this.types = plugins;
|
||||
});
|
||||
}
|
||||
typeChanged() {
|
||||
this.hasDashboards = false;
|
||||
return this.backendSrv.get('/api/plugins/' + this.current.type + '/settings').then(pluginInfo => {
|
||||
this.datasourceMeta = pluginInfo;
|
||||
console.log(this.datasourceMeta) ;
|
||||
this.hasDashboards = _.find(pluginInfo.includes, {type: 'dashboard'});
|
||||
});
|
||||
}
|
||||
|
||||
getDatasourceById(id) {
|
||||
this.backendSrv.get('/api/datasources/' + id).then(ds => {
|
||||
this.isNew = false;
|
||||
this.current = ds;
|
||||
if (datasourceCreated) {
|
||||
datasourceCreated = false;
|
||||
this.testDatasource();
|
||||
}
|
||||
return this.typeChanged();
|
||||
});
|
||||
}
|
||||
updateFrontendSettings() {
|
||||
return this.backendSrv.get('/api/frontend/settings').then(settings => {
|
||||
config.datasources = settings.datasources;
|
||||
config.defaultDatasource = settings.defaultDatasource;
|
||||
this.datasourceSrv.init();
|
||||
});
|
||||
}
|
||||
|
||||
typeChanged() {
|
||||
this.hasDashboards = false;
|
||||
return this.backendSrv.get('/api/plugins/' + this.current.type + '/settings').then(pluginInfo => {
|
||||
this.datasourceMeta = pluginInfo;
|
||||
console.log(this.datasourceMeta) ;
|
||||
this.hasDashboards = _.find(pluginInfo.includes, {type: 'dashboard'});
|
||||
});
|
||||
}
|
||||
testDatasource() {
|
||||
this.testing = { done: false };
|
||||
|
||||
updateFrontendSettings() {
|
||||
return this.backendSrv.get('/api/frontend/settings').then(settings => {
|
||||
config.datasources = settings.datasources;
|
||||
config.defaultDatasource = settings.defaultDatasource;
|
||||
this.datasourceSrv.init();
|
||||
});
|
||||
}
|
||||
|
||||
testDatasource() {
|
||||
this.testing = { done: false };
|
||||
|
||||
this.datasourceSrv.get(this.current.name).then(datasource => {
|
||||
if (!datasource.testDatasource) {
|
||||
delete this.testing;
|
||||
return;
|
||||
}
|
||||
|
||||
return datasource.testDatasource().then(result => {
|
||||
this.testing.message = result.message;
|
||||
this.testing.status = result.status;
|
||||
this.testing.title = result.title;
|
||||
}).catch(err => {
|
||||
if (err.statusText) {
|
||||
this.testing.message = err.statusText;
|
||||
this.testing.title = "HTTP Error";
|
||||
} else {
|
||||
this.testing.message = err.message;
|
||||
this.testing.title = "Unknown error";
|
||||
}
|
||||
});
|
||||
}).finally(() => {
|
||||
if (this.testing) {
|
||||
this.testing.done = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveChanges() {
|
||||
if (!this.editForm.$valid) {
|
||||
this.datasourceSrv.get(this.current.name).then(datasource => {
|
||||
if (!datasource.testDatasource) {
|
||||
delete this.testing;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.current.id) {
|
||||
return this.backendSrv.put('/api/datasources/' + this.current.id, this.current).then(() => {
|
||||
this.updateFrontendSettings().then(() => {
|
||||
this.testDatasource();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return this.backendSrv.post('/api/datasources', this.current).then(result => {
|
||||
this.updateFrontendSettings();
|
||||
|
||||
datasourceCreated = true;
|
||||
this.$location.path('datasources/edit/' + result.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
confirmDelete() {
|
||||
this.backendSrv.delete('/api/datasources/' + this.current.id).then(() => {
|
||||
this.$location.path('datasources');
|
||||
});
|
||||
}
|
||||
|
||||
delete(s) {
|
||||
appEvents.emit('confirm-modal', {
|
||||
title: 'Delete',
|
||||
text: 'Are you sure you want to delete this datasource?',
|
||||
yesText: "Delete",
|
||||
icon: "fa-trash",
|
||||
onConfirm: () => {
|
||||
this.confirmDelete();
|
||||
return datasource.testDatasource().then(result => {
|
||||
this.testing.message = result.message;
|
||||
this.testing.status = result.status;
|
||||
this.testing.title = result.title;
|
||||
}).catch(err => {
|
||||
if (err.statusText) {
|
||||
this.testing.message = err.statusText;
|
||||
this.testing.title = "HTTP Error";
|
||||
} else {
|
||||
this.testing.message = err.message;
|
||||
this.testing.title = "Unknown error";
|
||||
}
|
||||
});
|
||||
}).finally(() => {
|
||||
if (this.testing) {
|
||||
this.testing.done = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveChanges() {
|
||||
if (!this.editForm.$valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.current.id) {
|
||||
return this.backendSrv.put('/api/datasources/' + this.current.id, this.current).then(() => {
|
||||
this.updateFrontendSettings().then(() => {
|
||||
this.testDatasource();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return this.backendSrv.post('/api/datasources', this.current).then(result => {
|
||||
this.updateFrontendSettings();
|
||||
|
||||
datasourceCreated = true;
|
||||
this.$location.path('datasources/edit/' + result.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
confirmDelete() {
|
||||
this.backendSrv.delete('/api/datasources/' + this.current.id).then(() => {
|
||||
this.$location.path('datasources');
|
||||
});
|
||||
}
|
||||
|
||||
delete(s) {
|
||||
appEvents.emit('confirm-modal', {
|
||||
title: 'Delete',
|
||||
text: 'Are you sure you want to delete this datasource?',
|
||||
yesText: "Delete",
|
||||
icon: "fa-trash",
|
||||
onConfirm: () => {
|
||||
this.confirmDelete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.controller('DataSourceEditCtrl', DataSourceEditCtrl);
|
||||
|
||||
@@ -6,31 +6,41 @@ import coreModule from '../../core/core_module';
|
||||
|
||||
export class DataSourcesCtrl {
|
||||
datasources: any;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private $location, private $http, private backendSrv, private datasourceSrv) {
|
||||
backendSrv.get('/api/datasources')
|
||||
.then((result) => {
|
||||
this.datasources = result;
|
||||
});
|
||||
constructor(
|
||||
private $scope,
|
||||
private $location,
|
||||
private $http,
|
||||
private backendSrv,
|
||||
private datasourceSrv,
|
||||
private navModelSrv
|
||||
) {
|
||||
|
||||
this.navModel = this.navModelSrv.getDatasourceNav(0);
|
||||
|
||||
backendSrv.get('/api/datasources').then(result => {
|
||||
this.datasources = result;
|
||||
});
|
||||
}
|
||||
|
||||
removeDataSourceConfirmed(ds) {
|
||||
|
||||
this.backendSrv.delete('/api/datasources/' + ds.id)
|
||||
.then(() => {
|
||||
this.$scope.appEvent('alert-success', ['Datasource deleted', '']);
|
||||
}, () => {
|
||||
this.$scope.appEvent('alert-error', ['Unable to delete datasource', '']);
|
||||
}).then(() => {
|
||||
.then(() => {
|
||||
this.$scope.appEvent('alert-success', ['Datasource deleted', '']);
|
||||
}, () => {
|
||||
this.$scope.appEvent('alert-error', ['Unable to delete datasource', '']);
|
||||
}).then(() => {
|
||||
this.backendSrv.get('/api/datasources')
|
||||
.then((result) => {
|
||||
this.datasources = result;
|
||||
});
|
||||
.then((result) => {
|
||||
this.datasources = result;
|
||||
});
|
||||
this.backendSrv.get('/api/frontend/settings')
|
||||
.then((settings) => {
|
||||
this.datasourceSrv.init(settings.datasources);
|
||||
});
|
||||
.then((settings) => {
|
||||
this.datasourceSrv.init(settings.datasources);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar title="Data Sources" title-url="datasources" icon="icon-gf icon-gf-datasources">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
<navbar
|
||||
title="Data Sources"
|
||||
title-url="datasources"
|
||||
icon="icon-gf icon-gf-datasources">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar title="Plugins" title-url="plugins" icon="icon-gf icon-gf-apps">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container" ng-init="ctrl.init()">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar title="Plugins" icon="icon-gf icon-gf-apps" title-url="plugins">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon-url="{{ctrl.appLogoUrl}}" title="{{ctrl.appModel.name}}" title-url="{{ctrl.appModel.defaultNavUrl}}">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel" ng-if="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container" >
|
||||
<div ng-if="ctrl.page">
|
||||
|
||||
@@ -13,17 +13,22 @@ export class PluginEditCtrl {
|
||||
includedDatasources: any;
|
||||
tabIndex: number;
|
||||
tabs: any;
|
||||
navModel: any;
|
||||
hasDashboards: any;
|
||||
preUpdateHook: () => any;
|
||||
postUpdateHook: () => any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope,
|
||||
private $rootScope,
|
||||
private backendSrv,
|
||||
private $routeParams,
|
||||
private $sce,
|
||||
private $http) {
|
||||
constructor(
|
||||
private $scope,
|
||||
private $rootScope,
|
||||
private backendSrv,
|
||||
private $routeParams,
|
||||
private $sce,
|
||||
private $http,
|
||||
private navModelSrv,
|
||||
) {
|
||||
this.navModel = navModelSrv.getPluginsNav();
|
||||
this.model = {};
|
||||
this.pluginId = $routeParams.pluginId;
|
||||
this.tabIndex = 0;
|
||||
@@ -31,7 +36,7 @@ export class PluginEditCtrl {
|
||||
|
||||
this.preUpdateHook = () => Promise.resolve();
|
||||
this.postUpdateHook = () => Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
return this.backendSrv.get(`/api/plugins/${this.pluginId}/settings`).then(result => {
|
||||
|
||||
@@ -5,10 +5,12 @@ import angular from 'angular';
|
||||
export class PluginListCtrl {
|
||||
plugins: any[];
|
||||
tabIndex: number;
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv: any, $location) {
|
||||
constructor(private backendSrv: any, $location, navModelSrv) {
|
||||
this.tabIndex = 0;
|
||||
this.navModel = navModelSrv.getPluginsNav();
|
||||
|
||||
var pluginType = $location.search().type || 'panel';
|
||||
switch (pluginType) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import {NavModel} from 'app/core/core';
|
||||
|
||||
var pluginInfoCache = {};
|
||||
|
||||
@@ -9,7 +10,7 @@ export class AppPageCtrl {
|
||||
page: any;
|
||||
pluginId: any;
|
||||
appModel: any;
|
||||
appLogoUrl: any;
|
||||
navModel: NavModel;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv, private $routeParams: any, private $rootScope) {
|
||||
@@ -25,13 +26,53 @@ export class AppPageCtrl {
|
||||
initPage(app) {
|
||||
this.appModel = app;
|
||||
this.page = _.find(app.includes, {slug: this.$routeParams.slug});
|
||||
this.appLogoUrl = app.info.logos.small;
|
||||
|
||||
pluginInfoCache[this.pluginId] = app;
|
||||
|
||||
if (!this.page) {
|
||||
this.$rootScope.appEvent('alert-error', ['App Page Not Found', '']);
|
||||
|
||||
this.navModel = {
|
||||
section: {
|
||||
title: "Page not found",
|
||||
url: app.defaultNavUrl,
|
||||
icon: 'icon-gf icon-gf-sadface',
|
||||
},
|
||||
menu: [],
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let menu = [];
|
||||
|
||||
for (let item of app.includes) {
|
||||
if (item.addToNav) {
|
||||
if (item.type === 'dashboard') {
|
||||
menu.push({
|
||||
title: item.name,
|
||||
url: 'dashboard/db/' + item.slug,
|
||||
icon: 'fa fa-fw fa-dot-circle-o',
|
||||
});
|
||||
}
|
||||
if (item.type === 'page') {
|
||||
menu.push({
|
||||
title: item.name,
|
||||
url: `plugins/${app.id}/page/${item.slug}`,
|
||||
icon: 'fa fa-fw fa-dot-circle-o',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.navModel = {
|
||||
section: {
|
||||
title: app.name,
|
||||
url: app.defaultNavUrl,
|
||||
iconUrl: app.info.logos.small,
|
||||
},
|
||||
menu: menu,
|
||||
};
|
||||
}
|
||||
|
||||
loadPluginInfo() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="icon-gf icon-gf-snapshot" title="Snapshots" title-url="dashboard/snapshots">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -4,11 +4,20 @@ import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
export class SnapshotsCtrl {
|
||||
|
||||
navModel: any;
|
||||
snapshots: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $rootScope, private backendSrv) {
|
||||
this.navModel = {
|
||||
section: {
|
||||
title: 'Snapshots',
|
||||
icon: 'icon-gf icon-gf-snapshot',
|
||||
url: 'dashboard/snapshots',
|
||||
},
|
||||
menu: [],
|
||||
};
|
||||
|
||||
this.backendSrv.get('/api/dashboard/snapshots').then(result => {
|
||||
this.snapshots = result;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar icon="fa fa-fw fa-adjust" title="Style Guide" title-url="styleguide">
|
||||
</navbar>
|
||||
<navbar model="ctrl.navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
||||
@@ -12,9 +12,11 @@ class StyleGuideCtrl {
|
||||
icons: any = [];
|
||||
page: any;
|
||||
pages = ['colors', 'buttons', 'icons', 'plugins'];
|
||||
navModel: any;
|
||||
|
||||
/** @ngInject **/
|
||||
constructor(private $http, private $routeParams, private $location, private backendSrv) {
|
||||
constructor(private $http, private $routeParams, private $location, private backendSrv, navModelSrv) {
|
||||
this.navModel = navModelSrv.getAdminNav();
|
||||
this.theme = config.bootData.user.lightTheme ? 'light': 'dark';
|
||||
this.page = {};
|
||||
|
||||
|
||||
Vendored
+1
@@ -72,3 +72,4 @@ declare module 'd3' {
|
||||
var d3: any;
|
||||
export default d3;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<div dash-class ng-if="dashboard">
|
||||
<dashnav></dashnav>
|
||||
<dashnav dashboard="dashboard"></dashnav>
|
||||
|
||||
<div class="dashboard-container">
|
||||
<div dash-editor-view></div>
|
||||
<dashboard-search></dashboard-search>
|
||||
<div dash-editor-view class="dash-edit-view"></div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<dashboard-submenu ng-if="dashboard.meta.submenuEnabled" dashboard="dashboard"></dashboard-submenu>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<navbar title="404" icon="fa fa-fw fa-question" title-url="/">
|
||||
</navbar>
|
||||
<navbar model="navModel"></navbar>
|
||||
|
||||
<div class="page-container">
|
||||
|
||||
|
||||
@@ -119,8 +119,12 @@ function (queryDef) {
|
||||
query.fields = ["*", "_source"];
|
||||
}
|
||||
|
||||
query.script_fields = {},
|
||||
query.docvalue_fields = [this.timeField];
|
||||
query.script_fields = {};
|
||||
if (this.esVersion < 5) {
|
||||
query.fielddata_fields = [this.timeField];
|
||||
} else {
|
||||
query.docvalue_fields = [this.timeField];
|
||||
}
|
||||
return query;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,11 +8,31 @@ class GrafanaDatasource {
|
||||
constructor(private backendSrv, private $q) {}
|
||||
|
||||
query(options) {
|
||||
return this.$q.when({data: []});
|
||||
return this.backendSrv.get('/api/tsdb/testdata/random-walk', {
|
||||
from: options.range.from.valueOf(),
|
||||
to: options.range.to.valueOf(),
|
||||
intervalMs: options.intervalMs,
|
||||
maxDataPoints: options.maxDataPoints,
|
||||
}).then(res => {
|
||||
var data = [];
|
||||
|
||||
if (res.results) {
|
||||
_.forEach(res.results, queryRes => {
|
||||
for (let series of queryRes.series) {
|
||||
data.push({
|
||||
target: series.name,
|
||||
datapoints: series.points
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {data: data};
|
||||
});
|
||||
}
|
||||
|
||||
metricFindQuery() {
|
||||
return this.$q.when([]);
|
||||
metricFindQuery(options) {
|
||||
return this.$q.when({data: []});
|
||||
}
|
||||
|
||||
annotationQuery(options) {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">Test metric (fake data source)</label>
|
||||
<label class="gf-form-label">Test data: random walk</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</query-editor-row>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user