mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
migrated files to ts + fixed specfile
This commit is contained in:
parent
af34f9977e
commit
9b63a81756
@ -1,18 +1,18 @@
|
||||
import './dashboard_ctrl';
|
||||
import './alerting_srv';
|
||||
import './history/history';
|
||||
import './dashboardLoaderSrv';
|
||||
import './dashboard_loader_srv';
|
||||
import './dashnav/dashnav';
|
||||
import './submenu/submenu';
|
||||
import './save_as_modal';
|
||||
import './save_modal';
|
||||
import './shareModalCtrl';
|
||||
import './shareSnapshotCtrl';
|
||||
import './share_snapshot_ctrl';
|
||||
import './dashboard_srv';
|
||||
import './view_state_srv';
|
||||
import './validation_srv';
|
||||
import './time_srv';
|
||||
import './unsavedChangesSrv';
|
||||
import './unsaved_changes_srv';
|
||||
import './unsaved_changes_modal';
|
||||
import './timepicker/timepicker';
|
||||
import './upload';
|
||||
|
@ -1,109 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'moment',
|
||||
'lodash',
|
||||
'jquery',
|
||||
'app/core/utils/kbn',
|
||||
'app/core/utils/datemath',
|
||||
'app/core/services/impression_srv'
|
||||
],
|
||||
function (angular, moment, _, $, kbn, dateMath, impressionSrv) {
|
||||
'use strict';
|
||||
|
||||
kbn = kbn.default;
|
||||
impressionSrv = impressionSrv.default;
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
|
||||
module.service('dashboardLoaderSrv', function(backendSrv,
|
||||
dashboardSrv,
|
||||
datasourceSrv,
|
||||
$http, $q, $timeout,
|
||||
contextSrv, $routeParams,
|
||||
$rootScope) {
|
||||
var self = this;
|
||||
|
||||
this._dashboardLoadFailed = function(title, snapshot) {
|
||||
snapshot = snapshot || false;
|
||||
return {
|
||||
meta: { canStar: false, isSnapshot: snapshot, canDelete: false, canSave: false, canEdit: false, dashboardNotFound: true },
|
||||
dashboard: {title: title }
|
||||
};
|
||||
};
|
||||
|
||||
this.loadDashboard = function(type, slug) {
|
||||
var promise;
|
||||
|
||||
if (type === 'script') {
|
||||
promise = this._loadScriptedDashboard(slug);
|
||||
} else if (type === 'snapshot') {
|
||||
promise = backendSrv.get('/api/snapshots/' + $routeParams.slug)
|
||||
.catch(function() {
|
||||
return self._dashboardLoadFailed("Snapshot not found", true);
|
||||
});
|
||||
} else {
|
||||
promise = backendSrv.getDashboard($routeParams.type, $routeParams.slug)
|
||||
.then(function(result) {
|
||||
if (result.meta.isFolder) {
|
||||
$rootScope.appEvent("alert-error", ['Dashboard not found']);
|
||||
throw new Error("Dashboard not found");
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.catch(function() {
|
||||
return self._dashboardLoadFailed("Not found");
|
||||
});
|
||||
}
|
||||
|
||||
promise.then(function(result) {
|
||||
|
||||
if (result.meta.dashboardNotFound !== true) {
|
||||
impressionSrv.addDashboardImpression(result.dashboard.id);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
this._loadScriptedDashboard = function(file) {
|
||||
var url = 'public/dashboards/'+file.replace(/\.(?!js)/,"/") + '?' + new Date().getTime();
|
||||
|
||||
return $http({ url: url, method: "GET" })
|
||||
.then(this._executeScript).then(function(result) {
|
||||
return { meta: { fromScript: true, canDelete: false, canSave: false, canStar: false}, dashboard: result.data };
|
||||
}, function(err) {
|
||||
console.log('Script dashboard error '+ err);
|
||||
$rootScope.appEvent('alert-error', ["Script Error", "Please make sure it exists and returns a valid dashboard"]);
|
||||
return self._dashboardLoadFailed('Scripted dashboard');
|
||||
});
|
||||
};
|
||||
|
||||
this._executeScript = function(result) {
|
||||
var services = {
|
||||
dashboardSrv: dashboardSrv,
|
||||
datasourceSrv: datasourceSrv,
|
||||
$q: $q,
|
||||
};
|
||||
|
||||
/*jshint -W054 */
|
||||
var script_func = new Function('ARGS','kbn','dateMath','_','moment','window','document','$','jQuery', 'services', result.data);
|
||||
var script_result = script_func($routeParams, kbn, dateMath, _ , 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 };
|
||||
};
|
||||
|
||||
});
|
||||
});
|
158
public/app/features/dashboard/dashboard_loader_srv.ts
Normal file
158
public/app/features/dashboard/dashboard_loader_srv.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import angular from 'angular';
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
import impressionSrv from 'app/core/services/impression_srv';
|
||||
|
||||
export class DashboardLoaderSrv {
|
||||
/** @ngInject */
|
||||
constructor(
|
||||
private backendSrv,
|
||||
private dashboardSrv,
|
||||
private datasourceSrv,
|
||||
private $http,
|
||||
private $q,
|
||||
private $timeout,
|
||||
contextSrv,
|
||||
private $routeParams,
|
||||
private $rootScope
|
||||
) {}
|
||||
|
||||
_dashboardLoadFailed(title, snapshot) {
|
||||
snapshot = snapshot || false;
|
||||
return {
|
||||
meta: {
|
||||
canStar: false,
|
||||
isSnapshot: snapshot,
|
||||
canDelete: false,
|
||||
canSave: false,
|
||||
canEdit: false,
|
||||
dashboardNotFound: true,
|
||||
},
|
||||
dashboard: { title: title },
|
||||
};
|
||||
}
|
||||
|
||||
loadDashboard(type, slug) {
|
||||
var promise;
|
||||
|
||||
if (type === 'script') {
|
||||
promise = this._loadScriptedDashboard(slug);
|
||||
} else if (type === 'snapshot') {
|
||||
promise = this.backendSrv
|
||||
.get('/api/snapshots/' + this.$routeParams.slug)
|
||||
.catch(() => {
|
||||
return this._dashboardLoadFailed('Snapshot not found', true);
|
||||
});
|
||||
} else {
|
||||
promise = this.backendSrv
|
||||
.getDashboard(this.$routeParams.type, this.$routeParams.slug)
|
||||
.then(result => {
|
||||
if (result.meta.isFolder) {
|
||||
this.$rootScope.appEvent('alert-error', ['Dashboard not found']);
|
||||
throw new Error('Dashboard not found');
|
||||
}
|
||||
return result;
|
||||
})
|
||||
.catch(() => {
|
||||
return this._dashboardLoadFailed('Not found', true);
|
||||
});
|
||||
}
|
||||
|
||||
promise.then(function(result) {
|
||||
if (result.meta.dashboardNotFound !== true) {
|
||||
impressionSrv.addDashboardImpression(result.dashboard.id);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
_loadScriptedDashboard(file) {
|
||||
var url =
|
||||
'public/dashboards/' +
|
||||
file.replace(/\.(?!js)/, '/') +
|
||||
'?' +
|
||||
new Date().getTime();
|
||||
|
||||
return this.$http({ url: url, method: 'GET' })
|
||||
.then(this._executeScript)
|
||||
.then(
|
||||
function(result) {
|
||||
return {
|
||||
meta: {
|
||||
fromScript: true,
|
||||
canDelete: false,
|
||||
canSave: false,
|
||||
canStar: false,
|
||||
},
|
||||
dashboard: result.data,
|
||||
};
|
||||
},
|
||||
function(err) {
|
||||
console.log('Script dashboard error ' + err);
|
||||
this.$rootScope.appEvent('alert-error', [
|
||||
'Script Error',
|
||||
'Please make sure it exists and returns a valid dashboard',
|
||||
]);
|
||||
return this._dashboardLoadFailed('Scripted dashboard');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_executeScript(result) {
|
||||
var services = {
|
||||
dashboardSrv: this.dashboardSrv,
|
||||
datasourceSrv: this.datasourceSrv,
|
||||
$q: this.$q,
|
||||
};
|
||||
|
||||
/*jshint -W054 */
|
||||
var script_func = new Function(
|
||||
'ARGS',
|
||||
'kbn',
|
||||
'dateMath',
|
||||
'_',
|
||||
'moment',
|
||||
'window',
|
||||
'document',
|
||||
'$',
|
||||
'jQuery',
|
||||
'services',
|
||||
result.data
|
||||
);
|
||||
var script_result = script_func(
|
||||
this.$routeParams,
|
||||
kbn,
|
||||
dateMath,
|
||||
_,
|
||||
moment,
|
||||
window,
|
||||
document,
|
||||
$,
|
||||
$,
|
||||
services
|
||||
);
|
||||
|
||||
// Handle async dashboard scripts
|
||||
if (_.isFunction(script_result)) {
|
||||
var deferred = this.$q.defer();
|
||||
script_result(dashboard => {
|
||||
this.$timeout(() => {
|
||||
deferred.resolve({ data: dashboard });
|
||||
});
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
return { data: script_result };
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('grafana.services')
|
||||
.service('dashboardLoaderSrv', DashboardLoaderSrv);
|
@ -1,14 +1,8 @@
|
||||
define([
|
||||
'angular',
|
||||
'lodash',
|
||||
],
|
||||
function (angular, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('ShareSnapshotCtrl', function($scope, $rootScope, $location, backendSrv, $timeout, timeSrv) {
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
export class ShareSnapshotCtrl {
|
||||
constructor($scope, $rootScope, $location, backendSrv, $timeout, timeSrv) {
|
||||
$scope.snapshot = {
|
||||
name: $scope.dashboard.title,
|
||||
expires: 0,
|
||||
@ -18,16 +12,16 @@ function (angular, _) {
|
||||
$scope.step = 1;
|
||||
|
||||
$scope.expireOptions = [
|
||||
{text: '1 Hour', value: 60*60},
|
||||
{text: '1 Day', value: 60*60*24},
|
||||
{text: '7 Days', value: 60*60*24*7},
|
||||
{text: 'Never', value: 0},
|
||||
{ text: '1 Hour', value: 60 * 60 },
|
||||
{ text: '1 Day', value: 60 * 60 * 24 },
|
||||
{ text: '7 Days', value: 60 * 60 * 24 * 7 },
|
||||
{ text: 'Never', value: 0 },
|
||||
];
|
||||
|
||||
$scope.accessOptions = [
|
||||
{text: 'Anyone with the link', value: 1},
|
||||
{text: 'Organization users', value: 2},
|
||||
{text: 'Public on the web', value: 3},
|
||||
{ text: 'Anyone with the link', value: 1 },
|
||||
{ text: 'Organization users', value: 2 },
|
||||
{ text: 'Public on the web', value: 3 },
|
||||
];
|
||||
|
||||
$scope.init = function() {
|
||||
@ -42,7 +36,7 @@ function (angular, _) {
|
||||
|
||||
$scope.createSnapshot = function(external) {
|
||||
$scope.dashboard.snapshot = {
|
||||
timestamp: new Date()
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
if (!external) {
|
||||
@ -69,31 +63,37 @@ function (angular, _) {
|
||||
expires: $scope.snapshot.expires,
|
||||
};
|
||||
|
||||
var postUrl = external ? $scope.externalUrl + $scope.apiUrl : $scope.apiUrl;
|
||||
var postUrl = external
|
||||
? $scope.externalUrl + $scope.apiUrl
|
||||
: $scope.apiUrl;
|
||||
|
||||
backendSrv.post(postUrl, cmdData).then(function(results) {
|
||||
$scope.loading = false;
|
||||
backendSrv.post(postUrl, cmdData).then(
|
||||
function(results) {
|
||||
$scope.loading = false;
|
||||
|
||||
if (external) {
|
||||
$scope.deleteUrl = results.deleteUrl;
|
||||
$scope.snapshotUrl = results.url;
|
||||
$scope.saveExternalSnapshotRef(cmdData, results);
|
||||
} else {
|
||||
var url = $location.url();
|
||||
var baseUrl = $location.absUrl();
|
||||
if (external) {
|
||||
$scope.deleteUrl = results.deleteUrl;
|
||||
$scope.snapshotUrl = results.url;
|
||||
$scope.saveExternalSnapshotRef(cmdData, results);
|
||||
} else {
|
||||
var url = $location.url();
|
||||
var baseUrl = $location.absUrl();
|
||||
|
||||
if (url !== '/') {
|
||||
baseUrl = baseUrl.replace(url, '') + '/';
|
||||
if (url !== '/') {
|
||||
baseUrl = baseUrl.replace(url, '') + '/';
|
||||
}
|
||||
|
||||
$scope.snapshotUrl = baseUrl + 'dashboard/snapshot/' + results.key;
|
||||
$scope.deleteUrl =
|
||||
baseUrl + 'api/snapshots-delete/' + results.deleteKey;
|
||||
}
|
||||
|
||||
$scope.snapshotUrl = baseUrl + 'dashboard/snapshot/' + results.key;
|
||||
$scope.deleteUrl = baseUrl + 'api/snapshots-delete/' + results.deleteKey;
|
||||
$scope.step = 2;
|
||||
},
|
||||
function() {
|
||||
$scope.loading = false;
|
||||
}
|
||||
|
||||
$scope.step = 2;
|
||||
}, function() {
|
||||
$scope.loading = false;
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
$scope.getSnapshotUrl = function() {
|
||||
@ -116,21 +116,22 @@ function (angular, _) {
|
||||
|
||||
// remove annotation queries
|
||||
dash.annotations.list = _.chain(dash.annotations.list)
|
||||
.filter(function(annotation) {
|
||||
return annotation.enable;
|
||||
})
|
||||
.map(function(annotation) {
|
||||
return {
|
||||
name: annotation.name,
|
||||
enable: annotation.enable,
|
||||
iconColor: annotation.iconColor,
|
||||
snapshotData: annotation.snapshotData
|
||||
};
|
||||
}).value();
|
||||
.filter(function(annotation) {
|
||||
return annotation.enable;
|
||||
})
|
||||
.map(function(annotation) {
|
||||
return {
|
||||
name: annotation.name,
|
||||
enable: annotation.enable,
|
||||
iconColor: annotation.iconColor,
|
||||
snapshotData: annotation.snapshotData,
|
||||
};
|
||||
})
|
||||
.value();
|
||||
|
||||
// remove template queries
|
||||
_.each(dash.templating.list, function(variable) {
|
||||
variable.query = "";
|
||||
variable.query = '';
|
||||
variable.options = variable.current;
|
||||
variable.refresh = false;
|
||||
});
|
||||
@ -168,7 +169,9 @@ function (angular, _) {
|
||||
cmdData.deleteKey = results.deleteKey;
|
||||
backendSrv.post('/api/snapshots/', cmdData);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
angular
|
||||
.module('grafana.controllers')
|
||||
.controller('ShareSnapshotCtrl', ShareSnapshotCtrl);
|
@ -6,14 +6,17 @@ import {
|
||||
sinon,
|
||||
angularMocks,
|
||||
} from 'test/lib/common';
|
||||
import 'app/features/dashboard/unsavedChangesSrv';
|
||||
import { Tracker } from 'app/features/dashboard/unsaved_changes_srv';
|
||||
import 'app/features/dashboard/dashboard_srv';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
|
||||
describe('unsavedChangesSrv', function() {
|
||||
var _unsavedChangesSrv;
|
||||
var _dashboardSrv;
|
||||
var _contextSrvStub = { isEditor: true };
|
||||
var _rootScope;
|
||||
var _location;
|
||||
var _timeout;
|
||||
var _window;
|
||||
var tracker;
|
||||
var dash;
|
||||
var scope;
|
||||
@ -32,11 +35,15 @@ describe('unsavedChangesSrv', function() {
|
||||
unsavedChangesSrv,
|
||||
$location,
|
||||
$rootScope,
|
||||
dashboardSrv
|
||||
dashboardSrv,
|
||||
$timeout,
|
||||
$window
|
||||
) {
|
||||
_unsavedChangesSrv = unsavedChangesSrv;
|
||||
_dashboardSrv = dashboardSrv;
|
||||
_rootScope = $rootScope;
|
||||
_location = $location;
|
||||
_timeout = $timeout;
|
||||
_window = $window;
|
||||
})
|
||||
);
|
||||
|
||||
@ -54,7 +61,16 @@ describe('unsavedChangesSrv', function() {
|
||||
scope.appEvent = sinon.spy();
|
||||
scope.onAppEvent = sinon.spy();
|
||||
|
||||
tracker = new _unsavedChangesSrv.Tracker(dash, scope);
|
||||
tracker = new Tracker(
|
||||
dash,
|
||||
scope,
|
||||
undefined,
|
||||
_location,
|
||||
_window,
|
||||
_timeout,
|
||||
contextSrv,
|
||||
_rootScope
|
||||
);
|
||||
});
|
||||
|
||||
it('No changes should not have changes', function() {
|
||||
|
@ -1,189 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'lodash',
|
||||
],
|
||||
function(angular, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
|
||||
module.service('unsavedChangesSrv', function($rootScope, $q, $location, $timeout, contextSrv, dashboardSrv, $window) {
|
||||
|
||||
function Tracker(dashboard, scope, originalCopyDelay) {
|
||||
var self = this;
|
||||
|
||||
this.current = dashboard;
|
||||
this.originalPath = $location.path();
|
||||
this.scope = scope;
|
||||
|
||||
// register events
|
||||
scope.onAppEvent('dashboard-saved', function() {
|
||||
this.original = this.current.getSaveModelClone();
|
||||
this.originalPath = $location.path();
|
||||
}.bind(this));
|
||||
|
||||
$window.onbeforeunload = function() {
|
||||
if (self.ignoreChanges()) { return; }
|
||||
if (self.hasChanges()) {
|
||||
return "There are unsaved changes to this dashboard";
|
||||
}
|
||||
};
|
||||
|
||||
scope.$on("$locationChangeStart", function(event, next) {
|
||||
// check if we should look for changes
|
||||
if (self.originalPath === $location.path()) { return true; }
|
||||
if (self.ignoreChanges()) { return true; }
|
||||
|
||||
if (self.hasChanges()) {
|
||||
event.preventDefault();
|
||||
self.next = next;
|
||||
|
||||
$timeout(function() {
|
||||
self.open_modal();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (originalCopyDelay) {
|
||||
$timeout(function() {
|
||||
// wait for different services to patch the dashboard (missing properties)
|
||||
self.original = dashboard.getSaveModelClone();
|
||||
}, originalCopyDelay);
|
||||
} else {
|
||||
self.original = dashboard.getSaveModelClone();
|
||||
}
|
||||
}
|
||||
|
||||
var p = Tracker.prototype;
|
||||
|
||||
// for some dashboards and users
|
||||
// changes should be ignored
|
||||
p.ignoreChanges = function() {
|
||||
if (!this.original) { return true; }
|
||||
if (!contextSrv.isEditor) { return true; }
|
||||
if (!this.current || !this.current.meta) { return true; }
|
||||
|
||||
var meta = this.current.meta;
|
||||
return !meta.canSave || meta.fromScript || meta.fromFile;
|
||||
};
|
||||
|
||||
// remove stuff that should not count in diff
|
||||
p.cleanDashboardFromIgnoredChanges = function(dash) {
|
||||
// ignore time and refresh
|
||||
dash.time = 0;
|
||||
dash.refresh = 0;
|
||||
dash.schemaVersion = 0;
|
||||
|
||||
// filter row and panels properties that should be ignored
|
||||
dash.rows = _.filter(dash.rows, function(row) {
|
||||
if (row.repeatRowId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
row.panels = _.filter(row.panels, function(panel) {
|
||||
if (panel.repeatPanelId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove scopedVars
|
||||
panel.scopedVars = null;
|
||||
|
||||
// ignore span changes
|
||||
panel.span = null;
|
||||
|
||||
// ignore panel legend sort
|
||||
if (panel.legend) {
|
||||
delete panel.legend.sort;
|
||||
delete panel.legend.sortDesc;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// ignore collapse state
|
||||
row.collapse = false;
|
||||
return true;
|
||||
});
|
||||
|
||||
dash.panels = _.filter(dash.panels, function(panel) {
|
||||
if (panel.repeatPanelId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove scopedVars
|
||||
panel.scopedVars = null;
|
||||
|
||||
// ignore panel legend sort
|
||||
if (panel.legend) {
|
||||
delete panel.legend.sort;
|
||||
delete panel.legend.sortDesc;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// ignore template variable values
|
||||
_.each(dash.templating.list, function(value) {
|
||||
value.current = null;
|
||||
value.options = null;
|
||||
value.filters = null;
|
||||
});
|
||||
};
|
||||
|
||||
p.hasChanges = function() {
|
||||
var current = this.current.getSaveModelClone();
|
||||
var original = this.original;
|
||||
|
||||
this.cleanDashboardFromIgnoredChanges(current);
|
||||
this.cleanDashboardFromIgnoredChanges(original);
|
||||
|
||||
var currentTimepicker = _.find(current.nav, { type: 'timepicker' });
|
||||
var originalTimepicker = _.find(original.nav, { type: 'timepicker' });
|
||||
|
||||
if (currentTimepicker && originalTimepicker) {
|
||||
currentTimepicker.now = originalTimepicker.now;
|
||||
}
|
||||
|
||||
var currentJson = angular.toJson(current);
|
||||
var originalJson = angular.toJson(original);
|
||||
|
||||
return currentJson !== originalJson;
|
||||
};
|
||||
|
||||
p.discardChanges = function() {
|
||||
this.original = null;
|
||||
this.gotoNext();
|
||||
};
|
||||
|
||||
p.open_modal = function() {
|
||||
$rootScope.appEvent('show-modal', {
|
||||
templateHtml: '<unsaved-changes-modal dismiss="dismiss()"></unsaved-changes-modal>',
|
||||
modalClass: 'modal--narrow confirm-modal'
|
||||
});
|
||||
};
|
||||
|
||||
p.saveChanges = function() {
|
||||
var self = this;
|
||||
var cancel = $rootScope.$on('dashboard-saved', function() {
|
||||
cancel();
|
||||
$timeout(function() {
|
||||
self.gotoNext();
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.appEvent('save-dashboard');
|
||||
};
|
||||
|
||||
p.gotoNext = function() {
|
||||
var baseLen = $location.absUrl().length - $location.url().length;
|
||||
var nextUrl = this.next.substring(baseLen);
|
||||
$location.url(nextUrl);
|
||||
};
|
||||
|
||||
this.Tracker = Tracker;
|
||||
this.init = function(dashboard, scope) {
|
||||
this.tracker = new Tracker(dashboard, scope, 1000);
|
||||
return this.tracker;
|
||||
};
|
||||
});
|
||||
});
|
236
public/app/features/dashboard/unsaved_changes_srv.ts
Normal file
236
public/app/features/dashboard/unsaved_changes_srv.ts
Normal file
@ -0,0 +1,236 @@
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
export class Tracker {
|
||||
current: any;
|
||||
originalPath: any;
|
||||
scope: any;
|
||||
original: any;
|
||||
next: any;
|
||||
$window: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(
|
||||
dashboard,
|
||||
scope,
|
||||
originalCopyDelay,
|
||||
private $location,
|
||||
$window,
|
||||
private $timeout,
|
||||
private contextSrv,
|
||||
private $rootScope
|
||||
) {
|
||||
this.$location = $location;
|
||||
this.$window = $window;
|
||||
|
||||
this.current = dashboard;
|
||||
this.originalPath = $location.path();
|
||||
this.scope = scope;
|
||||
|
||||
// register events
|
||||
scope.onAppEvent('dashboard-saved', () => {
|
||||
this.original = this.current.getSaveModelClone();
|
||||
this.originalPath = $location.path();
|
||||
});
|
||||
|
||||
$window.onbeforeunload = () => {
|
||||
if (this.ignoreChanges()) {
|
||||
return '';
|
||||
}
|
||||
if (this.hasChanges()) {
|
||||
return 'There are unsaved changes to this dashboard';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
scope.$on('$locationChangeStart', (event, next) => {
|
||||
// check if we should look for changes
|
||||
if (this.originalPath === $location.path()) {
|
||||
return true;
|
||||
}
|
||||
if (this.ignoreChanges()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.hasChanges()) {
|
||||
event.preventDefault();
|
||||
this.next = next;
|
||||
|
||||
this.$timeout(() => {
|
||||
this.open_modal();
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (originalCopyDelay) {
|
||||
this.$timeout(() => {
|
||||
// wait for different services to patch the dashboard (missing properties)
|
||||
this.original = dashboard.getSaveModelClone();
|
||||
}, originalCopyDelay);
|
||||
} else {
|
||||
this.original = dashboard.getSaveModelClone();
|
||||
}
|
||||
}
|
||||
|
||||
// for some dashboards and users
|
||||
// changes should be ignored
|
||||
ignoreChanges() {
|
||||
if (!this.original) {
|
||||
return true;
|
||||
}
|
||||
if (!this.contextSrv.isEditor) {
|
||||
return true;
|
||||
}
|
||||
if (!this.current || !this.current.meta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var meta = this.current.meta;
|
||||
return !meta.canSave || meta.fromScript || meta.fromFile;
|
||||
}
|
||||
|
||||
// remove stuff that should not count in diff
|
||||
cleanDashboardFromIgnoredChanges(dash) {
|
||||
// ignore time and refresh
|
||||
dash.time = 0;
|
||||
dash.refresh = 0;
|
||||
dash.schemaVersion = 0;
|
||||
|
||||
// filter row and panels properties that should be ignored
|
||||
dash.rows = _.filter(dash.rows, function(row) {
|
||||
if (row.repeatRowId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
row.panels = _.filter(row.panels, function(panel) {
|
||||
if (panel.repeatPanelId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove scopedVars
|
||||
panel.scopedVars = null;
|
||||
|
||||
// ignore span changes
|
||||
panel.span = null;
|
||||
|
||||
// ignore panel legend sort
|
||||
if (panel.legend) {
|
||||
delete panel.legend.sort;
|
||||
delete panel.legend.sortDesc;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// ignore collapse state
|
||||
row.collapse = false;
|
||||
return true;
|
||||
});
|
||||
|
||||
dash.panels = _.filter(dash.panels, panel => {
|
||||
if (panel.repeatPanelId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove scopedVars
|
||||
panel.scopedVars = null;
|
||||
|
||||
// ignore panel legend sort
|
||||
if (panel.legend) {
|
||||
delete panel.legend.sort;
|
||||
delete panel.legend.sortDesc;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// ignore template variable values
|
||||
_.each(dash.templating.list, function(value) {
|
||||
value.current = null;
|
||||
value.options = null;
|
||||
value.filters = null;
|
||||
});
|
||||
}
|
||||
|
||||
hasChanges() {
|
||||
var current = this.current.getSaveModelClone();
|
||||
var original = this.original;
|
||||
|
||||
this.cleanDashboardFromIgnoredChanges(current);
|
||||
this.cleanDashboardFromIgnoredChanges(original);
|
||||
|
||||
var currentTimepicker = _.find(current.nav, { type: 'timepicker' });
|
||||
var originalTimepicker = _.find(original.nav, { type: 'timepicker' });
|
||||
|
||||
if (currentTimepicker && originalTimepicker) {
|
||||
currentTimepicker.now = originalTimepicker.now;
|
||||
}
|
||||
|
||||
var currentJson = angular.toJson(current);
|
||||
var originalJson = angular.toJson(original);
|
||||
|
||||
return currentJson !== originalJson;
|
||||
}
|
||||
|
||||
discardChanges() {
|
||||
this.original = null;
|
||||
this.gotoNext();
|
||||
}
|
||||
|
||||
open_modal() {
|
||||
this.$rootScope.appEvent('show-modal', {
|
||||
templateHtml:
|
||||
'<unsaved-changes-modal dismiss="dismiss()"></unsaved-changes-modal>',
|
||||
modalClass: 'modal--narrow confirm-modal',
|
||||
});
|
||||
}
|
||||
|
||||
saveChanges() {
|
||||
var self = this;
|
||||
var cancel = this.$rootScope.$on('dashboard-saved', () => {
|
||||
cancel();
|
||||
this.$timeout(() => {
|
||||
self.gotoNext();
|
||||
});
|
||||
});
|
||||
|
||||
this.$rootScope.appEvent('save-dashboard');
|
||||
}
|
||||
|
||||
gotoNext() {
|
||||
var baseLen = this.$location.absUrl().length - this.$location.url().length;
|
||||
var nextUrl = this.next.substring(baseLen);
|
||||
this.$location.url(nextUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/** @ngInject */
|
||||
export function unsavedChangesSrv(
|
||||
$rootScope,
|
||||
$q,
|
||||
$location,
|
||||
$timeout,
|
||||
contextSrv,
|
||||
dashboardSrv,
|
||||
$window
|
||||
) {
|
||||
this.Tracker = Tracker;
|
||||
this.init = function(dashboard, scope) {
|
||||
this.tracker = new Tracker(
|
||||
dashboard,
|
||||
scope,
|
||||
1000,
|
||||
$location,
|
||||
$window,
|
||||
$timeout,
|
||||
contextSrv,
|
||||
$rootScope
|
||||
);
|
||||
return this.tracker;
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('grafana.services')
|
||||
.service('unsavedChangesSrv', unsavedChangesSrv);
|
Loading…
Reference in New Issue
Block a user