DashboardSrv.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import coreModule from 'app/core/core_module';
  2. import { appEvents } from 'app/core/app_events';
  3. import locationUtil from 'app/core/utils/location_util';
  4. import { DashboardModel } from '../state/DashboardModel';
  5. import { removePanel } from '../utils/panel';
  6. import { DashboardMeta } from 'app/types';
  7. export class DashboardSrv {
  8. dashboard: DashboardModel;
  9. /** @ngInject */
  10. constructor(private backendSrv, private $rootScope, private $location) {
  11. appEvents.on('save-dashboard', this.saveDashboard.bind(this), $rootScope);
  12. appEvents.on('panel-change-view', this.onPanelChangeView);
  13. appEvents.on('remove-panel', this.onRemovePanel);
  14. // Export to react
  15. setDashboardSrv(this);
  16. }
  17. create(dashboard: any, meta: DashboardMeta) {
  18. return new DashboardModel(dashboard, meta);
  19. }
  20. setCurrent(dashboard: DashboardModel) {
  21. this.dashboard = dashboard;
  22. }
  23. getCurrent(): DashboardModel {
  24. return this.dashboard;
  25. }
  26. onRemovePanel = (panelId: number) => {
  27. const dashboard = this.getCurrent();
  28. removePanel(dashboard, dashboard.getPanelById(panelId), true);
  29. };
  30. onPanelChangeView = options => {
  31. const urlParams = this.$location.search();
  32. // handle toggle logic
  33. if (options.fullscreen === urlParams.fullscreen) {
  34. // I hate using these truthy converters (!!) but in this case
  35. // I think it's appropriate. edit can be null/false/undefined and
  36. // here i want all of those to compare the same
  37. if (!!options.edit === !!urlParams.edit) {
  38. delete urlParams.fullscreen;
  39. delete urlParams.edit;
  40. delete urlParams.panelId;
  41. delete urlParams.tab;
  42. this.$location.search(urlParams);
  43. return;
  44. }
  45. }
  46. if (options.fullscreen) {
  47. urlParams.fullscreen = true;
  48. } else {
  49. delete urlParams.fullscreen;
  50. }
  51. if (options.edit) {
  52. urlParams.edit = true;
  53. } else {
  54. delete urlParams.edit;
  55. delete urlParams.tab;
  56. }
  57. if (options.panelId || options.panelId === 0) {
  58. urlParams.panelId = options.panelId;
  59. } else {
  60. delete urlParams.panelId;
  61. }
  62. this.$location.search(urlParams);
  63. };
  64. handleSaveDashboardError(clone, options, err) {
  65. options = options || {};
  66. options.overwrite = true;
  67. if (err.data && err.data.status === 'version-mismatch') {
  68. err.isHandled = true;
  69. this.$rootScope.appEvent('confirm-modal', {
  70. title: 'Conflict',
  71. text: 'Someone else has updated this dashboard.',
  72. text2: 'Would you still like to save this dashboard?',
  73. yesText: 'Save & Overwrite',
  74. icon: 'fa-warning',
  75. onConfirm: () => {
  76. this.save(clone, options);
  77. },
  78. });
  79. }
  80. if (err.data && err.data.status === 'name-exists') {
  81. err.isHandled = true;
  82. this.$rootScope.appEvent('confirm-modal', {
  83. title: 'Conflict',
  84. text: 'A dashboard with the same name in selected folder already exists.',
  85. text2: 'Would you still like to save this dashboard?',
  86. yesText: 'Save & Overwrite',
  87. icon: 'fa-warning',
  88. onConfirm: () => {
  89. this.save(clone, options);
  90. },
  91. });
  92. }
  93. if (err.data && err.data.status === 'plugin-dashboard') {
  94. err.isHandled = true;
  95. this.$rootScope.appEvent('confirm-modal', {
  96. title: 'Plugin Dashboard',
  97. text: err.data.message,
  98. text2: 'Your changes will be lost when you update the plugin. Use Save As to create custom version.',
  99. yesText: 'Overwrite',
  100. icon: 'fa-warning',
  101. altActionText: 'Save As',
  102. onAltAction: () => {
  103. this.showSaveAsModal();
  104. },
  105. onConfirm: () => {
  106. this.save(clone, { overwrite: true });
  107. },
  108. });
  109. }
  110. }
  111. postSave(clone, data) {
  112. this.dashboard.version = data.version;
  113. // important that these happens before location redirect below
  114. this.$rootScope.appEvent('dashboard-saved', this.dashboard);
  115. this.$rootScope.appEvent('alert-success', ['Dashboard saved']);
  116. const newUrl = locationUtil.stripBaseFromUrl(data.url);
  117. const currentPath = this.$location.path();
  118. if (newUrl !== currentPath) {
  119. this.$location.url(newUrl).replace();
  120. }
  121. return this.dashboard;
  122. }
  123. save(clone, options) {
  124. options = options || {};
  125. options.folderId = options.folderId >= 0 ? options.folderId : this.dashboard.meta.folderId || clone.folderId;
  126. return this.backendSrv
  127. .saveDashboard(clone, options)
  128. .then(this.postSave.bind(this, clone))
  129. .catch(this.handleSaveDashboardError.bind(this, clone, options));
  130. }
  131. saveDashboard(options?, clone?) {
  132. if (clone) {
  133. this.setCurrent(this.create(clone, this.dashboard.meta));
  134. }
  135. if (this.dashboard.meta.provisioned) {
  136. return this.showDashboardProvisionedModal();
  137. }
  138. if (!this.dashboard.meta.canSave && options.makeEditable !== true) {
  139. return Promise.resolve();
  140. }
  141. if (this.dashboard.title === 'New dashboard') {
  142. return this.showSaveAsModal();
  143. }
  144. if (this.dashboard.version > 0) {
  145. return this.showSaveModal();
  146. }
  147. return this.save(this.dashboard.getSaveModelClone(), options);
  148. }
  149. saveJSONDashboard(json: string) {
  150. return this.save(JSON.parse(json), {});
  151. }
  152. showDashboardProvisionedModal() {
  153. this.$rootScope.appEvent('show-modal', {
  154. templateHtml: '<save-provisioned-dashboard-modal dismiss="dismiss()"></save-provisioned-dashboard-modal>',
  155. });
  156. }
  157. showSaveAsModal() {
  158. this.$rootScope.appEvent('show-modal', {
  159. templateHtml: '<save-dashboard-as-modal dismiss="dismiss()"></save-dashboard-as-modal>',
  160. modalClass: 'modal--narrow',
  161. });
  162. }
  163. showSaveModal() {
  164. this.$rootScope.appEvent('show-modal', {
  165. templateHtml: '<save-dashboard-modal dismiss="dismiss()"></save-dashboard-modal>',
  166. modalClass: 'modal--narrow',
  167. });
  168. }
  169. starDashboard(dashboardId, isStarred) {
  170. let promise;
  171. if (isStarred) {
  172. promise = this.backendSrv.delete('/api/user/stars/dashboard/' + dashboardId).then(() => {
  173. return false;
  174. });
  175. } else {
  176. promise = this.backendSrv.post('/api/user/stars/dashboard/' + dashboardId).then(() => {
  177. return true;
  178. });
  179. }
  180. return promise.then(res => {
  181. if (this.dashboard && this.dashboard.id === dashboardId) {
  182. this.dashboard.meta.isStarred = res;
  183. }
  184. return res;
  185. });
  186. }
  187. }
  188. coreModule.service('dashboardSrv', DashboardSrv);
  189. //
  190. // Code below is to export the service to react components
  191. //
  192. let singletonInstance: DashboardSrv;
  193. export function setDashboardSrv(instance: DashboardSrv) {
  194. singletonInstance = instance;
  195. }
  196. export function getDashboardSrv(): DashboardSrv {
  197. return singletonInstance;
  198. }