ShapingUIUtils.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. 'use strict';
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. /**
  4. * Licensed Materials - Property of IBM
  5. * IBM Cognos Products: Dashboard
  6. * (C) Copyright IBM Corp. 2017, 2020
  7. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  8. */
  9. define(['ca-modeller/shaping', '../nls/StringResources', 'underscore', '../metadata/MetadataColumn', './ShapingConstants', '../../metadataDND/MetadataDNDMapping'], function (Shaping, StringResources, _, MetadataColumn, ShapingConstants, MetadataDNDMapping) {
  10. var INVALID_OBJECT_TYPES_IN_PAYLOAD = ['QuerySubject', 'FolderType', 'Folder', 'DrillGroup'];
  11. var MODEL_FILTER_TYPE = 'Filter';
  12. var DRAG_ORIGIN = {
  13. HIERARCHY: 1,
  14. LEVEL: 2,
  15. SET: 3,
  16. HIERARCHY_MEMBER: 4,
  17. SET_MEMBER: 5
  18. };
  19. var ShapingUIUtils = function () {
  20. function ShapingUIUtils() {
  21. _classCallCheck(this, ShapingUIUtils);
  22. }
  23. /**
  24. * Renders the either the common simple calculation dialog or the global expression editor depending on
  25. * whether the object(s) is a query item or the shaping module, respectively.
  26. * @param itemIds array of dataitemids which are converted to moserObjects before passing
  27. * to the dialog
  28. * @return a promise containing the calculation moser object
  29. */
  30. ShapingUIUtils.renderCalculationDialog = function renderCalculationDialog(moduleAPI, itemIds, isEditCalculation, options) {
  31. var shapingHelper = moduleAPI.getShapingHelper();
  32. //create a global calc from the tree
  33. if (!itemIds) {
  34. return shapingHelper.openExpressionDialog(shapingHelper.getModule());
  35. }
  36. //convert the dataItemIds to moserObjects
  37. var elements = [];
  38. itemIds.forEach(function (element) {
  39. elements.push(moduleAPI.getMoserObject(element));
  40. });
  41. //open the appropriate dialog to create/edit the simple or global calculations
  42. if (isEditCalculation && Shaping.getExpressionType(elements[0]) === 'customCalculation') {
  43. if (options && options.forceGlobal === true) {
  44. return shapingHelper.openExpressionDialog(elements[0], true, { forceGlobal: true });
  45. } else {
  46. return shapingHelper.openExpressionDialog(elements[0], true);
  47. }
  48. }
  49. if (options && options.forceGlobal === true) {
  50. return shapingHelper.openSimpleCalculationDialog(elements, isEditCalculation, { forceGlobal: true });
  51. } else {
  52. return shapingHelper.openSimpleCalculationDialog(elements, isEditCalculation);
  53. }
  54. };
  55. /**
  56. * Renders the Expression dialog
  57. * @param itemIds array of dataitemids which are converted to moserObjects before passing
  58. * to the dialog
  59. * @return a promise containing the calculation moser object
  60. */
  61. ShapingUIUtils.renderMemberCalculationDialog = function renderMemberCalculationDialog(moduleAPI, elementId, itemIds, isEditCalculation) {
  62. //convert the dataItemIds to moserObjects
  63. //var editorMembers = [];
  64. var element;
  65. element = moduleAPI.getMoserObject(elementId);
  66. //NOT sure what to do with the Ids here
  67. // itemIds.forEach(function (id) {
  68. // editorMembers.push(element.getItem(id));
  69. // });
  70. return moduleAPI.getShapingHelper().openExpressionDialog(element, 'modeler-wa-member', StringResources.get('memberCalculationDialogLabels'), null, itemIds, isEditCalculation);
  71. };
  72. /**
  73. * Renders the either the simple custom group dialog or the complete custom group dialog from MUI
  74. * @param moduleAPI
  75. * @param itemId the itemId that can be converted to element
  76. * @param isEdit a boolean value indicate if the current dialog is in edit mode
  77. * @param mode a JSON object describing how the dialog will behave
  78. * @param data a JSON object describing the custom group data info
  79. * @return a promise containing the custom group moser object
  80. */
  81. ShapingUIUtils.renderCustomGroupDialog = function renderCustomGroupDialog(moduleAPI, itemId, isEdit, data, view) {
  82. return moduleAPI.load().then(function () {
  83. if (!view) {
  84. view = {
  85. startView: Shaping.customGroupUtils.getCustomGroupViews().CUSTOM_GROUP_VIEW_NAME
  86. };
  87. }
  88. var shapingHelper = moduleAPI.getShapingHelper();
  89. var element = moduleAPI.getMoserObject(itemId);
  90. return shapingHelper.customGroupAction(element, isEdit, view, data).then(function (response) {
  91. var custom_group_success = function custom_group_success(response) {
  92. var SUCCESS = Shaping.customGroupUtils.getCustomGroupMessageIds().CUSTOM_GROUP_INT_MSG_SUCCESS;
  93. return response.messages && response.messages.length === 1 && response.messages[0].messageId === SUCCESS;
  94. };
  95. if (custom_group_success(response)) {
  96. return response.data;
  97. } else {
  98. return Promise.reject(response);
  99. }
  100. });
  101. });
  102. };
  103. /**
  104. * Validates the supplied customGroup context to see if it supports customGroup creation
  105. * @param moduleAPI
  106. * @param itemId the itemId that can be converted to element
  107. * @param isEdit a boolean value indicate if the current dialog is in edit mode
  108. * @param view a JSON object describing the view type
  109. * @param data a JSON object describing the custom group data info
  110. * @return a promise containing the custom group moser object
  111. */
  112. ShapingUIUtils.validateCustomGroup = function validateCustomGroup(moduleAPI, itemId, isEdit, data) {
  113. var view = {
  114. startView: Shaping.customGroupUtils.getCustomGroupViews().CUSTOM_GROUP_VIEW_NAME
  115. };
  116. var element = moduleAPI.getMoserObject(itemId);
  117. return Shaping.customGroupUtils.validateCustomGroup(element, isEdit, view, data);
  118. };
  119. /**
  120. * Remove column from module, the events of the shaping model change is listened by dashboard in ShapingModelManager
  121. * @param moduleAPI
  122. * @param itemId the itemId that can be converted to element
  123. * @return a promise
  124. */
  125. ShapingUIUtils.removeColumnFromModule = function removeColumnFromModule(moduleAPI, itemId) {
  126. return moduleAPI.load().then(function () {
  127. var shapingHelper = moduleAPI.getShapingHelper();
  128. var element = moduleAPI.getMoserObject(itemId);
  129. shapingHelper.removeFromModule([element]);
  130. });
  131. };
  132. ShapingUIUtils.renderGrid = function renderGrid(moduleAPI, enableDataQuality, domNode, selectedId, dashboard) {
  133. return moduleAPI.load().then(function () {
  134. var shapingHelper = moduleAPI.getShapingHelper();
  135. var props = {
  136. onBeginDrag: function onBeginDrag(payload) {
  137. ShapingUIUtils.onBeginDrag(moduleAPI, dashboard, payload);
  138. },
  139. onEndDrag: function onEndDrag() {
  140. shapingHelper.clearSelection();
  141. },
  142. querySubjects: shapingHelper.getModule().basicGetQuerySubject(),
  143. selectedId: selectedId,
  144. enableDataQuality: enableDataQuality
  145. };
  146. shapingHelper.render(Shaping.ShapingTabGrid, props, domNode);
  147. });
  148. };
  149. ShapingUIUtils.copySelectedTreeItems = function copySelectedTreeItems(moduleAPI) {
  150. var shapingHelper = moduleAPI.getShapingHelper();
  151. var selections = shapingHelper.getSelection();
  152. ShapingUIUtils.selectedTreeItems = selections;
  153. ShapingUIUtils.moduleAPI = moduleAPI;
  154. };
  155. ShapingUIUtils.clearCopiedTreeItems = function clearCopiedTreeItems() {
  156. ShapingUIUtils.selectedTreeItems = undefined;
  157. };
  158. ShapingUIUtils.getCopiedTreeItems = function getCopiedTreeItems(moduleAPI, dashboard) {
  159. // if this is the first "paste" action into an empty widget, the moduleAPI will be null
  160. // because the module has not been established yet. For this case we fall back to the
  161. // moduleAPI used at copy time.
  162. moduleAPI = moduleAPI ? moduleAPI : ShapingUIUtils.moduleAPI;
  163. //Mock a Dnd drag object
  164. var copiedItems = ShapingUIUtils.selectedTreeItems;
  165. if (!copiedItems) {
  166. return null;
  167. }
  168. var payload = {
  169. data: {
  170. items: copiedItems
  171. },
  172. type: 'MODEL_ITEM'
  173. };
  174. ShapingUIUtils.onBeginDrag(moduleAPI, dashboard, payload.data);
  175. return payload;
  176. };
  177. /**
  178. * Renders the metadata tree for the current module
  179. * @param {Object} domNode Dom node in which the tree should be rendered
  180. */
  181. ShapingUIUtils.renderTree = function renderTree(moduleAPI, dashboard, domNode, callback) {
  182. return moduleAPI.load().then(function () {
  183. var enableShowNavigationGroups = !(moduleAPI.isOlapPackage() || moduleAPI.getSourceType() === 'data_asset');
  184. var shapingHelper = moduleAPI.getShapingHelper();
  185. var props = {
  186. onBeginDrag: function onBeginDrag(payload) {
  187. ShapingUIUtils.onBeginDrag(moduleAPI, dashboard, payload);
  188. },
  189. onKeyDown: function onKeyDown(key, payload) {
  190. ShapingUIUtils.onKeyDown(callback, payload, key, moduleAPI, dashboard);
  191. },
  192. onEndDrag: function onEndDrag() {
  193. shapingHelper.clearSelection();
  194. },
  195. showModule: false,
  196. allowReorder: false,
  197. showFullPackage: true,
  198. showNavigationGroups: enableShowNavigationGroups,
  199. forceGlobalCalcs: false
  200. };
  201. shapingHelper.render(Shaping.ShapingTree, props, domNode);
  202. });
  203. };
  204. ShapingUIUtils.onKeyDown = function onKeyDown(callback, payload, key, moduleAPI, dashboard) {
  205. if (!payload.data) {
  206. payload.data = {};
  207. }
  208. payload.data.items = payload;
  209. ShapingUIUtils.onBeginDrag(moduleAPI, dashboard, payload.data);
  210. payload.type = 'MODEL_ITEM';
  211. callback(payload, key);
  212. };
  213. ShapingUIUtils.onBeginDrag = function onBeginDrag(moduleAPI, dashboard, payload) {
  214. var metadataPayload = ShapingUIUtils.getMetadataColumnsFromDnDPayload(moduleAPI, dashboard, payload);
  215. _.extend(payload, metadataPayload);
  216. payload.isValid = function (options) {
  217. console.warn('isValid is depreacted. Use payload.utils.isValid.');
  218. return ShapingUIUtils.isValid(payload, options);
  219. };
  220. };
  221. /**
  222. * Gets an array of MetadataColumns for the items in the drop payload
  223. * @param {[MoserObjects]} moserObjects The moserJS objects for the items being dropped
  224. * @return {[MetadataColumn]} array of MetadataColumn objects
  225. */
  226. ShapingUIUtils.getMetadataColumnsFromDnDPayload = function getMetadataColumnsFromDnDPayload(moduleAPI, dashboard, payload) {
  227. var moserObjects = payload.items;
  228. var metadataDNDMapping = new MetadataDNDMapping(moduleAPI, dashboard, { moserObjects: moserObjects });
  229. var mappResults = metadataDNDMapping.getMappedResults();
  230. var dragColumns = [];
  231. mappResults.forEach(function (result) {
  232. var mappedMetadataColumn = result.mappedMetadataColumn;
  233. var realObj = result.referencedObject || mappedMetadataColumn;
  234. var objType = realObj && realObj.getObjectType(); // there are cases, e.g. folderType, wont have corresponding metadataColumn.
  235. var metadataColumn = null;
  236. if (objType && INVALID_OBJECT_TYPES_IN_PAYLOAD.indexOf(objType) === -1) {
  237. metadataColumn = realObj;
  238. }
  239. var item = {
  240. // @todo deprecate all moser objects
  241. // Use original.metadataColumn and original.children
  242. metadataColumn: metadataColumn,
  243. original: {
  244. metadataColumn: mappedMetadataColumn || metadataColumn,
  245. members: result.members
  246. }
  247. };
  248. dragColumns.push(item);
  249. });
  250. var baseUtils = {
  251. isValid: function isValid(options) {
  252. return ShapingUIUtils.isValid(payload, options);
  253. }
  254. };
  255. var extendedUtils = metadataDNDMapping.getUtils();
  256. return {
  257. sourceId: moduleAPI.getSourceId(),
  258. columns: dragColumns,
  259. utils: _.extend(baseUtils, extendedUtils)
  260. };
  261. };
  262. ShapingUIUtils.isValid = function isValid() {
  263. var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  264. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  265. var originalColumns = null;
  266. var metadataColumns = null;
  267. var columnsWithMembers = null;
  268. var columns = payload.columns;
  269. // TODO: The metadata column could be created with old APIs and mixed with new APIs, so need to consider both cases
  270. // TODO: Should clean up this part once the major clean up code is in for 11.1.7
  271. originalColumns = _.map(columns, function (column) {
  272. return column.original.metadataColumn;
  273. });
  274. metadataColumns = _.map(columns, function (column) {
  275. return column.metadataColumn;
  276. });
  277. columnsWithMembers = metadataColumns.length && payload.utils.getColumnsWithMembers();
  278. if (!originalColumns || originalColumns.length === 0 || !metadataColumns || metadataColumns.length === 0) {
  279. return false;
  280. }
  281. var checkModelFilter = false;
  282. if (options.fromCanvas || options.fromTemplate) {
  283. // If all drag objects are model filter, we don't allow to drop them in canvas, template container or filterDock.
  284. // If model filters are dropped along with other items, then we accept the drop in this case
  285. checkModelFilter = true;
  286. }
  287. var modelFilterCount = 0;
  288. for (var i = 0; i < originalColumns.length; ++i) {
  289. var objType = originalColumns[i] && originalColumns[i].getObjectType();
  290. if (!objType || INVALID_OBJECT_TYPES_IN_PAYLOAD.indexOf(objType) >= 0) {
  291. return false;
  292. }
  293. if (objType === MODEL_FILTER_TYPE) {
  294. // do not accept drop if object is model filter and is being dropped to non-filter data slot in live widget (alone or not)
  295. if (options.dropTarget && options.dropTarget !== ShapingConstants.DROP_TARGET_OPTIONS.FILTER) {
  296. return false;
  297. }
  298. modelFilterCount++;
  299. }
  300. }
  301. if (checkModelFilter && modelFilterCount === originalColumns.length) {
  302. return false;
  303. }
  304. // Check the metadata columns.
  305. if (this.isInvalidDragObject(metadataColumns, null, columnsWithMembers)) {
  306. return false;
  307. }
  308. return true;
  309. };
  310. /**
  311. * Check whether the drag object violate any rules and pass the result back to the
  312. * DnD manager so that it can determine if the drop is allowed.
  313. * For OLAP sources, within the same hierarchy the following rules need to be applied:
  314. * 1) one hierarchy is allowed, i.e the current hierarchy is only allowed to be dropped once
  315. * 2) one named set is allowed
  316. * 3) any number of members from a hierarchy or a namedSet is allowed
  317. * 4) any number of levels or properties is allowed
  318. * 5) No mix is allowed between 1), 2), 3) and 4), but mix is allowed between 1) and 3), or 2) and 3)
  319. * @param metadataColumns - the metadata columns used to compare with the cached dimensions and hierarchies.
  320. * @param oHierarchyMap - Map of hierarchies, used to check conflict of the existing metadata and the new dragged dataItem.
  321. * @example
  322. * {
  323. * hierarchyID1: [DRAG_ORIGIN.HIERARCHY, DRAG_ORIGIN.HIERARCHY_MEMBER],
  324. * hierarchyID2: [DRAG_ORIGIN.LEVEL]
  325. * }
  326. * @param metadataColumnsWithMembers - Object with keys are column IDs and values are the members from the corresponding column.
  327. * @param metadataColumnsExist - boolean to indicate whether the first parameter: metadataColumns are from current dragged columns or projected columns in Viz.
  328. * The flag is required with member support. For example drop a member to one slot, then a member to another slot of the same hierarchy.
  329. * Once the same column were mapped to 2 slots, there is nothing to indicate the 2 columns are built from members.
  330. * @returns {boolean} true if invalid drag metadata item is found.
  331. */
  332. ShapingUIUtils.isInvalidDragObject = function isInvalidDragObject(metadataColumns, oHierarchyMap, metadataColumnsWithMembers, metadataColumnsExist) {
  333. var _this = this;
  334. if ((!oHierarchyMap || _.isEmpty(oHierarchyMap)) && metadataColumnsExist) {
  335. // metadataColumns are already projected
  336. return false;
  337. }
  338. oHierarchyMap = oHierarchyMap || {};
  339. return _.some(metadataColumns, function (metadataColumn) {
  340. return !metadataColumn || _this._canNotDropMetadata(oHierarchyMap, metadataColumn, metadataColumnsWithMembers, metadataColumnsExist);
  341. });
  342. };
  343. ShapingUIUtils._canNotDropMetadata = function _canNotDropMetadata(oHierarchyMap, metadataColumn, metadataColumnsWithMembers, metadataColumnsExist) {
  344. var hierarchyID = void 0;
  345. var canNotDropMetadata = void 0;
  346. metadataColumnsWithMembers = metadataColumnsWithMembers || {};
  347. var isMemberColumn = function isMemberColumn(hierarchyID) {
  348. return metadataColumnsWithMembers[hierarchyID] !== undefined && !_.isEmpty(metadataColumnsWithMembers[hierarchyID]);
  349. };
  350. var origin = null;
  351. if (metadataColumn.isHierarchy && metadataColumn.isHierarchy()) {
  352. hierarchyID = metadataColumn.getId();
  353. origin = isMemberColumn(hierarchyID) ? DRAG_ORIGIN.HIERARCHY_MEMBER : DRAG_ORIGIN.HIERARCHY;
  354. } else if (!metadataColumn.isMissing() && (metadataColumn.isLevel() || metadataColumn.isProperty() || metadataColumn.isNamedSet())) {
  355. var parent = metadataColumn.getParent();
  356. hierarchyID = parent.getId();
  357. if (!hierarchyID) {
  358. hierarchyID = metadataColumn.getReferencedHierarchyId();
  359. }
  360. var isNamedSet = metadataColumn.isNamedSet();
  361. origin = isNamedSet ? isMemberColumn(hierarchyID) ? DRAG_ORIGIN.SET_MEMBER : DRAG_ORIGIN.SET : DRAG_ORIGIN.LEVEL;
  362. } else {
  363. // For all the other cases, return false so the drag and drop is not restricted.
  364. return false;
  365. }
  366. canNotDropMetadata = this._isRestrictedMetadata(oHierarchyMap, hierarchyID, origin);
  367. if (!metadataColumnsExist) {
  368. this._addMetadataToMap(oHierarchyMap, hierarchyID, origin);
  369. }
  370. return canNotDropMetadata;
  371. };
  372. /**
  373. * @returns {boolean} true if the metadata item is restricted to be dropped.
  374. */
  375. ShapingUIUtils._isRestrictedMetadata = function _isRestrictedMetadata(oHierarchyMap, hierarchyID, origin) {
  376. var hierarchyOrigins = oHierarchyMap[hierarchyID];
  377. if (!hierarchyOrigins || !hierarchyOrigins.length) {
  378. return false;
  379. }
  380. switch (origin) {
  381. case DRAG_ORIGIN.HIERARCHY:
  382. return _.some(hierarchyOrigins, function (hierarchyOrigin) {
  383. return hierarchyOrigin !== DRAG_ORIGIN.HIERARCHY_MEMBER;
  384. });
  385. case DRAG_ORIGIN.HIERARCHY_MEMBER:
  386. return _.some(hierarchyOrigins, function (hierarchyOrigin) {
  387. return hierarchyOrigin !== DRAG_ORIGIN.HIERARCHY && hierarchyOrigin !== DRAG_ORIGIN.HIERARCHY_MEMBER;
  388. });
  389. case DRAG_ORIGIN.SET:
  390. return _.some(hierarchyOrigins, function (hierarchyOrigin) {
  391. return hierarchyOrigin !== DRAG_ORIGIN.SET_MEMBER;
  392. });
  393. case DRAG_ORIGIN.SET_MEMBER:
  394. return _.some(hierarchyOrigins, function (hierarchyOrigin) {
  395. return hierarchyOrigin !== DRAG_ORIGIN.SET && hierarchyOrigin === DRAG_ORIGIN.SET_MEMBER;
  396. });
  397. case DRAG_ORIGIN.LEVEL:
  398. return _.some(hierarchyOrigins, function (hierarchyOrigin) {
  399. return hierarchyOrigin !== DRAG_ORIGIN.LEVEL;
  400. });
  401. default:
  402. return false;
  403. }
  404. };
  405. // Store the metadata to map and use the map to check next metadata object.
  406. ShapingUIUtils._addMetadataToMap = function _addMetadataToMap(oHierarchyMap, hierID, origin) {
  407. if (!oHierarchyMap[hierID]) {
  408. oHierarchyMap[hierID] = [];
  409. }
  410. if (oHierarchyMap[hierID].indexOf(origin) === -1) {
  411. oHierarchyMap[hierID].push(origin);
  412. }
  413. };
  414. return ShapingUIUtils;
  415. }();
  416. return ShapingUIUtils;
  417. });
  418. //# sourceMappingURL=ShapingUIUtils.js.map