mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: When saving a dashboard and another user has made changes inbetween, the user is promted with a warning if he really wants to overwrite the other's changes, Closes #718
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# 2.0.0 (unreleased)
|
# 2.0.0 (unreleased)
|
||||||
|
|
||||||
**New features**
|
**New features**
|
||||||
|
- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes inbetween the user is promted with a warning if he really wants to overwrite the other's changes
|
||||||
- [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views
|
- [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views
|
||||||
- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, usefull when you want to ignore last minute because it contains incomplete data
|
- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, usefull when you want to ignore last minute because it contains incomplete data
|
||||||
- [Issue #171](https://github.com/grafana/grafana/issues/171). Panel: Different time periods, panels can override dashboard relative time and/or add a time shift
|
- [Issue #171](https://github.com/grafana/grafana/issues/171). Panel: Different time periods, panels can override dashboard relative time and/or add a time shift
|
||||||
|
@@ -77,14 +77,18 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) {
|
|||||||
err := bus.Dispatch(&cmd)
|
err := bus.Dispatch(&cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == m.ErrDashboardWithSameNameExists {
|
if err == m.ErrDashboardWithSameNameExists {
|
||||||
c.JsonApiErr(400, "Dashboard with the same title already exists", nil)
|
c.JSON(412, util.DynMap{"status": "name-exists", "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err == m.ErrDashboardVersionMismatch {
|
||||||
|
c.JSON(412, util.DynMap{"status": "version-mismatch", "message": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JsonApiErr(500, "Failed to save dashboard", err)
|
c.JsonApiErr(500, "Failed to save dashboard", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(200, util.DynMap{"status": "success", "slug": cmd.Result.Slug})
|
c.JSON(200, util.DynMap{"status": "success", "slug": cmd.Result.Slug, "version": cmd.Result.Version})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHomeDashboard(c *middleware.Context) {
|
func GetHomeDashboard(c *middleware.Context) {
|
||||||
|
@@ -11,6 +11,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
ErrDashboardNotFound = errors.New("Account not found")
|
ErrDashboardNotFound = errors.New("Account not found")
|
||||||
ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
|
ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
|
||||||
|
ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Dashboard struct {
|
type Dashboard struct {
|
||||||
@@ -58,6 +59,10 @@ func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
|
|||||||
|
|
||||||
if dash.Data["id"] != nil {
|
if dash.Data["id"] != nil {
|
||||||
dash.Id = int64(dash.Data["id"].(float64))
|
dash.Id = int64(dash.Data["id"].(float64))
|
||||||
|
|
||||||
|
if dash.Data["version"] != nil {
|
||||||
|
dash.Version = int(dash.Data["version"].(float64))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dash
|
return dash
|
||||||
@@ -79,7 +84,8 @@ func (dash *Dashboard) UpdateSlug() {
|
|||||||
//
|
//
|
||||||
|
|
||||||
type SaveDashboardCommand struct {
|
type SaveDashboardCommand struct {
|
||||||
Dashboard map[string]interface{} `json:"dashboard"`
|
Dashboard map[string]interface{} `json:"dashboard" binding:"Required"`
|
||||||
|
Overwrite bool `json:"overwrite"`
|
||||||
OrgId int64 `json:"-"`
|
OrgId int64 `json:"-"`
|
||||||
|
|
||||||
Result *Dashboard
|
Result *Dashboard
|
||||||
|
@@ -28,13 +28,30 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasExisting && dash.Id != existing.Id {
|
if hasExisting {
|
||||||
return m.ErrDashboardWithSameNameExists
|
// another dashboard with same name
|
||||||
|
if dash.Id != existing.Id {
|
||||||
|
if cmd.Overwrite {
|
||||||
|
dash.Id = existing.Id
|
||||||
|
} else {
|
||||||
|
return m.ErrDashboardWithSameNameExists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check for is someone else has written in between
|
||||||
|
if dash.Version != existing.Version {
|
||||||
|
if cmd.Overwrite {
|
||||||
|
dash.Version = existing.Version
|
||||||
|
} else {
|
||||||
|
return m.ErrDashboardVersionMismatch
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dash.Id == 0 {
|
if dash.Id == 0 {
|
||||||
_, err = sess.Insert(dash)
|
_, err = sess.Insert(dash)
|
||||||
} else {
|
} else {
|
||||||
|
dash.Version += 1
|
||||||
|
dash.Data["version"] = dash.Version
|
||||||
_, err = sess.Id(dash.Id).Update(dash)
|
_, err = sess.Id(dash.Id).Update(dash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,26 +6,21 @@ function (angular) {
|
|||||||
|
|
||||||
var module = angular.module('grafana.controllers');
|
var module = angular.module('grafana.controllers');
|
||||||
|
|
||||||
module.controller('CloneDashboardCtrl', function($scope, datasourceSrv, $location) {
|
module.controller('CloneDashboardCtrl', function($scope, backendSrv, $location) {
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
$scope.db = datasourceSrv.getGrafanaDB();
|
|
||||||
$scope.clone.id = null;
|
$scope.clone.id = null;
|
||||||
$scope.clone.editable = true;
|
$scope.clone.editable = true;
|
||||||
$scope.clone.title = $scope.clone.title + " Copy";
|
$scope.clone.title = $scope.clone.title + " Copy";
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.saveClone = function() {
|
$scope.saveClone = function() {
|
||||||
$scope.db.saveDashboard($scope.clone)
|
backendSrv.saveDashboard($scope.clone)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
|
|
||||||
$scope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + result.title]);
|
$scope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + result.title]);
|
||||||
$location.url(result.url);
|
$location.url(result.url);
|
||||||
$scope.appEvent('dashboard-saved', $scope.clone);
|
$scope.appEvent('dashboard-saved', $scope.clone);
|
||||||
$scope.dismiss();
|
$scope.dismiss();
|
||||||
|
|
||||||
}, function(err) {
|
|
||||||
$scope.appEvent('alert-error', ['Save failed', err]);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@@ -55,10 +55,11 @@ function (angular, _, moment) {
|
|||||||
$scope.appEvent('hide-dash-editor');
|
$scope.appEvent('hide-dash-editor');
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.saveDashboard = function() {
|
$scope.saveDashboard = function(options) {
|
||||||
var clone = angular.copy($scope.dashboard);
|
var clone = angular.copy($scope.dashboard);
|
||||||
|
|
||||||
backendSrv.saveDashboard(clone).then(function(data) {
|
backendSrv.saveDashboard(clone, options).then(function(data) {
|
||||||
|
$scope.dashboard.version = data.version;
|
||||||
$scope.appEvent('dashboard-saved', $scope.dashboard);
|
$scope.appEvent('dashboard-saved', $scope.dashboard);
|
||||||
|
|
||||||
var dashboardUrl = '/dashboard/db/' + data.slug;
|
var dashboardUrl = '/dashboard/db/' + data.slug;
|
||||||
@@ -68,7 +69,35 @@ function (angular, _, moment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + clone.title]);
|
$scope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + clone.title]);
|
||||||
});
|
}, $scope.handleSaveDashError);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.handleSaveDashError = function(err) {
|
||||||
|
if (err.data && err.data.status === "version-mismatch" ) {
|
||||||
|
err.isHandled = true;
|
||||||
|
|
||||||
|
$scope.appEvent('confirm-modal', {
|
||||||
|
title: 'Someone else has updated this dashboard!',
|
||||||
|
text: "Do you STILL want to save?",
|
||||||
|
icon: "fa-warning",
|
||||||
|
onConfirm: function() {
|
||||||
|
$scope.saveDashboard({overwrite: true});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.data && err.data.status === "name-exists" ) {
|
||||||
|
err.isHandled = true;
|
||||||
|
|
||||||
|
$scope.appEvent('confirm-modal', {
|
||||||
|
title: 'Another dashboard with the same name exists',
|
||||||
|
text: "Do you STILL want to save and ovewrite it?",
|
||||||
|
icon: "fa-warning",
|
||||||
|
onConfirm: function() {
|
||||||
|
$scope.saveDashboard({overwrite: true});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.deleteDashboard = function() {
|
$scope.deleteDashboard = function() {
|
||||||
|
@@ -18,6 +18,10 @@ function (angular, $, kbn, _, moment) {
|
|||||||
data = {};
|
data = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data.id && data.version) {
|
||||||
|
data.schemaVersion = data.version;
|
||||||
|
}
|
||||||
|
|
||||||
this.id = data.id || null;
|
this.id = data.id || null;
|
||||||
this.title = data.title || 'No Title';
|
this.title = data.title || 'No Title';
|
||||||
this.originalTitle = this.title;
|
this.originalTitle = this.title;
|
||||||
@@ -33,8 +37,8 @@ function (angular, $, kbn, _, moment) {
|
|||||||
this.templating = this._ensureListExist(data.templating);
|
this.templating = this._ensureListExist(data.templating);
|
||||||
this.annotations = this._ensureListExist(data.annotations);
|
this.annotations = this._ensureListExist(data.annotations);
|
||||||
this.refresh = data.refresh;
|
this.refresh = data.refresh;
|
||||||
|
this.schemaVersion = data.schemaVersion || 0;
|
||||||
this.version = data.version || 0;
|
this.version = data.version || 0;
|
||||||
this.hideAllLegends = data.hideAllLegends || false;
|
|
||||||
|
|
||||||
if (this.nav.length === 0) {
|
if (this.nav.length === 0) {
|
||||||
this.nav.push({ type: 'timepicker' });
|
this.nav.push({ type: 'timepicker' });
|
||||||
@@ -134,9 +138,9 @@ function (angular, $, kbn, _, moment) {
|
|||||||
|
|
||||||
p._updateSchema = function(old) {
|
p._updateSchema = function(old) {
|
||||||
var i, j, k;
|
var i, j, k;
|
||||||
var oldVersion = this.version;
|
var oldVersion = this.schemaVersion;
|
||||||
var panelUpgrades = [];
|
var panelUpgrades = [];
|
||||||
this.version = 6;
|
this.schemaVersion = 6;
|
||||||
|
|
||||||
if (oldVersion === 6) {
|
if (oldVersion === 6) {
|
||||||
return;
|
return;
|
||||||
|
@@ -83,6 +83,8 @@ function(angular, _, config) {
|
|||||||
// ignore timespan changes
|
// ignore timespan changes
|
||||||
current.time = original.time = {};
|
current.time = original.time = {};
|
||||||
current.refresh = original.refresh;
|
current.refresh = original.refresh;
|
||||||
|
// ignore version
|
||||||
|
current.version = original.version;
|
||||||
|
|
||||||
// ignore template variable values
|
// ignore template variable values
|
||||||
_.each(current.templating.list, function(value, index) {
|
_.each(current.templating.list, function(value, index) {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<div class="modal-body gf-box gf-box-no-margin">
|
<div class="modal-body gf-box gf-box-no-margin">
|
||||||
<div class="gf-box-header">
|
<div class="gf-box-header">
|
||||||
<div class="gf-box-title">
|
<div class="gf-box-title">
|
||||||
<i class="fa fa-check"></i>
|
<i class="fa {{icon}}"></i>
|
||||||
{{title}}
|
{{title}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
116
src/app/routes/all.js
Normal file
116
src/app/routes/all.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
'./dashLoadControllers',
|
||||||
|
], function(angular) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var module = angular.module('grafana.routes');
|
||||||
|
|
||||||
|
module.config(function($routeProvider, $locationProvider) {
|
||||||
|
$locationProvider.html5Mode(true);
|
||||||
|
|
||||||
|
$routeProvider
|
||||||
|
.when('/', {
|
||||||
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
|
controller : 'DashFromDBCtrl',
|
||||||
|
reloadOnSearch: false,
|
||||||
|
})
|
||||||
|
.when('/dashboard/db/:slug', {
|
||||||
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
|
controller : 'DashFromDBCtrl',
|
||||||
|
reloadOnSearch: false,
|
||||||
|
})
|
||||||
|
.when('/dashboard/file/:jsonFile', {
|
||||||
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
|
controller : 'DashFromFileCtrl',
|
||||||
|
reloadOnSearch: false,
|
||||||
|
})
|
||||||
|
.when('/dashboard/script/:jsFile', {
|
||||||
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
|
controller : 'DashFromScriptCtrl',
|
||||||
|
reloadOnSearch: false,
|
||||||
|
})
|
||||||
|
.when('/dashboard/import/:file', {
|
||||||
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
|
controller : 'DashFromImportCtrl',
|
||||||
|
reloadOnSearch: false,
|
||||||
|
})
|
||||||
|
.when('/dashboard/new', {
|
||||||
|
templateUrl: 'app/partials/dashboard.html',
|
||||||
|
controller : 'NewDashboardCtrl',
|
||||||
|
reloadOnSearch: false,
|
||||||
|
})
|
||||||
|
.when('/dashboard/import', {
|
||||||
|
templateUrl: 'app/features/dashboard/partials/import.html',
|
||||||
|
controller : 'DashboardImportCtrl',
|
||||||
|
})
|
||||||
|
.when('/datasources', {
|
||||||
|
templateUrl: 'app/features/org/partials/datasources.html',
|
||||||
|
controller : 'DataSourcesCtrl',
|
||||||
|
})
|
||||||
|
.when('/datasources/edit/:id', {
|
||||||
|
templateUrl: 'app/features/org/partials/datasourceEdit.html',
|
||||||
|
controller : 'DataSourceEditCtrl',
|
||||||
|
})
|
||||||
|
.when('/datasources/new', {
|
||||||
|
templateUrl: 'app/features/org/partials/datasourceEdit.html',
|
||||||
|
controller : 'DataSourceEditCtrl',
|
||||||
|
})
|
||||||
|
.when('/org', {
|
||||||
|
templateUrl: 'app/features/org/partials/orgDetails.html',
|
||||||
|
controller : 'OrgDetailsCtrl',
|
||||||
|
})
|
||||||
|
.when('/org/new', {
|
||||||
|
templateUrl: 'app/features/org/partials/newOrg.html',
|
||||||
|
controller : 'NewOrgCtrl',
|
||||||
|
})
|
||||||
|
.when('/org/users', {
|
||||||
|
templateUrl: 'app/features/org/partials/orgUsers.html',
|
||||||
|
controller : 'OrgUsersCtrl',
|
||||||
|
})
|
||||||
|
.when('/org/apikeys', {
|
||||||
|
templateUrl: 'app/features/org/partials/orgApiKeys.html',
|
||||||
|
controller : 'OrgApiKeysCtrl',
|
||||||
|
})
|
||||||
|
.when('/profile', {
|
||||||
|
templateUrl: 'app/features/profile/partials/profile.html',
|
||||||
|
controller : 'ProfileCtrl',
|
||||||
|
})
|
||||||
|
.when('/profile/password', {
|
||||||
|
templateUrl: 'app/features/profile/partials/password.html',
|
||||||
|
controller : 'ChangePasswordCtrl',
|
||||||
|
})
|
||||||
|
.when('/admin/settings', {
|
||||||
|
templateUrl: 'app/features/admin/partials/settings.html',
|
||||||
|
controller : 'AdminSettingsCtrl',
|
||||||
|
})
|
||||||
|
.when('/admin/users', {
|
||||||
|
templateUrl: 'app/features/admin/partials/users.html',
|
||||||
|
controller : 'AdminUsersCtrl',
|
||||||
|
})
|
||||||
|
.when('/admin/users/create', {
|
||||||
|
templateUrl: 'app/features/admin/partials/new_user.html',
|
||||||
|
controller : 'AdminEditUserCtrl',
|
||||||
|
})
|
||||||
|
.when('/admin/users/edit/:id', {
|
||||||
|
templateUrl: 'app/features/admin/partials/edit_user.html',
|
||||||
|
controller : 'AdminEditUserCtrl',
|
||||||
|
})
|
||||||
|
.when('/admin/orgs', {
|
||||||
|
templateUrl: 'app/features/admin/partials/orgs.html',
|
||||||
|
})
|
||||||
|
.when('/login', {
|
||||||
|
templateUrl: 'app/partials/login.html',
|
||||||
|
controller : 'LoginCtrl',
|
||||||
|
})
|
||||||
|
.when('/dashboard/solo/:slug/', {
|
||||||
|
templateUrl: 'app/features/panel/partials/soloPanel.html',
|
||||||
|
controller : 'SoloPanelCtrl',
|
||||||
|
})
|
||||||
|
.otherwise({
|
||||||
|
templateUrl: 'app/partials/error.html',
|
||||||
|
controller: 'ErrorCtrl'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
126
src/app/routes/dashLoadControllers.js
Normal file
126
src/app/routes/dashLoadControllers.js
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
'lodash',
|
||||||
|
'kbn',
|
||||||
|
'moment',
|
||||||
|
'jquery',
|
||||||
|
],
|
||||||
|
function (angular, _, kbn, moment, $) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var module = angular.module('grafana.routes');
|
||||||
|
|
||||||
|
module.controller('DashFromDBCtrl', function($scope, $routeParams, backendSrv) {
|
||||||
|
|
||||||
|
if (!$routeParams.slug) {
|
||||||
|
backendSrv.get('/api/dashboards/home').then(function(result) {
|
||||||
|
$scope.initDashboard(result, $scope);
|
||||||
|
},function() {
|
||||||
|
$scope.initDashboard({}, $scope);
|
||||||
|
$scope.appEvent('alert-error', ['Load dashboard failed', '']);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return backendSrv.getDashboard($routeParams.slug).then(function(result) {
|
||||||
|
$scope.initDashboard(result, $scope);
|
||||||
|
}, function() {
|
||||||
|
$scope.initDashboard({
|
||||||
|
meta: {},
|
||||||
|
model: { title: 'Not found' }
|
||||||
|
}, $scope);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('DashFromImportCtrl', function($scope, $location, alertSrv) {
|
||||||
|
if (!window.grafanaImportDashboard) {
|
||||||
|
alertSrv.set('Not found', 'Cannot reload page with unsaved imported dashboard', 'warning', 7000);
|
||||||
|
$location.path('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope.initDashboard({ meta: {}, model: window.grafanaImportDashboard }, $scope);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('NewDashboardCtrl', function($scope) {
|
||||||
|
$scope.initDashboard({
|
||||||
|
meta: {},
|
||||||
|
model: {
|
||||||
|
title: "New dashboard",
|
||||||
|
rows: [{ height: '250px', panels:[] }]
|
||||||
|
},
|
||||||
|
}, $scope);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('DashFromFileCtrl', function($scope, $rootScope, $http, $routeParams) {
|
||||||
|
|
||||||
|
var file_load = function(file) {
|
||||||
|
return $http({
|
||||||
|
url: "public/dashboards/"+file.replace(/\.(?!json)/,"/")+'?' + new Date().getTime(),
|
||||||
|
method: "GET",
|
||||||
|
transformResponse: function(response) {
|
||||||
|
return angular.fromJson(response);
|
||||||
|
}
|
||||||
|
}).then(function(result) {
|
||||||
|
if(!result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return result.data;
|
||||||
|
},function() {
|
||||||
|
$scope.appEvent('alert-error', ["Dashboard load failed", "Could not load <i>dashboards/"+file+"</i>. Please make sure it exists"]);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
file_load($routeParams.jsonFile).then(function(result) {
|
||||||
|
$scope.initDashboard({meta: {}, model: result}, $scope);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.controller('DashFromScriptCtrl', function($scope, $rootScope, $http, $routeParams, $q, dashboardSrv, datasourceSrv, $timeout) {
|
||||||
|
|
||||||
|
var execute_script = function(result) {
|
||||||
|
var services = {
|
||||||
|
dashboardSrv: dashboardSrv,
|
||||||
|
datasourceSrv: datasourceSrv,
|
||||||
|
$q: $q,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*jshint -W054 */
|
||||||
|
var script_func = new Function('ARGS','kbn','_','moment','window','document','$','jQuery', 'services', result.data);
|
||||||
|
var script_result = script_func($routeParams, kbn, _ , moment, window, document, $, $, services);
|
||||||
|
|
||||||
|
// Handle async dashboard scripts
|
||||||
|
if (_.isFunction(script_result)) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
script_result(function(dashboard) {
|
||||||
|
$timeout(function() {
|
||||||
|
deferred.resolve({ data: dashboard });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: script_result };
|
||||||
|
};
|
||||||
|
|
||||||
|
var script_load = function(file) {
|
||||||
|
var url = 'public/dashboards/'+file.replace(/\.(?!js)/,"/") + '?' + new Date().getTime();
|
||||||
|
|
||||||
|
return $http({ url: url, method: "GET" })
|
||||||
|
.then(execute_script)
|
||||||
|
.then(null,function(err) {
|
||||||
|
console.log('Script dashboard error '+ err);
|
||||||
|
$scope.appEvent('alert-error', ["Script Error", "Please make sure it exists and returns a valid dashboard"]);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
script_load($routeParams.jsFile).then(function(result) {
|
||||||
|
$scope.initDashboard({meta: {}, model: result.data}, $scope);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -64,6 +64,7 @@ function (angular, _) {
|
|||||||
scope.title = payload.title;
|
scope.title = payload.title;
|
||||||
scope.text = payload.text;
|
scope.text = payload.text;
|
||||||
scope.onConfirm = payload.onConfirm;
|
scope.onConfirm = payload.onConfirm;
|
||||||
|
scope.icon = payload.icon || "fa-check";
|
||||||
|
|
||||||
var confirmModal = $modal({
|
var confirmModal = $modal({
|
||||||
template: './app/partials/confirm_modal.html',
|
template: './app/partials/confirm_modal.html',
|
||||||
|
@@ -85,8 +85,9 @@ function (angular, _, config) {
|
|||||||
return this.get('/api/dashboards/db/' + slug);
|
return this.get('/api/dashboards/db/' + slug);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.saveDashboard = function(dash) {
|
this.saveDashboard = function(dash, options) {
|
||||||
return this.post('/api/dashboards/db/', {dashboard: dash});
|
options = (options || {});
|
||||||
|
return this.post('/api/dashboards/db/', {dashboard: dash, overwrite: options.overwrite === true});
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -161,7 +161,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('dashboard schema version should be set to latest', function() {
|
it('dashboard schema version should be set to latest', function() {
|
||||||
expect(model.version).to.be(6);
|
expect(model.schemaVersion).to.be(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user