@sapui5/sap.fe.core 1.99.0 → 1.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/package.json +4 -2
  2. package/src/sap/fe/core/.library +1 -1
  3. package/src/sap/fe/core/AnnotationHelper.js +1 -69
  4. package/src/sap/fe/core/AnnotationHelper.ts +0 -70
  5. package/src/sap/fe/core/AppComponent.js +388 -399
  6. package/src/sap/fe/core/AppComponent.ts +403 -0
  7. package/src/sap/fe/core/AppStateHandler.js +127 -158
  8. package/src/sap/fe/core/BaseController.js +82 -58
  9. package/src/sap/fe/core/BaseController.ts +68 -0
  10. package/src/sap/fe/core/CommonUtils.js +111 -20
  11. package/src/sap/fe/core/CommonUtils.ts +131 -32
  12. package/src/sap/fe/core/ExtensionAPI.js +278 -274
  13. package/src/sap/fe/core/ExtensionAPI.ts +250 -0
  14. package/src/sap/fe/core/PageController.js +157 -29
  15. package/src/sap/fe/core/PageController.ts +60 -32
  16. package/src/sap/fe/core/RouterProxy.js +694 -756
  17. package/src/sap/fe/core/RouterProxy.ts +11 -9
  18. package/src/sap/fe/core/Synchronization.js +21 -31
  19. package/src/sap/fe/core/TemplateComponent.js +1 -1
  20. package/src/sap/fe/core/TemplateComponent.ts +10 -3
  21. package/src/sap/fe/core/TemplateModel.js +20 -38
  22. package/src/sap/fe/core/TemplateModel.ts +1 -1
  23. package/src/sap/fe/core/TransactionHelper.js +1354 -1370
  24. package/src/sap/fe/core/TransactionHelper.ts +33 -22
  25. package/src/sap/fe/core/actions/collaboration/ActivitySync.js +392 -0
  26. package/src/sap/fe/core/actions/collaboration/ActivitySync.ts +355 -0
  27. package/src/sap/fe/core/actions/collaboration/CollaborationCommon.js +136 -0
  28. package/src/sap/fe/core/actions/collaboration/CollaborationCommon.ts +119 -0
  29. package/src/sap/fe/core/actions/collaboration/Manage.js +262 -0
  30. package/src/sap/fe/core/actions/collaboration/Manage.ts +244 -0
  31. package/src/sap/fe/core/actions/collaboration/ManageDialog.fragment.xml +103 -0
  32. package/src/sap/fe/core/actions/collaboration/UserDetails.fragment.xml +13 -0
  33. package/src/sap/fe/core/actions/draft.js +30 -68
  34. package/src/sap/fe/core/actions/draft.ts +44 -71
  35. package/src/sap/fe/core/actions/messageHandling.js +55 -36
  36. package/src/sap/fe/core/actions/messageHandling.ts +66 -46
  37. package/src/sap/fe/core/actions/operations.js +34 -15
  38. package/src/sap/fe/core/actions/operations.ts +48 -18
  39. package/src/sap/fe/core/actions/sticky.js +17 -4
  40. package/src/sap/fe/core/actions/sticky.ts +21 -4
  41. package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.js +69 -66
  42. package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.ts +65 -0
  43. package/src/sap/fe/core/controllerextensions/EditFlow.js +1593 -1669
  44. package/src/sap/fe/core/controllerextensions/EditFlow.ts +1672 -0
  45. package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +79 -54
  46. package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.ts +61 -0
  47. package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +718 -779
  48. package/src/sap/fe/core/controllerextensions/InternalEditFlow.ts +783 -0
  49. package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +796 -816
  50. package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.ts +803 -0
  51. package/src/sap/fe/core/controllerextensions/InternalRouting.js +1004 -1005
  52. package/src/sap/fe/core/controllerextensions/InternalRouting.ts +978 -0
  53. package/src/sap/fe/core/controllerextensions/KPIManagement.js +487 -521
  54. package/src/sap/fe/core/controllerextensions/KPIManagement.ts +2 -2
  55. package/src/sap/fe/core/controllerextensions/MassEdit.js +141 -169
  56. package/src/sap/fe/core/controllerextensions/MassEdit.ts +156 -0
  57. package/src/sap/fe/core/controllerextensions/MessageHandler.js +233 -244
  58. package/src/sap/fe/core/controllerextensions/MessageHandler.ts +225 -0
  59. package/src/sap/fe/core/controllerextensions/PageReady.js +301 -336
  60. package/src/sap/fe/core/controllerextensions/PageReady.ts +12 -10
  61. package/src/sap/fe/core/controllerextensions/Paginator.js +188 -175
  62. package/src/sap/fe/core/controllerextensions/Paginator.ts +163 -0
  63. package/src/sap/fe/core/controllerextensions/Placeholder.js +157 -149
  64. package/src/sap/fe/core/controllerextensions/Placeholder.ts +151 -0
  65. package/src/sap/fe/core/controllerextensions/Routing.js +144 -125
  66. package/src/sap/fe/core/controllerextensions/Routing.ts +132 -0
  67. package/src/sap/fe/core/controllerextensions/RoutingListener.js +7 -6
  68. package/src/sap/fe/core/controllerextensions/RoutingListener.ts +3 -0
  69. package/src/sap/fe/core/controllerextensions/Share.js +230 -268
  70. package/src/sap/fe/core/controllerextensions/Share.ts +231 -0
  71. package/src/sap/fe/core/controllerextensions/SideEffects.js +592 -633
  72. package/src/sap/fe/core/controllerextensions/SideEffects.ts +8 -7
  73. package/src/sap/fe/core/controllerextensions/ViewState.js +788 -806
  74. package/src/sap/fe/core/controllerextensions/ViewState.ts +805 -0
  75. package/src/sap/fe/core/controls/ActionParameterDialog.fragment.xml +2 -2
  76. package/src/sap/fe/core/controls/ActionPartial.fragment.xml +2 -2
  77. package/src/sap/fe/core/controls/CommandExecution.js +67 -66
  78. package/src/sap/fe/core/controls/CommandExecution.ts +72 -0
  79. package/src/sap/fe/core/controls/ConditionalWrapper.js +90 -75
  80. package/src/sap/fe/core/controls/ConditionalWrapper.ts +83 -0
  81. package/src/sap/fe/core/controls/CustomQuickViewPage.js +130 -125
  82. package/src/sap/fe/core/controls/CustomQuickViewPage.ts +126 -0
  83. package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +103 -112
  84. package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.ts +101 -0
  85. package/src/sap/fe/core/controls/FieldWrapper.js +122 -135
  86. package/src/sap/fe/core/controls/FieldWrapper.ts +115 -0
  87. package/src/sap/fe/core/controls/FilterBar.js +162 -159
  88. package/src/sap/fe/core/controls/FilterBar.ts +143 -0
  89. package/src/sap/fe/core/controls/FormElementWrapper.js +45 -39
  90. package/src/sap/fe/core/controls/FormElementWrapper.ts +40 -0
  91. package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +37 -42
  92. package/src/sap/fe/core/controls/MultiValueParameterDelegate.ts +31 -0
  93. package/src/sap/fe/core/controls/filterbar/FilterContainer.js +126 -116
  94. package/src/sap/fe/core/controls/filterbar/FilterContainer.ts +98 -0
  95. package/src/sap/fe/core/controls/filterbar/VisualFilter.js +241 -255
  96. package/src/sap/fe/core/controls/filterbar/VisualFilter.ts +245 -0
  97. package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +150 -141
  98. package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.ts +125 -0
  99. package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +335 -322
  100. package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.ts +337 -0
  101. package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +74 -74
  102. package/src/sap/fe/core/controls/massEdit/MassEditHandlers.ts +70 -0
  103. package/src/sap/fe/core/converters/ConverterContext.js +348 -379
  104. package/src/sap/fe/core/converters/ConverterContext.ts +19 -16
  105. package/src/sap/fe/core/converters/ManifestSettings.js +12 -1
  106. package/src/sap/fe/core/converters/ManifestSettings.ts +12 -1
  107. package/src/sap/fe/core/converters/ManifestWrapper.js +354 -378
  108. package/src/sap/fe/core/converters/ManifestWrapper.ts +10 -0
  109. package/src/sap/fe/core/converters/MetaModelConverter.js +6 -4
  110. package/src/sap/fe/core/converters/MetaModelConverter.ts +5 -2
  111. package/src/sap/fe/core/converters/TemplateConverter.js +1 -1
  112. package/src/sap/fe/core/converters/TemplateConverter.ts +2 -1
  113. package/src/sap/fe/core/converters/annotations/DataField.js +26 -12
  114. package/src/sap/fe/core/converters/annotations/DataField.ts +37 -13
  115. package/src/sap/fe/core/converters/controls/Common/Form.js +2 -2
  116. package/src/sap/fe/core/converters/controls/Common/Form.ts +4 -2
  117. package/src/sap/fe/core/converters/controls/Common/KPI.js +1 -1
  118. package/src/sap/fe/core/converters/controls/Common/KPI.ts +3 -2
  119. package/src/sap/fe/core/converters/controls/Common/Table.js +130 -23
  120. package/src/sap/fe/core/converters/controls/Common/Table.ts +134 -29
  121. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +118 -53
  122. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +156 -93
  123. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +161 -207
  124. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +148 -206
  125. package/src/sap/fe/core/converters/helpers/Aggregation.js +115 -133
  126. package/src/sap/fe/core/converters/helpers/BindingHelper.js +20 -6
  127. package/src/sap/fe/core/converters/helpers/BindingHelper.ts +16 -4
  128. package/src/sap/fe/core/converters/helpers/ConfigurableObject.js +12 -1
  129. package/src/sap/fe/core/converters/helpers/ConfigurableObject.ts +11 -0
  130. package/src/sap/fe/core/converters/helpers/Key.js +42 -57
  131. package/src/sap/fe/core/converters/helpers/Key.ts +1 -1
  132. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +25 -4
  133. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +20 -3
  134. package/src/sap/fe/core/converters/templates/ListReportConverter.js +10 -4
  135. package/src/sap/fe/core/converters/templates/ListReportConverter.ts +15 -4
  136. package/src/sap/fe/core/converters/templates/ObjectPageConverter.js +7 -5
  137. package/src/sap/fe/core/converters/templates/ObjectPageConverter.ts +5 -1
  138. package/src/sap/fe/core/designtime/AppComponent.designtime.js +92 -98
  139. package/src/sap/fe/core/designtime/AppComponent.designtime.ts +91 -0
  140. package/src/sap/fe/core/formatters/CollaborationFormatter.js +104 -0
  141. package/src/sap/fe/core/formatters/CollaborationFormatter.ts +60 -0
  142. package/src/sap/fe/core/formatters/TableFormatter.js +53 -2
  143. package/src/sap/fe/core/formatters/TableFormatter.ts +51 -0
  144. package/src/sap/fe/core/fpm/Component.js +50 -54
  145. package/src/sap/fe/core/fpm/Component.ts +48 -0
  146. package/src/sap/fe/core/helpers/AppStartupHelper.js +323 -309
  147. package/src/sap/fe/core/helpers/AppStartupHelper.ts +363 -337
  148. package/src/sap/fe/core/helpers/BindingExpression.js +7 -7
  149. package/src/sap/fe/core/helpers/BindingExpression.ts +7 -7
  150. package/src/sap/fe/core/helpers/ClassSupport.js +186 -62
  151. package/src/sap/fe/core/helpers/ClassSupport.ts +168 -52
  152. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +5 -4
  153. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.ts +1 -5
  154. package/src/sap/fe/core/helpers/FPMHelper.js +1 -1
  155. package/src/sap/fe/core/helpers/FPMHelper.ts +1 -1
  156. package/src/sap/fe/core/helpers/MassEditHelper.js +601 -684
  157. package/src/sap/fe/core/helpers/MassEditHelper.ts +699 -0
  158. package/src/sap/fe/core/helpers/ModelHelper.js +25 -1
  159. package/src/sap/fe/core/helpers/ModelHelper.ts +23 -0
  160. package/src/sap/fe/core/helpers/SemanticDateOperators.js +5 -1
  161. package/src/sap/fe/core/helpers/SemanticDateOperators.ts +4 -0
  162. package/src/sap/fe/core/library.js +426 -451
  163. package/src/sap/fe/core/library.support.js +22 -33
  164. package/src/sap/fe/core/library.support.ts +23 -0
  165. package/src/sap/fe/core/library.ts +420 -0
  166. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +5 -3
  167. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +4 -1
  168. package/src/sap/fe/core/messagebundle.properties +22 -5
  169. package/src/sap/fe/core/messagebundle_ar.properties +27 -7
  170. package/src/sap/fe/core/messagebundle_bg.properties +27 -7
  171. package/src/sap/fe/core/messagebundle_ca.properties +27 -7
  172. package/src/sap/fe/core/messagebundle_cs.properties +27 -7
  173. package/src/sap/fe/core/messagebundle_cy.properties +27 -7
  174. package/src/sap/fe/core/messagebundle_da.properties +27 -7
  175. package/src/sap/fe/core/messagebundle_de.properties +27 -7
  176. package/src/sap/fe/core/messagebundle_el.properties +27 -7
  177. package/src/sap/fe/core/messagebundle_en.properties +27 -7
  178. package/src/sap/fe/core/messagebundle_en_GB.properties +27 -7
  179. package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +27 -7
  180. package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +27 -7
  181. package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +27 -7
  182. package/src/sap/fe/core/messagebundle_es.properties +27 -7
  183. package/src/sap/fe/core/messagebundle_es_MX.properties +27 -7
  184. package/src/sap/fe/core/messagebundle_et.properties +27 -7
  185. package/src/sap/fe/core/messagebundle_fi.properties +28 -8
  186. package/src/sap/fe/core/messagebundle_fr.properties +28 -8
  187. package/src/sap/fe/core/messagebundle_fr_CA.properties +29 -9
  188. package/src/sap/fe/core/messagebundle_hi.properties +27 -7
  189. package/src/sap/fe/core/messagebundle_hr.properties +27 -7
  190. package/src/sap/fe/core/messagebundle_hu.properties +27 -7
  191. package/src/sap/fe/core/messagebundle_id.properties +27 -7
  192. package/src/sap/fe/core/messagebundle_it.properties +27 -7
  193. package/src/sap/fe/core/messagebundle_iw.properties +27 -7
  194. package/src/sap/fe/core/messagebundle_ja.properties +26 -6
  195. package/src/sap/fe/core/messagebundle_kk.properties +27 -7
  196. package/src/sap/fe/core/messagebundle_ko.properties +27 -7
  197. package/src/sap/fe/core/messagebundle_lt.properties +27 -7
  198. package/src/sap/fe/core/messagebundle_lv.properties +28 -8
  199. package/src/sap/fe/core/messagebundle_ms.properties +27 -7
  200. package/src/sap/fe/core/messagebundle_nl.properties +27 -7
  201. package/src/sap/fe/core/messagebundle_no.properties +27 -7
  202. package/src/sap/fe/core/messagebundle_pl.properties +27 -7
  203. package/src/sap/fe/core/messagebundle_pt.properties +28 -8
  204. package/src/sap/fe/core/messagebundle_pt_PT.properties +27 -7
  205. package/src/sap/fe/core/messagebundle_ro.properties +27 -7
  206. package/src/sap/fe/core/messagebundle_ru.properties +27 -7
  207. package/src/sap/fe/core/messagebundle_sh.properties +27 -7
  208. package/src/sap/fe/core/messagebundle_sk.properties +27 -7
  209. package/src/sap/fe/core/messagebundle_sl.properties +27 -7
  210. package/src/sap/fe/core/messagebundle_sv.properties +27 -7
  211. package/src/sap/fe/core/messagebundle_th.properties +26 -6
  212. package/src/sap/fe/core/messagebundle_tr.properties +30 -10
  213. package/src/sap/fe/core/messagebundle_uk.properties +27 -7
  214. package/src/sap/fe/core/messagebundle_vi.properties +27 -7
  215. package/src/sap/fe/core/messagebundle_zh_CN.properties +27 -7
  216. package/src/sap/fe/core/messagebundle_zh_TW.properties +27 -7
  217. package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +45 -71
  218. package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +154 -192
  219. package/src/sap/fe/core/services/CacheHandlerServiceFactory.ts +4 -4
  220. package/src/sap/fe/core/services/EnvironmentServiceFactory.js +66 -92
  221. package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +1 -1
  222. package/src/sap/fe/core/services/NavigationServiceFactory.js +284 -339
  223. package/src/sap/fe/core/services/NavigationServiceFactory.ts +10 -13
  224. package/src/sap/fe/core/services/ResourceModelServiceFactory.js +67 -102
  225. package/src/sap/fe/core/services/ResourceModelServiceFactory.ts +5 -2
  226. package/src/sap/fe/core/services/RoutingServiceFactory.js +754 -814
  227. package/src/sap/fe/core/services/RoutingServiceFactory.ts +13 -13
  228. package/src/sap/fe/core/services/ShellServicesFactory.js +649 -736
  229. package/src/sap/fe/core/services/ShellServicesFactory.ts +7 -4
  230. package/src/sap/fe/core/services/SideEffectsServiceFactory.js +567 -592
  231. package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +22 -3
  232. package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +354 -386
  233. package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +21 -14
  234. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +1 -1
  235. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.ts +1 -1
  236. package/src/sap/fe/core/support/CommonHelper.js +1 -1
  237. package/src/sap/fe/core/support/CommonHelper.ts +1 -1
  238. package/src/sap/fe/core/support/Diagnostics.js +36 -48
  239. package/src/sap/fe/core/templating/DataModelPathHelper.js +93 -85
  240. package/src/sap/fe/core/templating/DataModelPathHelper.ts +104 -95
  241. package/src/sap/fe/core/templating/FilterHelper.js +25 -32
  242. package/src/sap/fe/core/templating/FilterHelper.ts +36 -35
  243. package/src/sap/fe/core/templating/PropertyHelper.js +2 -2
  244. package/src/sap/fe/core/templating/PropertyHelper.ts +1 -1
  245. package/src/sap/fe/core/templating/UIFormatters.js +32 -26
  246. package/src/sap/fe/core/templating/UIFormatters.ts +37 -24
