audit_ctrl.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. ///<reference path="../../../headers/common.d.ts" />
  2. import _ from 'lodash';
  3. import angular from 'angular';
  4. import moment from 'moment';
  5. import coreModule from 'app/core/core_module';
  6. import {DashboardModel} from '../model';
  7. import {AuditLogOpts, RevisionsModel} from './models';
  8. export class AuditLogCtrl {
  9. appending: boolean;
  10. dashboard: DashboardModel;
  11. delta: { basic: string; html: string; };
  12. diff: string;
  13. limit: number;
  14. loading: boolean;
  15. max: number;
  16. mode: string;
  17. orderBy: string;
  18. revisions: RevisionsModel[];
  19. selected: number[];
  20. start: number;
  21. /** @ngInject */
  22. constructor(private $scope,
  23. private $rootScope,
  24. private $window,
  25. private $q,
  26. private contextSrv,
  27. private auditSrv) {
  28. $scope.ctrl = this;
  29. this.appending = false;
  30. this.dashboard = $scope.dashboard;
  31. this.diff = 'basic';
  32. this.limit = 10;
  33. this.loading = false;
  34. this.max = 2;
  35. this.mode = 'list';
  36. this.orderBy = 'version';
  37. this.selected = [];
  38. this.start = 0;
  39. this.resetFromSource();
  40. $scope.$watch('ctrl.mode', newVal => {
  41. $window.scrollTo(0, 0);
  42. if (newVal === 'list') {
  43. this.reset();
  44. }
  45. });
  46. $rootScope.onAppEvent('dashboard-saved', this.onDashboardSaved.bind(this));
  47. }
  48. addToLog() {
  49. this.start = this.start + this.limit;
  50. this.getLog(true);
  51. }
  52. compareRevisionStateChanged(revision: any) {
  53. if (revision.checked) {
  54. this.selected.push(revision.version);
  55. } else {
  56. _.remove(this.selected, version => version === revision.version);
  57. }
  58. this.selected = _.sortBy(this.selected);
  59. }
  60. compareRevisionDisabled(checked: boolean) {
  61. return (this.selected.length === this.max && !checked) || this.revisions.length === 1;
  62. }
  63. formatDate(date) {
  64. date = moment.isMoment(date) ? date : moment(date);
  65. const format = 'YYYY-MM-DD HH:mm:ss';
  66. return this.dashboard.timezone === 'browser' ?
  67. moment(date).format(format) :
  68. moment.utc(date).format(format);
  69. }
  70. formatBasicDate(date) {
  71. const now = this.dashboard.timezone === 'browser' ? moment() : moment.utc();
  72. const then = this.dashboard.timezone === 'browser' ? moment(date) : moment.utc(date);
  73. return then.from(now);
  74. }
  75. getDiff(diff: string) {
  76. if (!this.isComparable()) { return; } // disable button but not tooltip
  77. this.diff = diff;
  78. this.mode = 'compare';
  79. this.loading = true;
  80. // instead of using lodash to find min/max we use the index
  81. // due to the array being sorted in ascending order
  82. const compare = {
  83. new: this.selected[1],
  84. original: this.selected[0],
  85. };
  86. if (this.delta[this.diff]) {
  87. this.loading = false;
  88. return this.$q.when(this.delta[this.diff]);
  89. } else {
  90. return this.auditSrv.compareVersions(this.dashboard, compare, diff).then(response => {
  91. this.delta[this.diff] = response;
  92. }).catch(err => {
  93. this.mode = 'list';
  94. this.$rootScope.appEvent('alert-error', ['There was an error fetching the diff', (err.message || err)]);
  95. }).finally(() => { this.loading = false; });
  96. }
  97. }
  98. getLog(append = false) {
  99. this.loading = !append;
  100. this.appending = append;
  101. const options: AuditLogOpts = {
  102. limit: this.limit,
  103. start: this.start,
  104. orderBy: this.orderBy,
  105. };
  106. return this.auditSrv.getAuditLog(this.dashboard, options).then(revisions => {
  107. const formattedRevisions = _.flow(
  108. _.partialRight(_.map, rev => _.extend({}, rev, {
  109. checked: false,
  110. message: (revision => {
  111. if (revision.message === '') {
  112. if (revision.version === 1) {
  113. return 'Dashboard\'s initial save';
  114. }
  115. if (revision.restoredFrom > 0) {
  116. return `Restored from version ${revision.restoredFrom}`;
  117. }
  118. if (revision.parentVersion === 0) {
  119. return 'Dashboard overwritten';
  120. }
  121. return 'Dashboard saved';
  122. }
  123. return revision.message;
  124. })(rev),
  125. })))(revisions);
  126. this.revisions = append ? this.revisions.concat(formattedRevisions) : formattedRevisions;
  127. }).catch(err => {
  128. this.$rootScope.appEvent('alert-error', ['There was an error fetching the audit log', (err.message || err)]);
  129. }).finally(() => {
  130. this.loading = false;
  131. this.appending = false;
  132. });
  133. }
  134. getMeta(version: number, property: string) {
  135. const revision = _.find(this.revisions, rev => rev.version === version);
  136. return revision[property];
  137. }
  138. isOriginalCurrent() {
  139. return this.selected[1] === this.dashboard.version;
  140. }
  141. isComparable() {
  142. const isParamLength = this.selected.length === 2;
  143. const areNumbers = this.selected.every(version => _.isNumber(version));
  144. const areValidVersions = _.filter(this.revisions, revision => {
  145. return revision.version === this.selected[0] || revision.version === this.selected[1];
  146. }).length === 2;
  147. return isParamLength && areNumbers && areValidVersions;
  148. }
  149. isLastPage() {
  150. return _.find(this.revisions, rev => rev.version === 1);
  151. }
  152. onDashboardSaved() {
  153. this.$rootScope.appEvent('hide-dash-editor');
  154. }
  155. reset() {
  156. this.delta = { basic: '', html: '' };
  157. this.diff = 'basic';
  158. this.mode = 'list';
  159. this.revisions = _.map(this.revisions, rev => _.extend({}, rev, { checked: false }));
  160. this.selected = [];
  161. this.start = 0;
  162. }
  163. resetFromSource() {
  164. this.revisions = [];
  165. return this.getLog().then(this.reset.bind(this));
  166. }
  167. restore(version: number) {
  168. this.$rootScope.appEvent('confirm-modal', {
  169. title: 'Restore version',
  170. text: '',
  171. text2: `Are you sure you want to restore the dashboard to version ${version}? All unsaved changes will be lost.`,
  172. icon: 'fa-rotate-right',
  173. yesText: `Yes, restore to version ${version}`,
  174. onConfirm: this.restoreConfirm.bind(this, version),
  175. });
  176. }
  177. restoreConfirm(version: number) {
  178. this.loading = true;
  179. return this.auditSrv.restoreDashboard(this.dashboard, version).then(response => {
  180. this.revisions.unshift({
  181. id: this.revisions[0].id + 1,
  182. checked: false,
  183. dashboardId: this.dashboard.id,
  184. parentVersion: version,
  185. version: this.revisions[0].version + 1,
  186. created: new Date(),
  187. createdBy: this.contextSrv.user.name,
  188. message: `Restored from version ${version}`,
  189. });
  190. this.reset();
  191. const restoredData = response.dashboard;
  192. this.dashboard = restoredData.dashboard;
  193. this.dashboard.meta = restoredData.meta;
  194. this.$scope.setupDashboard(restoredData);
  195. }).catch(err => {
  196. this.$rootScope.appEvent('alert-error', ['There was an error restoring the dashboard', (err.message || err)]);
  197. }).finally(() => { this.loading = false; });
  198. }
  199. }
  200. coreModule.controller('AuditLogCtrl', AuditLogCtrl);