فهرست منبع

Worked on clone dashboard feature, #1488

Torkel Ödegaard 11 سال پیش
والد
کامیت
596ce18aeb

+ 4 - 0
CHANGELOG.md

@@ -4,6 +4,10 @@
 - [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 #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 #1488](https://github.com/grafana/grafana/issues/1488). Dashboard: Clone dashboard / Save as
+
+**Changes**
+- Dashboard title change & save will no longer create a new dashboard, it will just change the title.
 
 **Enhancements**
 - [Issue #1297](https://github.com/grafana/grafana/issues/1297). Graphite: Added cumulative and minimumBelow graphite functions

+ 0 - 1
src/app/components/settings.js

@@ -27,7 +27,6 @@ function (_, crypto) {
       playlist_timespan: "1m",
       unsaved_changes_warning: true,
       search: { max_results: 100 },
-      admin: {},
       appSubUrl: ""
     };
 

+ 1 - 0
src/app/features/dashboard/all.js

@@ -1,6 +1,7 @@
 define([
   './dashboardCtrl',
   './dashboardNavCtrl',
+  './cloneDashboardCtrl',
   './playlistCtrl',
   './rowCtrl',
   './sharePanelCtrl',

+ 33 - 0
src/app/features/dashboard/cloneDashboardCtrl.js

@@ -0,0 +1,33 @@
+define([
+  'angular',
+],
+function (angular) {
+  'use strict';
+
+  var module = angular.module('grafana.controllers');
+
+  module.controller('CloneDashboardCtrl', function($scope, datasourceSrv, $location) {
+
+    $scope.init = function() {
+      $scope.db = datasourceSrv.getGrafanaDB();
+      $scope.clone.id = null;
+      $scope.clone.editable = true;
+      $scope.clone.title = $scope.clone.title + " Copy";
+    };
+
+    $scope.saveClone = function() {
+      $scope.db.saveDashboard($scope.clone)
+        .then(function(result) {
+
+          $scope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + result.title]);
+          $location.url(result.url);
+          $scope.appEvent('dashboard-saved', $scope.clone);
+          $scope.dismiss();
+
+        }, function(err) {
+          $scope.appEvent('alert-error', ['Save failed', err]);
+        });
+    };
+  });
+
+});

+ 11 - 19
src/app/features/dashboard/dashboardNavCtrl.js

@@ -65,27 +65,11 @@ function (angular, _, moment, config, store) {
       window.sessionStorage["grafanaAdminPassword"] = pwd;
     };
 
-    $scope.isAdmin = function() {
-      if (!config.admin || !config.admin.password) { return true; }
-      if ($scope.passwordCache() === config.admin.password) { return true; }
-
-      var password = window.prompt("Admin password", "");
-      $scope.passwordCache(password);
-
-      if (password === config.admin.password) { return true; }
-
-      alertSrv.set('Save failed', 'Password incorrect', 'error');
-
-      return false;
-    };
-
     $scope.openSearch = function() {
       $scope.appEvent('show-dash-search');
     };
 
     $scope.saveDashboard = function() {
-      if (!$scope.isAdmin()) { return false; }
-
       var clone = angular.copy($scope.dashboard);
       $scope.db.saveDashboard(clone)
         .then(function(result) {
@@ -96,7 +80,7 @@ function (angular, _, moment, config, store) {
             $location.path(result.url);
           }
 
-          $rootScope.$emit('dashboard-saved', $scope.dashboard);
+          $scope.appEvent('dashboard-saved', $scope.dashboard);
 
         }, function(err) {
           $scope.appEvent('alert-error', ['Save failed', err]);
@@ -104,8 +88,6 @@ function (angular, _, moment, config, store) {
     };
 
     $scope.deleteDashboard = function() {
-      if (!$scope.isAdmin()) { return false; }
-
       $scope.appEvent('confirm-modal', {
         title: 'Delete dashboard',
         text: 'Do you want to delete dashboard ' + $scope.dashboard.title + '?',
@@ -123,6 +105,16 @@ function (angular, _, moment, config, store) {
       });
     };
 
+    $scope.cloneDashboard = function() {
+      var newScope = $rootScope.$new();
+      newScope.clone = angular.copy($scope.dashboard);
+
+      $scope.appEvent('show-modal', {
+        src: './app/features/dashboard/partials/cloneDashboard.html',
+        scope: newScope,
+      });
+    };
+
     $scope.exportDashboard = function() {
       var blob = new Blob([angular.toJson($scope.dashboard, true)], { type: "application/json;charset=utf-8" });
       window.saveAs(blob, $scope.dashboard.title + '-' + new Date().getTime());

+ 26 - 0
src/app/features/dashboard/partials/cloneDashboard.html

@@ -0,0 +1,26 @@
+<div class="modal-body gf-box gf-box-no-margin" ng-controller="CloneDashboardCtrl" ng-init="init();">
+	<div class="gf-box-header">
+		<div class="gf-box-title">
+			<i class="fa fa-copy"></i>
+			Clone Dashboard
+		</div>
+
+		<button class="gf-box-header-close-btn" ng-click="dismiss();">
+			<i class="fa fa-remove"></i>
+		</button>
+	</div>
+
+	<div class="gf-box-body">
+		<div class="text-center">
+			<h4>New title</h4>
+
+			<input type="text" class="input input-fluid" ng-model="clone.title">
+			<br>
+			<br>
+
+			<button class="btn btn-success" ng-click="saveClone();">Clone</button>
+			<button class="btn btn-inverse" ng-click="dismiss();">Cancel</button>
+		</div>
+	</div>
+</div>
+

+ 0 - 5
src/app/features/grafanaDatasource/datasource.js

@@ -45,11 +45,6 @@ function (angular, _, kbn) {
     };
 
     GrafanaDatasource.prototype.saveDashboard = function(dashboard) {
-      // remove id if title has changed
-      if (dashboard.title !== dashboard.originalTitle) {
-        dashboard.id = null;
-      }
-
       return backendSrv.post('/api/dashboards/db/', { dashboard: dashboard })
         .then(function(data) {
           return { title: dashboard.title, url: '/dashboard/db/' + data.slug };

+ 1 - 0
src/app/partials/dashboard_topnav.html

@@ -44,6 +44,7 @@
 						<li><a class="pointer" ng-click="exportDashboard();">Export</a></li>
 						<li><a class="pointer" ng-click="editJson();">View JSON</a></li>
 						<li><a class="pointer" ng-click="deleteDashboard();">Delete dashboard</a></li>
+						<li><a class="pointer" ng-click="cloneDashboard();">Clone dashboard</a></li>
 					</ul>
 				</li>
 			</ul>

+ 0 - 6
src/config.sample.js

@@ -86,12 +86,6 @@ define(['settings'], function(Settings) {
       // Example: "1m", "1h"
       playlist_timespan: "1m",
 
-      // If you want to specify password before saving, please specify it below
-      // The purpose of this password is not security, but to stop some users from accidentally changing dashboards
-      admin: {
-        password: ''
-      },
-
       // Change window title prefix from 'Grafana - <dashboard title>'
       window_title_prefix: 'Grafana - ',
 

+ 2 - 1
src/css/less/forms.less

@@ -1,7 +1,8 @@
 input[type=text].input-fluid {
   width: 100%;
   box-sizing: border-box;
-  padding: 14px;
+  padding: 10px;
+  font-size: 16px;
   -moz-box-sizing: border-box;
   height: 100%;
 }

+ 1 - 1
src/css/less/tightform.less

@@ -9,7 +9,7 @@
     padding: 0; margin: 0;
   }
 
-  &:last-child {
+  &:last-child, &.last {
     border-bottom: 1px solid @grafanaTargetBorder;
   }
 }

+ 0 - 1
src/test/specs/sharePanelCtrl-specs.js

@@ -20,7 +20,6 @@ define([
 
     describe('shareUrl with current time range and panel', function() {
 
-
       it('should generate share url relative time', function() {
         ctx.$location.path('/test');
         ctx.scope.panel = { id: 22 };