@@ -0,0 +1,355 @@
1
+ import Event from "sap/ui/base/Event";
2
+ import Control from "sap/ui/core/Control";
3
+ import SapPcpWebSocket from "sap/ui/core/ws/SapPcpWebSocket";
4
+ import MessageBox from "sap/m/MessageBox";
5
+ import { Activity, CollaborationUtils, Message, User, UserActivity } from "sap/fe/core/actions/collaboration/CollaborationCommon";
6
+ import View from "sap/ui/core/mvc/View";
7
+ import UriParameters from "sap/base/util/UriParameters";
8
+ import ODataMetaModel from "sap/ui/model/odata/v4/ODataMetaModel";
9
+ import Log from "sap/base/Log";
10
+ import Context from "sap/ui/model/odata/v4/Context";
11
+ import ResourceBundle from "sap/base/i18n/ResourceBundle";
12
+
13
+ const CONNECTED = "/collaboration/connected";
14
+ const CONNECTION = "/collaboration/connection";
15
+ const MYACTIVITY = "/collaboration/myActivity";
16
+ const ACTIVEUSERS = "/collaboration/activeUsers";
17
+ const ACTIVITIES = "/collaboration/activities";
18
+
19
+ export const isConnected = function(control: Control): boolean {
20
+ return !!control.getModel("internal").getProperty(CONNECTED);
21
+ };
22
+
23
+ export const send = function(control: Control, action: Activity, content: string | string[] | Context | Context[] | undefined) {
24
+ if (isConnected(control)) {
25
+ const contentArray = Array.isArray(content) ? content : [content];
26
+ let clientContent: string = "";
27
+ contentArray.forEach(function(c) {
28
+ if (c) {
29
+ clientContent += clientContent ? "|" : "";
30
+ clientContent += typeof c === "string" ? c : c.getPath();
31
+ }
32
+ });
33
+
34
+ const internalModel = control.getModel("internal") as any;
35
+ const webSocket = internalModel.getProperty(CONNECTION) as SapPcpWebSocket;
36
+
37
+ if (action === Activity.LiveChange) {
38
+ // To avoid unnecessary traffic we keep track of live changes and send it only once
39
+ const myActivity = internalModel.getProperty(MYACTIVITY);
40
+ if (myActivity === clientContent) {
41
+ return;
42
+ } else {
43
+ internalModel.setProperty(MYACTIVITY, clientContent);
44
+ }
45
+ } else {
46
+ // user finished the activity
47
+ internalModel.setProperty(MYACTIVITY, null);
48
+ }
49
+
50
+ webSocket.send("", {
51
+ clientAction: action,
52
+ clientContent: clientContent
53
+ });
54
+
55
+ if (action === Activity.Activate || action === Activity.Discard) {
56
+ disconnect(control);
57
+ }
58
+ }
59
+ };
60
+
61
+ export const connect = async function(view: View) {
62
+ const internalModel: any = view.getModel("internal");
63
+ const me = CollaborationUtils.getMe(view);
64
+ if (internalModel.getProperty(CONNECTION)) {
65
+ // connection already established
66
+ return;
67
+ }
68
+
69
+ // Retrieving ME from shell service
70
+ if (!me) {
71
+ // no me = no shell = not sure what to do
72
+ return;
73
+ }
74
+
75
+ const activeUsers: any[] = [me];
76
+ internalModel.setProperty("/collaboration", { activeUsers: activeUsers, activities: {} });
77
+ const bindingContext = view.getBindingContext() as Context;
78
+ const webSocketBaseURL = bindingContext
79
+ .getModel()
80
+ .getMetaModel()
81
+ .getObject("/@com.sap.vocabularies.Common.v1.WebSocketBaseURL");
82
+
83
+ if (!webSocketBaseURL) {
84
+ return;
85
+ }
86
+
87
+ const sDraftUUID = await bindingContext.requestProperty("DraftAdministrativeData/DraftUUID");
88
+ if (!sDraftUUID) {
89
+ return;
90
+ }
91
+
92
+ const webSocket = establishWebSocketConnection(webSocketBaseURL, sDraftUUID, view);
93
+
94
+ webSocket.attachMessage(function(event: Event) {
95
+ const message: Message = event.getParameter("pcpFields");
96
+ messageReceive(message, view, webSocket);
97
+ });
98
+ };
99
+
100
+ export const disconnect = function(control: Control) {
101
+ const internalModel = control.getModel("internal") as any;
102
+ const webSocket = internalModel.getProperty(CONNECTION) as SapPcpWebSocket;
103
+ webSocket.close();
104
+ internalModel.setProperty(CONNECTION, null);
105
+ };
106
+
107
+ function establishWebSocketConnection(socketBaseURL: string, draftUUID: string, view: View) {
108
+ const internalModel: any = view.getModel("internal");
109
+ const hostLocation = window.location;
110
+ let socketURI;
111
+
112
+ // Support useBackendUrl for local testing
113
+ const useBackendUrl = UriParameters.fromQuery(window.location.search).get("useBackendUrl");
114
+ if (useBackendUrl) {
115
+ socketURI = useBackendUrl.replace("https", "wss");
116
+ } else {
117
+ socketURI = hostLocation.protocol === "https:" ? "wss:" : "ws:";
118
+ socketURI += "//" + hostLocation.host;
119
+ }
120
+
121
+ socketURI += (socketBaseURL.startsWith("/") ? "" : "/") + socketBaseURL + "?draft=" + draftUUID;
122
+
123
+ const webSocket = new SapPcpWebSocket(socketURI, ["v10.pcp.sap.com"]); // TODO: use enum
124
+ internalModel.setProperty(CONNECTION, webSocket);
125
+
126
+ webSocket.attachOpen(function() {
127
+ internalModel.setProperty(CONNECTED, true);
128
+ });
129
+
130
+ webSocket.attachError(function() {
131
+ Log.error("The connection to the websocket channel " + socketBaseURL + " could not be established");
132
+ internalModel.setProperty(CONNECTED, false);
133
+ });
134
+
135
+ webSocket.attachClose(function() {
136
+ internalModel.setProperty(CONNECTED, false);
137
+ });
138
+
139
+ return webSocket;
140
+ }
141
+
142
+ function messageReceive(message: Message, view: View, webSocket: any) {
143
+ const internalModel: any = view.getModel("internal");
144
+ let activeUsers: User[] = internalModel.getProperty(ACTIVEUSERS);
145
+ let activities: UserActivity[];
146
+ let activityKey: string;
147
+ const metaPath: string = message.clientContent && (view.getModel().getMetaModel() as ODataMetaModel).getMetaPath(message.clientContent);
148
+ message.userAction = message.userAction || message.clientAction;
149
+
150
+ const sender: User = {
151
+ id: message.userID,
152
+ name: message.userDescription,
153
+ initials: CollaborationUtils.formatInitials(message.userDescription),
154
+ color: CollaborationUtils.getUserColor(message.userID, activeUsers, [])
155
+ };
156
+
157
+ switch (message.userAction) {
158
+ case Activity.Join:
159
+ case Activity.JoinEcho:
160
+ if (activeUsers.findIndex(user => user.id === sender.id) === -1) {
161
+ activeUsers.push(sender);
162
+ internalModel.setProperty(ACTIVEUSERS, activeUsers);
163
+ }
164
+
165
+ if (message.userAction === Activity.Join) {
166
+ // we echo our existence to the newly entered user and also send the current activity if there is any
167
+ webSocket.send("", {
168
+ clientAction: Activity.JoinEcho,
169
+ clientContent: internalModel.getProperty(MYACTIVITY)
170
+ });
171
+ // TODO: echo current activity
172
+ }
173
+
174
+ if (message.userAction === Activity.JoinEcho) {
175
+ if (message.clientContent) {
176
+ // another user was already typing therefore I want to see his activity immediately. Calling me again as a live change
177
+ message.userAction = Activity.LiveChange;
178
+ messageReceive(message, view, webSocket);
179
+ }
180
+ }
181
+
182
+ break;
183
+ case Activity.Leave:
184
+ // Removing the active user. Not removing "me" if I had the screen open in another session
185
+ activeUsers = activeUsers.filter(user => user.id !== sender.id || user.me);
186
+ internalModel.setProperty(ACTIVEUSERS, activeUsers);
187
+ const allActivities = internalModel.getProperty(ACTIVITIES) || {};
188
+ const removeUserActivities = function(bag: any) {
189
+ if (Array.isArray(bag)) {
190
+ return bag.filter(activity => activity.id !== sender.id);
191
+ } else {
192
+ for (const p in bag) {
193
+ bag[p] = removeUserActivities(bag[p]);
194
+ }
195
+ return bag;
196
+ }
197
+ };
198
+ removeUserActivities(allActivities);
199
+ internalModel.setProperty(ACTIVITIES, allActivities);
200
+ break;
201
+
202
+ case Activity.Change:
203
+ let currentActivities: any[] = internalModel.getProperty(ACTIVITIES + metaPath) || [];
204
+ activityKey = getActivityKey(message.clientContent);
205
+ currentActivities = currentActivities.filter(activity => activity.key !== activityKey);
206
+ internalModel.setProperty(ACTIVITIES + metaPath, currentActivities);
207
+
208
+ update(view, message, metaPath, Activity.Change);
209
+ break;
210
+ case Activity.Create:
211
+ // For create we actually just need to refresh the table
212
+ update(view, message, metaPath, Activity.Create);
213
+ break;
214
+ case Activity.Delete:
215
+ // For now also refresh the page but in case of deletion we need to inform the user
216
+ update(view, message, metaPath, Activity.Delete);
217
+ break;
218
+ case Activity.Activate:
219
+ disconnect(view);
220
+ MessageBox.information(getText("C_COLLABORATIONDRAFT_ACTIVATE", sender.name));
221
+ navigate(message.clientContent, view);
222
+ break;
223
+ case Activity.Discard:
224
+ disconnect(view);
225
+ MessageBox.information(getText("C_COLLABORATIONDRAFT_DISCARD", sender.name));
226
+ navigate(message.clientContent, view);
227
+ break;
228
+ /*
229
+ // TODO: Action to be implemented
230
+ case Activity.Action:
231
+ // Just for test reasons show a toast - to be checked with UX
232
+ MessageToast.show("User " + sender.name + " has executed action " + metaPath.split("|")[0]);
233
+ //update(view, message, metaPath, Activity.Delete);
234
+ break;
235
+ */
236
+ case Activity.LiveChange:
237
+ const activity: UserActivity = sender;
238
+ activity.key = getActivityKey(message.clientContent);
239
+
240
+ // stupid JSON model...
241
+ let initJSONModel: string = "";
242
+ const parts = metaPath.split("/");
243
+ for (let i = 1; i < parts.length - 1; i++) {
244
+ initJSONModel += "/" + parts[i];
245
+ if (!internalModel.getProperty(ACTIVITIES + initJSONModel)) {
246
+ internalModel.setProperty(ACTIVITIES + initJSONModel, {});
247
+ }
248
+ }
249
+
250
+ activities = internalModel.getProperty(ACTIVITIES + metaPath);
251
+ activities = activities ? activities.slice() : [];
252
+ activities.push(activity);
253
+ internalModel.setProperty(ACTIVITIES + metaPath, activities);
254
+ break;
255
+ case Activity.Undo:
256
+ // The user did a change but reverted it, therefore unblock the control
257
+ activities = internalModel.getProperty(ACTIVITIES + metaPath);
258
+ activityKey = getActivityKey(message.clientContent);
259
+ internalModel.setProperty(
260
+ ACTIVITIES + metaPath,
261
+ activities.filter(a => a.key !== activityKey)
262
+ );
263
+ break;
264
+ }
265
+ }
266
+
267
+ function update(view: View, message: Message, metaPath: string, action: Activity) {
268
+ const appComponent = CollaborationUtils.getAppComponent(view);
269
+ const metaModel = view.getModel().getMetaModel() as ODataMetaModel;
270
+ const currentPage = getCurrentPage(view);
271
+ const sideEffectsService = (appComponent as any).getSideEffectsService(); // TODO: add to appcomponent
272
+ const currentContext = currentPage.getBindingContext();
273
+ const currentPath = currentContext.getPath();
274
+ const currentMetaPath = metaModel.getMetaPath(currentPath);
275
+ let changedDocument = message.clientContent;
276
+
277
+ if (action === Activity.Delete) {
278
+ // check if user currently displays one deleted object
279
+ const deletedObjects = message.clientContent.split("|");
280
+ if (deletedObjects.findIndex(deletedObject => currentPath.startsWith(deletedObject)) > -1) {
281
+ // any other user deleted the object I'm currently looking at. Inform the user we will navigate to root now
282
+ MessageBox.information(getText("C_COLLABORATIONDRAFT_DELETE", message.userDescription), {
283
+ onClose: function() {
284
+ // TODO: Navigate to root? To be implemented
285
+ history.back();
286
+ }
287
+ });
288
+ }
289
+ // TODO: For now just take the first object to get the meta path and do a full refresh of the table
290
+ changedDocument = deletedObjects[0];
291
+ }
292
+
293
+ if (changedDocument.startsWith(currentPath)) {
294
+ // Execute SideEffects (TODO for Meet there should be one central method)
295
+ const activityPath = metaPath.replace(currentMetaPath, "").slice(1);
296
+ if (activityPath) {
297
+ // Request also the property itself
298
+ const sideEffects: any[] = [
299
+ {
300
+ $PropertyPath: activityPath
301
+ }
302
+ ];
303
+ const entityType = sideEffectsService.getEntityTypeFromContext(currentContext);
304
+ const entityTypeSideEffects = sideEffectsService.getODataEntitySideEffects(entityType);
305
+ // Poor man solution without checking source targets, just for POC, this is throw-way coding only
306
+ const object: any = Object; // just to overcome TS issues, will be anyway replaced
307
+ const relevantSideEffects = object.fromEntries(
308
+ object
309
+ .entries(entityTypeSideEffects)
310
+ .filter((x: any[]) => x[1].SourceProperties?.findIndex((source: any) => source.value === activityPath) > -1)
311
+ );
312
+ for (const p in relevantSideEffects) {
313
+ relevantSideEffects[p].TargetProperties.forEach(function(targetProperty: any) {
314
+ sideEffects.push({
315
+ $PropertyPath: targetProperty
316
+ });
317
+ });
318
+ }
319
+ sideEffectsService.requestSideEffects(sideEffects, currentContext, "$auto");
320
+ }
321
+ }
322
+
323
+ // Simulate any change so the edit flow shows the draft indicator and sets the page to dirty
324
+ currentPage.getController().editFlow.updateDocument(currentContext, Promise.resolve());
325
+ }
326
+
327
+ function navigate(path: string, view: View) {
328
+ // TODO: routing.navigate doesn't consider semantic bookmarking
329
+ const currentPage = getCurrentPage(view);
330
+ const targetContext = view
331
+ .getModel()
332
+ .bindContext(path)
333
+ .getBoundContext();
334
+ currentPage.getController().routing.navigate(targetContext);
335
+ }
336
+
337
+ function getCurrentPage(view: View) {
338
+ const appComponent = CollaborationUtils.getAppComponent(view);
339
+ // TODO: isn't there an easier way to get the current page?
340
+ // TODO: What about FCL?
341
+ return (appComponent.getRootControl() as any)
342
+ .getContent()[0]
343
+ .getCurrentPage()
344
+ .getComponentInstance()
345
+ .getRootControl();
346
+ }
347
+
348
+ function getActivityKey(x: string): string {
349
+ return x.substring(x.lastIndexOf("(") + 1, x.lastIndexOf(")"));
350
+ }
351
+
352
+ function getText(textId: string, ...args: string[]): string {
353
+ const oResourceModel = sap.ui.getCore().getLibraryResourceBundle("sap.fe.core") as ResourceBundle;
354
+ return oResourceModel.getText(textId, args);
355
+ }
@@ -0,0 +1,136 @@
1
+ /*!
2
+ * SAP UI development toolkit for HTML5 (SAPUI5)
3
+ * (c) Copyright 2009-2021 SAP SE. All rights reserved
4
+ */
5
+ sap.ui.define(["sap/ui/core/Component"], function (Component) {
6
+ "use strict";
7
+
8
+ var _exports = {};
9
+
10
+ var Role;
11
+
12
+ (function (Role) {
13
+ Role["Owner"] = "O";
14
+ Role["Edit"] = "E";
15
+ })(Role || (Role = {}));
16
+
17
+ _exports.Role = Role;
18
+ var Activity;
19
+
20
+ (function (Activity) {
21
+ Activity["Join"] = "JOIN";
22
+ Activity["JoinEcho"] = "JOINECHO";
23
+ Activity["Leave"] = "LEAVE";
24
+ Activity["Change"] = "CHANGE";
25
+ Activity["Create"] = "CREATE";
26
+ Activity["Delete"] = "DELETE";
27
+ Activity["Action"] = "ACTION";
28
+ Activity["LiveChange"] = "LIVECHANGE";
29
+ Activity["Activate"] = "ACTIVATE";
30
+ Activity["Discard"] = "DISCARD";
31
+ Activity["Undo"] = "UNDO";
32
+ })(Activity || (Activity = {}));
33
+
34
+ _exports.Activity = Activity;
35
+
36
+ function formatInitials(fullName) {
37
+ // remove titles - those are the ones from S/4 to be checked if there are others
38
+ var academicTitles = ["Dr.", "Prof.", "Prof. Dr.", "B.A.", "MBA", "Ph.D."];
39
+ academicTitles.forEach(function (academicTitle) {
40
+ fullName = fullName.replace(academicTitle, "");
41
+ });
42
+ var initials;
43
+ var parts = fullName.trimStart().split(" ");
44
+
45
+ if (parts.length > 1) {
46
+ var _parts$shift, _parts$pop;
47
+
48
+ initials = ((parts === null || parts === void 0 ? void 0 : (_parts$shift = parts.shift()) === null || _parts$shift === void 0 ? void 0 : _parts$shift.charAt(0)) || "") + ((_parts$pop = parts.pop()) === null || _parts$pop === void 0 ? void 0 : _parts$pop.charAt(0));
49
+ } else {
50
+ initials = fullName.substring(0, 2);
51
+ }
52
+
53
+ return initials.toUpperCase();
54
+ }
55
+
56
+ function getUserColor(UserID, activeUsers, invitedUsers) {
57
+ // search if user is known
58
+ var user = activeUsers.find(function (u) {
59
+ return u.id === UserID;
60
+ });
61
+
62
+ if (user) {
63
+ return user.color;
64
+ } else {
65
+ var _loop = function (i) {
66
+ if (activeUsers.findIndex(function (u) {
67
+ return u.color === i;
68
+ }) === -1 && invitedUsers.findIndex(function (u) {
69
+ return u.color === i;
70
+ }) === -1) {
71
+ return {
72
+ v: i
73
+ };
74
+ }
75
+ };
76
+
77
+ // search for next free color
78
+ for (var i = 1; i <= 10; i++) {
79
+ var _ret = _loop(i);
80
+
81
+ if (typeof _ret === "object") return _ret.v;
82
+ } // this seems to be a popular object :) for now just return 10 for all.
83
+ // for invited we should start from 1 again so the colors are different
84
+
85
+
86
+ return 10;
87
+ }
88
+ } // copied from CommonUtils. Due to a cycle dependency I can't use CommonUtils here.
89
+ // That's to be fixed. the discard popover thingy shouldn't be in the common utils at all
90
+
91
+
92
+ function getAppComponent(oControl) {
93
+ if (oControl.isA("sap.fe.core.AppComponent")) {
94
+ return oControl;
95
+ }
96
+
97
+ var oOwner = Component.getOwnerComponentFor(oControl);
98
+
99
+ if (!oOwner) {
100
+ return oControl;
101
+ } else {
102
+ return getAppComponent(oOwner);
103
+ }
104
+ }
105
+
106
+ function getMe(view) {
107
+ var shellServiceHelper = getAppComponent(view).getShellServices();
108
+
109
+ if (!shellServiceHelper || !shellServiceHelper.hasUShell()) {
110
+ throw "No Shell... No User";
111
+ }
112
+
113
+ var me = {
114
+ initials: shellServiceHelper.getUser().getInitials(),
115
+ id: shellServiceHelper.getUser().getId(),
116
+ name: shellServiceHelper.getUser().getFullName(),
117
+ color: 6,
118
+ // same color as FLP...
119
+ role: Role.Owner,
120
+ me: true
121
+ };
122
+ me.name += " (You)"; // TODO resource bundle
123
+
124
+ return me;
125
+ }
126
+
127
+ var CollaborationUtils = {
128
+ formatInitials: formatInitials,
129
+ getUserColor: getUserColor,
130
+ getMe: getMe,
131
+ getAppComponent: getAppComponent
132
+ };
133
+ _exports.CollaborationUtils = CollaborationUtils;
134
+ return _exports;
135
+ }, false);
136
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbGxhYm9yYXRpb25Db21tb24udHMiXSwibmFtZXMiOlsiUm9sZSIsIkFjdGl2aXR5IiwiZm9ybWF0SW5pdGlhbHMiLCJmdWxsTmFtZSIsImFjYWRlbWljVGl0bGVzIiwiZm9yRWFjaCIsImFjYWRlbWljVGl0bGUiLCJyZXBsYWNlIiwiaW5pdGlhbHMiLCJwYXJ0cyIsInRyaW1TdGFydCIsInNwbGl0IiwibGVuZ3RoIiwic2hpZnQiLCJjaGFyQXQiLCJwb3AiLCJzdWJzdHJpbmciLCJ0b1VwcGVyQ2FzZSIsImdldFVzZXJDb2xvciIsIlVzZXJJRCIsImFjdGl2ZVVzZXJzIiwiaW52aXRlZFVzZXJzIiwidXNlciIsImZpbmQiLCJ1IiwiaWQiLCJjb2xvciIsImkiLCJmaW5kSW5kZXgiLCJnZXRBcHBDb21wb25lbnQiLCJvQ29udHJvbCIsImlzQSIsIm9Pd25lciIsIkNvbXBvbmVudCIsImdldE93bmVyQ29tcG9uZW50Rm9yIiwiZ2V0TWUiLCJ2aWV3Iiwic2hlbGxTZXJ2aWNlSGVscGVyIiwiZ2V0U2hlbGxTZXJ2aWNlcyIsImhhc1VTaGVsbCIsIm1lIiwiZ2V0VXNlciIsImdldEluaXRpYWxzIiwiZ2V0SWQiLCJuYW1lIiwiZ2V0RnVsbE5hbWUiLCJyb2xlIiwiT3duZXIiLCJDb2xsYWJvcmF0aW9uVXRpbHMiXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7Ozs7OztNQWdCWUEsSTs7YUFBQUEsSTtBQUFBQSxJQUFBQSxJO0FBQUFBLElBQUFBLEk7S0FBQUEsSSxLQUFBQSxJOzs7TUFLQUMsUTs7YUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7QUFBQUEsSUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7QUFBQUEsSUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7QUFBQUEsSUFBQUEsUTtBQUFBQSxJQUFBQSxRO0FBQUFBLElBQUFBLFE7S0FBQUEsUSxLQUFBQSxROzs7O0FBc0JaLFdBQVNDLGNBQVQs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QUFDRSxTQUFuQixFQUE1QixFQUE0RDtBQUMzRCxZQUFNLHFCQUFOO0FBQ0E7O0FBQ0QsUUFBTUMsRUFBUSxHQUFHO0FBQ2hCaEMsTUFBQUEsUUFBUSxFQUFFNkIsa0JBQWtCLENBQUNJLE9BQW5CLEdBQTZCQyxXQUE3QixFQURNO0FBRWhCakIsTUFBQUEsRUFBRSxFQUFFWSxrQkFBa0IsQ0FBQ0ksT0FBbkIsR0FBNkJFLEtBQTdCLEVBRlk7QUFHaEJDLE1BQUFBLElBQUksRUFBRVAsa0JBQWtCLENBQUNJLE9BQW5CLEdBQTZCSSxXQUE3QixFQUhVO0FBSWhCbkIsTUFBQUEsS0FBSyxFQUFFLENBSlM7QUFJTjtBQUNWb0IsTUFBQUEsSUFBSSxFQUFFOUMsSUFBSSxDQUFDK0MsS0FMSztBQU1oQlAsTUFBQUEsRUFBRSxFQUFFO0FBTlksS0FBakI7QUFRQUEsSUFBQUEsRUFBRSxDQUFDSSxJQUFILElBQVcsUUFBWCxDQWJnQyxDQWFYOztBQUNyQixXQUFPSixFQUFQO0FBQ0E7O0FBRU0sTUFBTVEsa0JBQWtCLEdBQUc7QUFDakM5QyxJQUFBQSxjQUFjLEVBQUVBLGNBRGlCO0FBRWpDZ0IsSUFBQUEsWUFBWSxFQUFFQSxZQUZtQjtBQUdqQ2lCLElBQUFBLEtBQUssRUFBRUEsS0FIMEI7QUFJakNOLElBQUFBLGVBQWUsRUFBRUE7QUFKZ0IsR0FBM0IiLCJzb3VyY2VSb290IjoiLiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBWaWV3IGZyb20gXCJzYXAvdWkvY29yZS9tdmMvVmlld1wiO1xuaW1wb3J0IENvbXBvbmVudCBmcm9tIFwic2FwL3VpL2NvcmUvQ29tcG9uZW50XCI7XG5pbXBvcnQgQXBwQ29tcG9uZW50IGZyb20gXCJzYXAvZmUvY29yZS9BcHBDb21wb25lbnRcIjtcblxuZXhwb3J0IHR5cGUgVXNlciA9IHtcblx0aWQ6IHN0cmluZztcblx0aW5pdGlhbHM/OiBzdHJpbmc7XG5cdG5hbWU6IHN0cmluZztcblx0cm9sZT86IFJvbGU7XG5cdGNvbG9yPzogbnVtYmVyO1xuXHR0cmFuc2llbnQ/OiBib29sZWFuO1xuXHRtZT86IGJvb2xlYW47XG59O1xuXG5leHBvcnQgdHlwZSBVc2VyQWN0aXZpdHkgPSBVc2VyICYge1xuXHRrZXk/OiBzdHJpbmc7XG59O1xuXG5leHBvcnQgZW51bSBSb2xlIHtcblx0T3duZXIgPSBcIk9cIixcblx0RWRpdCA9IFwiRVwiXG59XG5cbmV4cG9ydCBlbnVtIEFjdGl2aXR5IHtcblx0Sm9pbiA9IFwiSk9JTlwiLFxuXHRKb2luRWNobyA9IFwiSk9JTkVDSE9cIixcblx0TGVhdmUgPSBcIkxFQVZFXCIsXG5cdENoYW5nZSA9IFwiQ0hBTkdFXCIsXG5cdENyZWF0ZSA9IFwiQ1JFQVRFXCIsXG5cdERlbGV0ZSA9IFwiREVMRVRFXCIsXG5cdEFjdGlvbiA9IFwiQUNUSU9OXCIsXG5cdExpdmVDaGFuZ2UgPSBcIkxJVkVDSEFOR0VcIixcblx0QWN0aXZhdGUgPSBcIkFDVElWQVRFXCIsXG5cdERpc2NhcmQgPSBcIkRJU0NBUkRcIixcblx0VW5kbyA9IFwiVU5ET1wiXG59XG5cbmV4cG9ydCB0eXBlIE1lc3NhZ2UgPSB7XG5cdHVzZXJEZXNjcmlwdGlvbjogc3RyaW5nO1xuXHR1c2VySUQ6IHN0cmluZztcblx0dXNlckFjdGlvbjogc3RyaW5nO1xuXHRjbGllbnRBY3Rpb246IHN0cmluZztcblx0Y2xpZW50Q29udGVudDogc3RyaW5nO1xufTtcblxuZnVuY3Rpb24gZm9ybWF0SW5pdGlhbHMoZnVsbE5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG5cdC8vIHJlbW92ZSB0aXRsZXMgLSB0aG9zZSBhcmUgdGhlIG9uZXMgZnJvbSBTLzQgdG8gYmUgY2hlY2tlZCBpZiB0aGVyZSBhcmUgb3RoZXJzXG5cdGNvbnN0IGFjYWRlbWljVGl0bGVzID0gW1wiRHIuXCIsIFwiUHJvZi5cIiwgXCJQcm9mLiBEci5cIiwgXCJCLkEuXCIsIFwiTUJBXCIsIFwiUGguRC5cIl07XG5cdGFjYWRlbWljVGl0bGVzLmZvckVhY2goZnVuY3Rpb24oYWNhZGVtaWNUaXRsZSkge1xuXHRcdGZ1bGxOYW1lID0gZnVsbE5hbWUucmVwbGFjZShhY2FkZW1pY1RpdGxlLCBcIlwiKTtcblx0fSk7XG5cblx0bGV0IGluaXRpYWxzOiBzdHJpbmc7XG5cdGNvbnN0IHBhcnRzID0gZnVsbE5hbWUudHJpbVN0YXJ0KCkuc3BsaXQoXCIgXCIpO1xuXG5cdGlmIChwYXJ0cy5sZW5ndGggPiAxKSB7XG5cdFx0aW5pdGlhbHMgPSAocGFydHM/LnNoaWZ0KCk/LmNoYXJBdCgwKSB8fCBcIlwiKSArIHBhcnRzLnBvcCgpPy5jaGFyQXQoMCk7XG5cdH0gZWxzZSB7XG5cdFx0aW5pdGlhbHMgPSBmdWxsTmFtZS5zdWJzdHJpbmcoMCwgMik7XG5cdH1cblxuXHRyZXR1cm4gaW5pdGlhbHMudG9VcHBlckNhc2UoKTtcbn1cblxuZnVuY3Rpb24gZ2V0VXNlckNvbG9yKFVzZXJJRDogc3RyaW5nLCBhY3RpdmVVc2VyczogVXNlcltdLCBpbnZpdGVkVXNlcnM6IFVzZXJbXSkge1xuXHQvLyBzZWFyY2ggaWYgdXNlciBpcyBrbm93blxuXHRjb25zdCB1c2VyID0gYWN0aXZlVXNlcnMuZmluZCh1ID0+IHUuaWQgPT09IFVzZXJJRCk7XG5cdGlmICh1c2VyKSB7XG5cdFx0cmV0dXJuIHVzZXIuY29sb3I7XG5cdH0gZWxzZSB7XG5cdFx0Ly8gc2VhcmNoIGZvciBuZXh0IGZyZWUgY29sb3Jcblx0XHRmb3IgKGxldCBpID0gMTsgaSA8PSAxMDsgaSsrKSB7XG5cdFx0XHRpZiAoYWN0aXZlVXNlcnMuZmluZEluZGV4KHUgPT4gdS5jb2xvciA9PT0gaSkgPT09IC0xICYmIGludml0ZWRVc2Vycy5maW5kSW5kZXgodSA9PiB1LmNvbG9yID09PSBpKSA9PT0gLTEpIHtcblx0XHRcdFx0cmV0dXJuIGk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdC8vIHRoaXMgc2VlbXMgdG8gYmUgYSBwb3B1bGFyIG9iamVjdCA6KSBmb3Igbm93IGp1c3QgcmV0dXJuIDEwIGZvciBhbGwuXG5cdFx0Ly8gZm9yIGludml0ZWQgd2Ugc2hvdWxkIHN0YXJ0IGZyb20gMSBhZ2FpbiBzbyB0aGUgY29sb3JzIGFyZSBkaWZmZXJlbnRcblx0XHRyZXR1cm4gMTA7XG5cdH1cbn1cblxuLy8gY29waWVkIGZyb20gQ29tbW9uVXRpbHMuIER1ZSB0byBhIGN5Y2xlIGRlcGVuZGVuY3kgSSBjYW4ndCB1c2UgQ29tbW9uVXRpbHMgaGVyZS5cbi8vIFRoYXQncyB0byBiZSBmaXhlZC4gdGhlIGRpc2NhcmQgcG9wb3ZlciB0aGluZ3kgc2hvdWxkbid0IGJlIGluIHRoZSBjb21tb24gdXRpbHMgYXQgYWxsXG5mdW5jdGlvbiBnZXRBcHBDb21wb25lbnQob0NvbnRyb2w6IGFueSk6IEFwcENvbXBvbmVudCB7XG5cdGlmIChvQ29udHJvbC5pc0EoXCJzYXAuZmUuY29yZS5BcHBDb21wb25lbnRcIikpIHtcblx0XHRyZXR1cm4gb0NvbnRyb2w7XG5cdH1cblx0Y29uc3Qgb093bmVyID0gQ29tcG9uZW50LmdldE93bmVyQ29tcG9uZW50Rm9yKG9Db250cm9sKTtcblx0aWYgKCFvT3duZXIpIHtcblx0XHRyZXR1cm4gb0NvbnRyb2w7XG5cdH0gZWxzZSB7XG5cdFx0cmV0dXJuIGdldEFwcENvbXBvbmVudChvT3duZXIpO1xuXHR9XG59XG5cbmZ1bmN0aW9uIGdldE1lKHZpZXc6IFZpZXcpOiBVc2VyIHtcblx0Y29uc3Qgc2hlbGxTZXJ2aWNlSGVscGVyID0gZ2V0QXBwQ29tcG9uZW50KHZpZXcpLmdldFNoZWxsU2VydmljZXMoKTtcblx0aWYgKCFzaGVsbFNlcnZpY2VIZWxwZXIgfHwgIXNoZWxsU2VydmljZUhlbHBlci5oYXNVU2hlbGwoKSkge1xuXHRcdHRocm93IFwiTm8gU2hlbGwuLi4gTm8gVXNlclwiO1xuXHR9XG5cdGNvbnN0IG1lOiBVc2VyID0ge1xuXHRcdGluaXRpYWxzOiBzaGVsbFNlcnZpY2VIZWxwZXIuZ2V0VXNlcigpLmdldEluaXRpYWxzKCksXG5cdFx0aWQ6IHNoZWxsU2VydmljZUhlbHBlci5nZXRVc2VyKCkuZ2V0SWQoKSxcblx0XHRuYW1lOiBzaGVsbFNlcnZpY2VIZWxwZXIuZ2V0VXNlcigpLmdldEZ1bGxOYW1lKCksXG5cdFx0Y29sb3I6IDYsIC8vICBzYW1lIGNvbG9yIGFzIEZMUC4uLlxuXHRcdHJvbGU6IFJvbGUuT3duZXIsXG5cdFx0bWU6IHRydWVcblx0fTtcblx0bWUubmFtZSArPSBcIiAoWW91KVwiOyAvLyBUT0RPIHJlc291cmNlIGJ1bmRsZVxuXHRyZXR1cm4gbWU7XG59XG5cbmV4cG9ydCBjb25zdCBDb2xsYWJvcmF0aW9uVXRpbHMgPSB7XG5cdGZvcm1hdEluaXRpYWxzOiBmb3JtYXRJbml0aWFscyxcblx0Z2V0VXNlckNvbG9yOiBnZXRVc2VyQ29sb3IsXG5cdGdldE1lOiBnZXRNZSxcblx0Z2V0QXBwQ29tcG9uZW50OiBnZXRBcHBDb21wb25lbnRcbn07XG4iXX0=
@@ -0,0 +1,119 @@
1
+ import View from "sap/ui/core/mvc/View";
2
+ import Component from "sap/ui/core/Component";
3
+ import AppComponent from "sap/fe/core/AppComponent";
4
+
5
+ export type User = {
6
+ id: string;
7
+ initials?: string;
8
+ name: string;
9
+ role?: Role;
10
+ color?: number;
11
+ transient?: boolean;
12
+ me?: boolean;
13
+ };
14
+
15
+ export type UserActivity = User & {
16
+ key?: string;
17
+ };
18
+
19
+ export enum Role {
20
+ Owner = "O",
21
+ Edit = "E"
22
+ }
23
+
24
+ export enum Activity {
25
+ Join = "JOIN",
26
+ JoinEcho = "JOINECHO",
27
+ Leave = "LEAVE",
28
+ Change = "CHANGE",
29
+ Create = "CREATE",
30
+ Delete = "DELETE",
31
+ Action = "ACTION",
32
+ LiveChange = "LIVECHANGE",
33
+ Activate = "ACTIVATE",
34
+ Discard = "DISCARD",
35
+ Undo = "UNDO"
36
+ }
37
+
38
+ export type Message = {
39
+ userDescription: string;
40
+ userID: string;
41
+ userAction: string;
42
+ clientAction: string;
43
+ clientContent: string;
44
+ };
45
+
46
+ function formatInitials(fullName: string): string {
47
+ // remove titles - those are the ones from S/4 to be checked if there are others
48
+ const academicTitles = ["Dr.", "Prof.", "Prof. Dr.", "B.A.", "MBA", "Ph.D."];
49
+ academicTitles.forEach(function(academicTitle) {
50
+ fullName = fullName.replace(academicTitle, "");
51
+ });
52
+
53
+ let initials: string;
54
+ const parts = fullName.trimStart().split(" ");
55
+
56
+ if (parts.length > 1) {
57
+ initials = (parts?.shift()?.charAt(0) || "") + parts.pop()?.charAt(0);
58
+ } else {
59
+ initials = fullName.substring(0, 2);
60
+ }
61
+
62
+ return initials.toUpperCase();
63
+ }
64
+
65
+ function getUserColor(UserID: string, activeUsers: User[], invitedUsers: User[]) {
66
+ // search if user is known
67
+ const user = activeUsers.find(u => u.id === UserID);
68
+ if (user) {
69
+ return user.color;
70
+ } else {
71
+ // search for next free color
72
+ for (let i = 1; i <= 10; i++) {
73
+ if (activeUsers.findIndex(u => u.color === i) === -1 && invitedUsers.findIndex(u => u.color === i) === -1) {
74
+ return i;
75
+ }
76
+ }
77
+ // this seems to be a popular object :) for now just return 10 for all.
78
+ // for invited we should start from 1 again so the colors are different
79
+ return 10;
80
+ }
81
+ }
82
+
83
+ // copied from CommonUtils. Due to a cycle dependency I can't use CommonUtils here.
84
+ // That's to be fixed. the discard popover thingy shouldn't be in the common utils at all
85
+ function getAppComponent(oControl: any): AppComponent {
86
+ if (oControl.isA("sap.fe.core.AppComponent")) {
87
+ return oControl;
88
+ }
89
+ const oOwner = Component.getOwnerComponentFor(oControl);
90
+ if (!oOwner) {
91
+ return oControl;
92
+ } else {
93
+ return getAppComponent(oOwner);
94
+ }
95
+ }
96
+
97
+ function getMe(view: View): User {
98
+ const shellServiceHelper = getAppComponent(view).getShellServices();
99
+ if (!shellServiceHelper || !shellServiceHelper.hasUShell()) {
100
+ throw "No Shell... No User";
101
+ }
102
+ const me: User = {
103
+ initials: shellServiceHelper.getUser().getInitials(),
104
+ id: shellServiceHelper.getUser().getId(),
105
+ name: shellServiceHelper.getUser().getFullName(),
106
+ color: 6, // same color as FLP...
107
+ role: Role.Owner,
108
+ me: true
109
+ };
110
+ me.name += " (You)"; // TODO resource bundle
111
+ return me;
112
+ }
113
+
114
+ export const CollaborationUtils = {
115
+ formatInitials: formatInitials,
116
+ getUserColor: getUserColor,
117
+ getMe: getMe,
118
+ getAppComponent: getAppComponent
119
+ };