SaveActionHandler.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. 'use strict';
  2. /*
  3. *+------------------------------------------------------------------------+
  4. *| Licensed Materials - Property of IBM
  5. *|
  6. *| IBM Cognos Products: dashboard-core
  7. *|
  8. *| (C) Copyright IBM Corp. 2015, 2020
  9. *|
  10. *| US Government Users Restricted Rights - Use, duplication or disclosure
  11. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  12. *+------------------------------------------------------------------------+
  13. */
  14. /*
  15. * !!WARNING!! !!WARNING!! !!WARNING!!
  16. *
  17. * This file is used/inherited in Storytelling.
  18. * Please make sure changes in here do not break Storytelling or at least let us know.
  19. */
  20. define(['underscore', '../../../lib/@waca/core-client/js/core-client/ui/core/Class', '../../../app/util/ErrorUtils', '../../../app/util/GlassUtil', '../../../dashboard/glass/util/SaveActionUtil'], function (_, BaseClass, ErrorUtils, GlassUtil, SaveActionUtil) {
  21. var saveResponseFields = ['status', 'name', 'saveRequest', 'assetId', 'url', 'savedSpec', 'cmSpec'];
  22. var ActionHandler = BaseClass.extend({
  23. getContentView: function getContentView(context) {
  24. return context.glassContext.appController.currentAppView.currentContentView;
  25. },
  26. getDashboardView: function getDashboardView(context) {
  27. return this.getContentView(context);
  28. },
  29. getStringResourcesService: function getStringResourcesService(context) {
  30. return this.getContentView(context).services.getSvcSync('.StringResources');
  31. },
  32. _getDashboardApi: function _getDashboardApi(context) {
  33. return this.getContentView(context).getDashboardApi();
  34. },
  35. _getContextContent: function _getContextContent(context) {
  36. return this.getContentView(context).getContent();
  37. },
  38. /**
  39. * Handle the save button click. If the dashboard has an id, then we will save the dashboard. otherwise we will do a save as
  40. */
  41. onSelectItem: function onSelectItem(context) {
  42. var id = this.getContentView(context).getBoardId();
  43. var saveMode = context.target.itemId === 'com.ibm.bi.dashboard.save';
  44. if (id && saveMode && this.getContentView(context).canAuthor()) {
  45. this.save(context, true, true /*bShowToast*/);
  46. } else {
  47. this.saveAs(context, id);
  48. }
  49. },
  50. isItemVisible: function isItemVisible(context) {
  51. // explore and dashboard shares the save button, but dashboard capability should not affect explore user to save exploration
  52. // TODO: clean this up once explore has their own button contribution
  53. var appViewType = this.getContentView(context).getDashboardApi().getType().toUpperCase();
  54. var saveMode = context.target.itemId === 'com.ibm.bi.dashboard.save';
  55. if (appViewType === 'EXPLORE') {
  56. return saveMode ? this.getContentView(context).canAuthor() : true;
  57. }
  58. return saveMode ? this.getContentView(context).canAuthor() && ErrorUtils.hasCapability(context.glassContext, 'canAuthorDashboard') : ErrorUtils.hasCapability(context.glassContext, 'canAuthorDashboard');
  59. },
  60. /**
  61. * Hook to override require calls for the unit test.
  62. */
  63. _require: function _require(modules, callback) {
  64. return require(modules, callback);
  65. },
  66. /**
  67. * Prompt the user for a location and do a save as.
  68. * @param {Object} context of selection (passed during onSelect)
  69. * @param {string} id The Board Id
  70. */
  71. saveAs: function saveAs(context, id) {
  72. var _this = this;
  73. var dashboardView = this.getDashboardView(context);
  74. var defaultName = dashboardView.boardModel.name;
  75. return this._getAncestors(context, id).then(function (ancestors) {
  76. return context.glassContext.getSvc('.ContentDialogFactory').then(function (dialogFactory) {
  77. _this._saveAsDialog = dialogFactory.createSaveAsDialog({
  78. glassContext: context.glassContext,
  79. defaultFileName: defaultName,
  80. service: {
  81. context: context,
  82. save: _this.onSaveAs.bind(_this)
  83. },
  84. ancestors: ancestors
  85. });
  86. _this._saveAsDialog.open();
  87. });
  88. });
  89. },
  90. _getAncestors: function _getAncestors(context, id) {
  91. return this._getContentService(context).then(function (contentService) {
  92. return contentService.getAncestors({ id: id });
  93. });
  94. },
  95. onSaveAs: function onSaveAs(service, selection, name, overwrite) {
  96. var context = service.context;
  97. return this._getSaveFeature(context).saveAs(name, selection.url, overwrite, saveResponseFields).then(this._onSaveAsSuccess.bind(this, context), this._onSaveAsError.bind(this, context)).then(this._invokeLifeCycleHandlers.bind(this, 'saveas.after', context)).finally(this._saveAsDialog.hide.bind(this._saveAsDialog));
  98. },
  99. _onSaveAsError: function _onSaveAsError(context, errorObject) {
  100. var errorCode = ErrorUtils.getErrorCode(errorObject.saveRequest);
  101. if (errorCode === 'cmDuplicateName') {
  102. var error = {
  103. 'isDuplicate': true
  104. };
  105. return Promise.reject(error);
  106. } else if (errorCode.lastIndexOf('cmAddWithReplaceFailed', 0) === 0 || errorCode.lastIndexOf('cmInvalidReference', 0) === 0) {
  107. // error codes: cmAddWithReplaceFailed, cmAddWithReplaceFailedBadClass, cmInvalidReference2
  108. error = {
  109. 'isReplaceFailed': true
  110. };
  111. return Promise.reject(error);
  112. } else {
  113. ErrorUtils.showError(context.glassContext, errorObject.saveRequest, errorObject.cmSpec);
  114. return Promise.resolve();
  115. }
  116. },
  117. _onSaveAsSuccess: function _onSaveAsSuccess(context) {
  118. var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  119. var contentView = this.getContentView(context);
  120. contentView.setSavedInstance(params.savedSpec);
  121. // Call Content Service GET to trigger MRU
  122. return this._getContentService(context).then(function (oContentService) {
  123. return oContentService.onSaveAsSuccess({ url: params.url });
  124. }).done(function (dbInfo) {
  125. var dashboard = this._getDashboardApi(context);
  126. if (dbInfo) {
  127. if (dbInfo.id) {
  128. contentView.boardModel.set({ id: dbInfo.id }, { silent: true });
  129. }
  130. if (dbInfo.permissions) {
  131. contentView.setPermissions(dbInfo.permissions);
  132. }
  133. }
  134. // Update the name in the dashboard model
  135. contentView.boardModel.set({
  136. name: params.name
  137. }, {
  138. silent: true
  139. });
  140. var segment = dashboard.getFeature('segment');
  141. var segmentType = { type: 'Updated Object' };
  142. segment.track('savedAs', function () {
  143. return segmentType;
  144. });
  145. // Clear dirty flag in the UI
  146. dashboard.getFeature('DashboardState').setDirty(false);
  147. contentView.boardController.undoRedoController.registerSaveAction();
  148. // Update current cached app view to prevent Defect 235654
  149. context.glassContext.appController.updateCurrentCachedAppView();
  150. var appConfig = this._getAppConfig(context);
  151. if (appConfig && appConfig.saveThumbnail) {
  152. this._saveThumbnail(context, dbInfo && dbInfo.id);
  153. }
  154. var stringResources = this.getStringResourcesService(context);
  155. context.glassContext.appController.showToast(stringResources.get('dashboard_save_success'));
  156. return this._invokeSaveServiceHandlers(context, {
  157. saveAs: true,
  158. stringResources: stringResources,
  159. context: context,
  160. spec: dashboard.getFeature('Serializer').toJSON()
  161. });
  162. }.bind(this));
  163. },
  164. /**
  165. * Get glass service .DashboardContent
  166. * @private
  167. * @async
  168. * @return {Promise<Object>}
  169. */
  170. _getContentService: function _getContentService(context) {
  171. return context.glassContext.getSvc('.DashboardContent');
  172. },
  173. save: function save(context, bInvokeSaveServiceHandlers, bShowToast) {
  174. var _this2 = this;
  175. var dashboardView = this.getDashboardView(context);
  176. var dashboard = dashboardView.getDashboardApi();
  177. var id = dashboardView.getBoardId();
  178. var boardModel = dashboardView.boardModel;
  179. var boardSpec = dashboard.getFeature('Serializer').toJSON();
  180. var _saveSpec = null;
  181. SaveActionUtil.cleanUnusedCustomColors(dashboard, boardSpec, boardModel);
  182. return this._getSaveFeature(context).save(saveResponseFields).then(function (_ref) {
  183. var savedSpec = _ref.savedSpec;
  184. if (savedSpec) {
  185. _saveSpec = savedSpec;
  186. }
  187. return _this2._invokeLifeCycleHandlers('save.after', context);
  188. }).then(function () {
  189. var stringResources = _this2.getStringResourcesService(context);
  190. var segment = dashboard.getFeature('segment');
  191. var segmentType = { type: 'Updated Object' };
  192. segment.track('saved', function () {
  193. return segmentType;
  194. });
  195. // Clear dirty flag in the UI
  196. dashboard.getFeature('DashboardState').setDirty(false);
  197. dashboardView.boardController.undoRedoController.registerSaveAction();
  198. dashboardView.setSavedInstance(_saveSpec);
  199. var appConfig = _this2._getAppConfig(context);
  200. if (appConfig && appConfig.saveThumbnail) {
  201. _this2._saveThumbnail(context, id);
  202. }
  203. if (bShowToast) {
  204. context.glassContext.appController.showToast(stringResources.get('dashboard_save_success'));
  205. }
  206. if (bInvokeSaveServiceHandlers) {
  207. _this2._invokeSaveServiceHandlers(context, {
  208. saveAs: false,
  209. stringResources: stringResources,
  210. context: context,
  211. spec: boardSpec
  212. });
  213. }
  214. }).catch(function (request) {
  215. var code = ErrorUtils.showError(context.glassContext, request);
  216. // The object is deleted.. try a save as
  217. if (code === 'cmEmptySelection') {
  218. _this2.saveAs(context);
  219. }
  220. });
  221. },
  222. _invokeLifeCycleHandlers: function _invokeLifeCycleHandlers(name, context) {
  223. var dashboardApi = this.getDashboardView(context).getDashboardApi();
  224. var lifeCycle = dashboardApi.getFeature('LifeCycle');
  225. return lifeCycle.invokeHandlers(name).catch(function (error) {
  226. var logger = dashboardApi.getGlassCoreSvc('.Logger');
  227. logger.error('[SaveActionHandler] Life cycle handlers error', error);
  228. });
  229. },
  230. /**
  231. * Loop over the services that are in the saveServices collection to call their 'onDashboardSave' method
  232. */
  233. _invokeSaveServiceHandlers: function _invokeSaveServiceHandlers(context, options) {
  234. var dashboard = this._getDashboardApi(context);
  235. return this._getSaveServiceExtensionCollection(context.glassContext, dashboard).then(function (collection) {
  236. var promises = [];
  237. if (collection) {
  238. // provide a call that will update the dashboard spec in CM if needed
  239. options.resaveDashboardSpecCallback = this.save.bind(this, context, false, false /*bShowToast*/);
  240. collection.forEach(function (extension) {
  241. var saveService = dashboard.getFeature(extension.name);
  242. if (saveService && saveService.onDashboardSave) {
  243. promises.push(saveService.onDashboardSave(options));
  244. }
  245. });
  246. }
  247. return Promise.all(promises);
  248. }.bind(this));
  249. },
  250. _getSaveServiceExtensionCollection: function _getSaveServiceExtensionCollection(glassContext, dashboard) {
  251. var saveServices = dashboard.getCollectionConfig('saveServices');
  252. if (saveServices) {
  253. return Promise.all([glassContext.appController.findCollection(saveServices.id)]).then(function (extensions) {
  254. return extensions[0];
  255. });
  256. }
  257. return Promise.resolve();
  258. },
  259. _getAppConfig: function _getAppConfig(context) {
  260. var content = this._getContextContent(context);
  261. return content && content.options && content.options.config;
  262. },
  263. _saveThumbnail: function _saveThumbnail(context, id) {
  264. var _this3 = this;
  265. return this._createThumbnail(context).then(function (thumbnailDataUri) {
  266. return _this3._getContentService(context).then(function (contentService) {
  267. return contentService.saveThumbnail({ thumbnailDataUri: thumbnailDataUri, id: id });
  268. });
  269. });
  270. },
  271. _createThumbnail: function _createThumbnail(context) {
  272. var dashboardView = this.getDashboardView(context);
  273. var thumbnailSvc = dashboardView.getDashboardApi().getFeature('.Thumbnail');
  274. return thumbnailSvc && thumbnailSvc.generateImage(dashboardView.el);
  275. },
  276. _getSaveFeature: function _getSaveFeature(context) {
  277. return this._getDashboardApi(context).getFeature('Save');
  278. }
  279. });
  280. return ActionHandler;
  281. });
  282. //# sourceMappingURL=SaveActionHandler.js.map