@sap-ux/preview-middleware 0.20.1 → 0.20.3

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 (32) hide show
  1. package/dist/client/adp/command-executor.js +2 -2
  2. package/dist/client/adp/command-executor.ts +4 -4
  3. package/dist/client/adp/controllers/AddFragment.controller.js +1 -1
  4. package/dist/client/adp/controllers/AddFragment.controller.ts +2 -2
  5. package/dist/client/adp/controllers/AddTableColumnFragments.controller.js +1 -1
  6. package/dist/client/adp/controllers/AddTableColumnFragments.controller.ts +2 -2
  7. package/dist/client/adp/controllers/BaseDialog.controller.js +2 -13
  8. package/dist/client/adp/controllers/BaseDialog.controller.ts +7 -25
  9. package/dist/client/adp/controllers/ControllerExtension.controller.js +73 -15
  10. package/dist/client/adp/controllers/ControllerExtension.controller.ts +110 -22
  11. package/dist/client/adp/dialog-factory.js +1 -1
  12. package/dist/client/adp/dialog-factory.ts +3 -1
  13. package/dist/client/adp/extend-controller.js +48 -0
  14. package/dist/client/adp/extend-controller.ts +49 -0
  15. package/dist/client/adp/init-dialogs.js +15 -35
  16. package/dist/client/adp/init-dialogs.ts +42 -20
  17. package/dist/client/adp/init.js +9 -1
  18. package/dist/client/adp/init.ts +6 -1
  19. package/dist/client/adp/quick-actions/common/add-controller-to-page.js +16 -2
  20. package/dist/client/adp/quick-actions/common/add-controller-to-page.ts +25 -3
  21. package/dist/client/adp/quick-actions/fe-v4/create-table-action.js +1 -1
  22. package/dist/client/adp/quick-actions/fe-v4/create-table-action.ts +1 -1
  23. package/dist/client/adp/ui/ControllerExtension.fragment.xml +14 -3
  24. package/dist/client/adp/utils.js +49 -8
  25. package/dist/client/adp/utils.ts +55 -7
  26. package/dist/client/cpe/connector-service.ts +1 -0
  27. package/dist/client/cpe/context-menu-service.js +6 -3
  28. package/dist/client/cpe/context-menu-service.ts +3 -1
  29. package/dist/client/cpe/quick-actions/quick-action-service.js +10 -3
  30. package/dist/client/cpe/quick-actions/quick-action-service.ts +7 -1
  31. package/dist/client/messagebundle.properties +5 -0
  32. package/package.json +6 -6
@@ -22,10 +22,10 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/rta/command/CommandFactory", "../ut
22
22
  * @param runtimeControl Managed object
23
23
  * @param commandName Command name
24
24
  * @param modifiedValue Modified value/s
25
- * @param designMetadata Design time metadata
26
25
  * @param flexSettings Additional flex settings
26
+ * @param designMetadata Design time metadata
27
27
  */
28
- async getCommand(runtimeControl, commandName, modifiedValue, designMetadata, flexSettings) {
28
+ async getCommand(runtimeControl, commandName, modifiedValue, flexSettings, designMetadata) {
29
29
  try {
30
30
  return await CommandFactory.getCommandFor(runtimeControl, commandName, modifiedValue, designMetadata, flexSettings);
31
31
  } catch (e) {
@@ -8,7 +8,7 @@ import type DesignTimeMetadata from 'sap/ui/dt/DesignTimeMetadata';
8
8
  import type FlexCommand from 'sap/ui/rta/command/FlexCommand';
9
9
  import { getError } from '../utils/error';
10
10
 
11
- type CommandNames = 'addXML';
11
+ type CommandNames = 'addXML' | 'codeExt';
12
12
 
13
13
  /**
14
14
  * Class responsible for handling rta calls
@@ -26,15 +26,15 @@ export default class CommandExecutor {
26
26
  * @param runtimeControl Managed object
27
27
  * @param commandName Command name
28
28
  * @param modifiedValue Modified value/s
29
- * @param designMetadata Design time metadata
30
29
  * @param flexSettings Additional flex settings
30
+ * @param designMetadata Design time metadata
31
31
  */
32
32
  public async getCommand<T>(
33
33
  runtimeControl: ManagedObject,
34
34
  commandName: CommandNames,
35
35
  modifiedValue: object,
36
- designMetadata: DesignTimeMetadata,
37
- flexSettings: FlexSettings
36
+ flexSettings: FlexSettings,
37
+ designMetadata?: DesignTimeMetadata
38
38
  ): Promise<FlexCommand<T>> {
39
39
  try {
40
40
  return await CommandFactory.getCommandFor(
@@ -143,7 +143,7 @@ sap.ui.define([
143
143
  const flexSettings = this.rta.getFlexSettings();
144
144
  const overlay = OverlayRegistry.getOverlay(this.runtimeControl);
145
145
  const designMetadata = overlay.getDesignTimeMetadata();
146
- const command = await this.commandExecutor.getCommand(this.runtimeControl, 'addXML', modifiedValue, designMetadata, flexSettings);
146
+ const command = await this.commandExecutor.getCommand(this.runtimeControl, 'addXML', modifiedValue, flexSettings, designMetadata);
147
147
  await this.commandExecutor.pushAndExecuteCommand(command);
148
148
  }
149
149
  });
@@ -243,8 +243,8 @@ export default class AddFragment extends BaseDialog<AddFragmentModel> {
243
243
  this.runtimeControl,
244
244
  'addXML',
245
245
  modifiedValue,
246
- designMetadata,
247
- flexSettings
246
+ flexSettings,
247
+ designMetadata
248
248
  );
249
249
 
250
250
  await this.commandExecutor.pushAndExecuteCommand(command);
@@ -160,7 +160,7 @@ sap.ui.define([
160
160
  targetAggregation: fragment.targetAggregation === ITEMS_AGGREGATION ? CELLS_AGGREGATION : fragment.targetAggregation
161
161
  };
162
162
  const targetObject = fragment.targetAggregation === COLUMNS_AGGREGATION ? this.runtimeControl : this.runtimeControl.getAggregation(ITEMS_AGGREGATION)[0];
163
- const command = await this.commandExecutor.getCommand(targetObject, 'addXML', modifiedValue, designMetadata, flexSettings);
163
+ const command = await this.commandExecutor.getCommand(targetObject, 'addXML', modifiedValue, flexSettings, designMetadata);
164
164
  const templateName = fragment.targetAggregation === COLUMNS_AGGREGATION ? `V2_SMART_TABLE_COLUMN` : 'V2_SMART_TABLE_CELL';
165
165
  const preparedChange = command.getPreparedChange();
166
166
  setAdditionalChangeInfoForChangeFile(preparedChange.getDefinition().fileName, { templateName });
@@ -283,8 +283,8 @@ export default class AddTableColumnFragments extends BaseDialog<AddTableColumnsF
283
283
  targetObject,
284
284
  'addXML',
285
285
  modifiedValue,
286
- designMetadata,
287
- flexSettings
286
+ flexSettings,
287
+ designMetadata
288
288
  );
289
289
 
290
290
  const templateName =
@@ -16,7 +16,7 @@ sap.ui.define([
16
16
  return obj && obj.__esModule && typeof obj.default !== 'undefined' ? obj.default : obj;
17
17
  }
18
18
  const ValueState = sap_ui_core_library['ValueState'];
19
- const matchesFragmentName = ___utils['matchesFragmentName'];
19
+ const checkForExistingChange = ___utils['checkForExistingChange'];
20
20
  const getError = ____utils_error['getError'];
21
21
  const getControlById = ____utils_core['getControlById'];
22
22
  const ControlUtils = _interopRequireDefault(__ControlUtils);
@@ -118,7 +118,7 @@ sap.ui.define([
118
118
  updateDialogState(ValueState.Error, 'A fragment file name cannot contain more than 64 characters.');
119
119
  return;
120
120
  }
121
- const changeExists = this.checkForExistingChange(fragmentName);
121
+ const changeExists = checkForExistingChange(this.rta, 'addXMLAtExtensionPoint', 'content.fragmentPath', `${ fragmentName }.fragment.xml`);
122
122
  if (changeExists) {
123
123
  updateDialogState(ValueState.Error, 'Enter a different name. The fragment name entered matches the name of an unsaved fragment.');
124
124
  return;
@@ -126,17 +126,6 @@ sap.ui.define([
126
126
  updateDialogState(ValueState.Success);
127
127
  this.model.setProperty('/newFragmentName', fragmentName);
128
128
  },
129
- checkForExistingChange: function _checkForExistingChange(fragmentName) {
130
- const allCommands = this.rta.getCommandStack().getCommands();
131
- return allCommands.some(command => {
132
- if (typeof command.getCommands === 'function') {
133
- const addXmlCommand = command.getCommands().find(c => c?.getProperty('name') === 'addXMLAtExtensionPoint');
134
- return addXmlCommand && matchesFragmentName(addXmlCommand, fragmentName);
135
- } else {
136
- return matchesFragmentName(command, fragmentName);
137
- }
138
- });
139
- },
140
129
  setEscapeHandler: function _setEscapeHandler() {
141
130
  this.dialog.setEscapeHandler(_ref => {
142
131
  let {resolve} = _ref;
@@ -7,10 +7,9 @@ import { ValueState } from 'sap/ui/core/library';
7
7
  import Controller from 'sap/ui/core/mvc/Controller';
8
8
  import JSONModel from 'sap/ui/model/json/JSONModel';
9
9
  import RuntimeAuthoring from 'sap/ui/rta/RuntimeAuthoring';
10
- import FlexCommand from 'sap/ui/rta/command/FlexCommand';
11
10
  import MessageToast from 'sap/m/MessageToast';
12
11
  import CommandExecutor from '../command-executor';
13
- import { matchesFragmentName } from '../utils';
12
+ import { checkForExistingChange } from '../utils';
14
13
  import type { Fragments } from '../api-handler';
15
14
  import { getError } from '../../utils/error';
16
15
  import ManagedObjectMetadata from 'sap/ui/base/ManagedObjectMetadata';
@@ -168,7 +167,12 @@ export default abstract class BaseDialog<T extends BaseDialogModel = BaseDialogM
168
167
  return;
169
168
  }
170
169
 
171
- const changeExists = this.checkForExistingChange(fragmentName);
170
+ const changeExists = checkForExistingChange(
171
+ this.rta,
172
+ 'addXMLAtExtensionPoint',
173
+ 'content.fragmentPath',
174
+ `${fragmentName}.fragment.xml`
175
+ );
172
176
 
173
177
  if (changeExists) {
174
178
  updateDialogState(
@@ -182,28 +186,6 @@ export default abstract class BaseDialog<T extends BaseDialogModel = BaseDialogM
182
186
  this.model.setProperty('/newFragmentName', fragmentName);
183
187
  }
184
188
 
185
- /**
186
- * Checks for the existence of a change associated with a specific fragment name in the RTA command stack.
187
- *
188
- * @param {string} fragmentName - The name of the fragment to check for existing changes.
189
- * @returns {Promise<boolean>} A promise that resolves to `true` if a matching change is found, otherwise `false`.
190
- */
191
- checkForExistingChange(fragmentName: string): boolean {
192
- const allCommands = this.rta.getCommandStack().getCommands();
193
-
194
- return allCommands.some((command: FlexCommand) => {
195
- if (typeof command.getCommands === 'function') {
196
- const addXmlCommand = command
197
- .getCommands()
198
- .find((c: FlexCommand) => c?.getProperty('name') === 'addXMLAtExtensionPoint');
199
-
200
- return addXmlCommand && matchesFragmentName(addXmlCommand, fragmentName);
201
- } else {
202
- return matchesFragmentName(command, fragmentName);
203
- }
204
- });
205
- }
206
-
207
189
  /**
208
190
  * Sets custom function that fires when user presses escape key.
209
191
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/JSONModel", "../api-handler", "./BaseDialog.controller", "../utils"], function (MessageToast, sap_ui_core_library, JSONModel, ___api_handler, __BaseDialog, ___utils) {
3
+ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/JSONModel", "../api-handler", "./BaseDialog.controller", "../../i18n", "../utils", "../../utils/version", "../command-executor", "../../utils/core"], function (MessageToast, sap_ui_core_library, JSONModel, ___api_handler, __BaseDialog, ____i18n, ___utils, ____utils_version, __CommandExecutor, ____utils_core) {
4
4
  "use strict";
5
5
 
6
6
  function _interopRequireDefault(obj) {
@@ -14,16 +14,25 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
14
14
  const writeChange = ___api_handler["writeChange"];
15
15
  const writeController = ___api_handler["writeController"];
16
16
  const BaseDialog = _interopRequireDefault(__BaseDialog);
17
+ const getResourceModel = ____i18n["getResourceModel"];
18
+ const getTextBundle = ____i18n["getTextBundle"];
19
+ const notifyUser = ___utils["notifyUser"];
20
+ const checkForExistingChange = ___utils["checkForExistingChange"];
17
21
  const getControllerInfo = ___utils["getControllerInfo"];
22
+ const getUi5Version = ____utils_version["getUi5Version"];
23
+ const isLowerThanMinimalUi5Version = ____utils_version["isLowerThanMinimalUi5Version"];
24
+ const CommandExecutor = _interopRequireDefault(__CommandExecutor);
25
+ const getControlById = ____utils_core["getControlById"];
18
26
  /**
19
27
  * @namespace open.ux.preview.client.adp.controllers
20
28
  */
21
29
  const ControllerExtension = BaseDialog.extend("open.ux.preview.client.adp.controllers.ControllerExtension", {
22
- constructor: function _constructor(name, overlays, rta, telemetryData) {
30
+ constructor: function _constructor(name, overlays, rta, data, telemetryData) {
23
31
  BaseDialog.prototype.constructor.call(this, name, telemetryData);
24
32
  this.rta = rta;
25
33
  this.overlays = overlays;
26
34
  this.model = new JSONModel();
35
+ this.data = data;
27
36
  },
28
37
  /**
29
38
  * Setups the Dialog and the JSON Model
@@ -33,7 +42,10 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
33
42
  setup: async function _setup(dialog) {
34
43
  this.dialog = dialog;
35
44
  this.setEscapeHandler();
45
+ const resourceModel = await getResourceModel('open.ux.preview.client');
46
+ this.bundle = await getTextBundle();
36
47
  await this.buildDialogData();
48
+ this.dialog.setModel(resourceModel, 'i18n');
37
49
  this.dialog.setModel(this.model);
38
50
  this.dialog.open();
39
51
  },
@@ -58,10 +70,15 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
58
70
  return;
59
71
  }
60
72
  const fileExists = controllerList.some(f => f.controllerName === controllerName);
73
+ const pendingChangeExists = checkForExistingChange(this.rta, 'codeExt', 'content.codeRef', `${controllerName}.js`);
61
74
  if (fileExists) {
62
75
  updateDialogState(ValueState.Error, 'Enter a different name. The controller name that you entered already exists in your project.');
63
76
  return;
64
77
  }
78
+ if (pendingChangeExists) {
79
+ updateDialogState(ValueState.Error, 'Enter a different name. The controller name that you entered already exists as a pending change.');
80
+ return;
81
+ }
65
82
  const isValidName = /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(controllerName);
66
83
  if (!isValidName) {
67
84
  updateDialogState(ValueState.Error, 'The controller name cannot contain white spaces or special characters.');
@@ -87,7 +104,15 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
87
104
  source.setEnabled(false);
88
105
  const controllerName = this.model.getProperty('/newControllerName');
89
106
  const viewId = this.model.getProperty('/viewId');
90
- await this.createNewController(controllerName, viewId);
107
+ const controllerRef = {
108
+ codeRef: `coding/${controllerName}.js`,
109
+ viewId
110
+ };
111
+ if (this.data) {
112
+ this.data.deferred.resolve(controllerRef);
113
+ } else {
114
+ await this.createNewController(controllerName, controllerRef);
115
+ }
91
116
  } else {
92
117
  const controllerPath = this.model.getProperty('/controllerPath');
93
118
  window.open(`vscode://file${controllerPath}`);
@@ -105,8 +130,11 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
105
130
  viewId
106
131
  } = getControllerInfo(overlayControl);
107
132
  const data = await this.getExistingController(controllerName);
133
+ const hasPendingChangeForView = checkForExistingChange(this.rta, 'codeExt', 'selector.controllerName', controllerName);
108
134
  if (data) {
109
- if (data?.controllerExists) {
135
+ if (hasPendingChangeForView) {
136
+ this.updateModelForExistingPendingChange();
137
+ } else if (data?.controllerExists) {
110
138
  this.updateModelForExistingController(data);
111
139
  } else {
112
140
  this.updateModelForNewController(viewId, data.isTsSupported);
@@ -129,11 +157,9 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
129
157
  this.model.setProperty('/controllerExists', controllerExists);
130
158
  this.model.setProperty('/controllerPath', controllerPath);
131
159
  this.model.setProperty('/controllerPathFromRoot', controllerPathFromRoot);
132
- const content = this.dialog.getContent();
133
- const form = content[0];
134
- form.setVisible(false);
135
- const messageForm = content[1];
136
- messageForm.setVisible(true);
160
+ this.model.setProperty('/inputFormVisibility', false);
161
+ this.model.setProperty('/pendingChangeFormVisibility', false);
162
+ this.model.setProperty('/existingControllerFormVisibility', true);
137
163
  if (isRunningInBAS) {
138
164
  this.dialog.getBeginButton().setVisible(false);
139
165
  } else {
@@ -141,6 +167,16 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
141
167
  }
142
168
  this.dialog.getEndButton().setText('Close');
143
169
  },
170
+ /**
171
+ * Updates the model properties for an existing controller in a pending change.
172
+ */
173
+ updateModelForExistingPendingChange: function _updateModelForExistingPendingChange() {
174
+ this.model.setProperty('/inputFormVisibility', false);
175
+ this.model.setProperty('/existingControllerFormVisibility', false);
176
+ this.model.setProperty('/pendingChangeFormVisibility', true);
177
+ this.dialog.getBeginButton().setVisible(false);
178
+ this.dialog.getEndButton().setText('Close');
179
+ },
144
180
  /**
145
181
  * Updates the model property for a new controller.
146
182
  *
@@ -150,6 +186,9 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
150
186
  updateModelForNewController: function _updateModelForNewController(viewId, isTsSupported) {
151
187
  this.model.setProperty('/viewId', viewId);
152
188
  this.model.setProperty('/controllerExtension', isTsSupported ? '.ts' : '.js');
189
+ this.model.setProperty('/existingControllerFormVisibility', false);
190
+ this.model.setProperty('/pendingChangeFormVisibility', false);
191
+ this.model.setProperty('/inputFormVisibility', true);
153
192
  },
154
193
  /**
155
194
  * Retrieves existing controller data if found in the project's workspace.
@@ -183,17 +222,21 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
183
222
  * Creates a new fragment for the specified control
184
223
  *
185
224
  * @param controllerName Controller Name
186
- * @param viewId View Id
225
+ * @param controllerRef Controller reference
187
226
  */
188
- createNewController: async function _createNewController(controllerName, viewId) {
227
+ createNewController: async function _createNewController(controllerName, controllerRef) {
228
+ const ui5Version = await getUi5Version();
229
+ if (!isLowerThanMinimalUi5Version(ui5Version, {
230
+ major: 1,
231
+ minor: 135
232
+ })) {
233
+ await this.createControllerCommand(controllerName, controllerRef);
234
+ return;
235
+ }
189
236
  try {
190
237
  await writeController({
191
238
  controllerName
192
239
  });
193
- const controllerRef = {
194
- codeRef: `coding/${controllerName}.js`,
195
- viewId
196
- };
197
240
  const service = await this.rta.getService('controllerExtension');
198
241
  const change = await service.add(controllerRef.codeRef, controllerRef.viewId);
199
242
  change.creation = new Date().toISOString();
@@ -205,6 +248,21 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/library", "sap/ui/model/json/J
205
248
  await this.getControllers();
206
249
  this.handleError(e);
207
250
  }
251
+ },
252
+ /**
253
+ * Creates a controller command and executes it.
254
+ *
255
+ * @param controllerName Controller name
256
+ * @param controllerRef Controller reference
257
+ */
258
+ createControllerCommand: async function _createControllerCommand(controllerName, controllerRef) {
259
+ const flexSettings = this.rta.getFlexSettings();
260
+ const commandExecutor = new CommandExecutor(this.rta);
261
+ const view = getControlById(controllerRef.viewId);
262
+ const command = await commandExecutor.getCommand(view, 'codeExt', controllerRef, flexSettings);
263
+ await commandExecutor.pushAndExecuteCommand(command);
264
+ const bundle = await getTextBundle();
265
+ notifyUser(bundle.getText('ADP_CREATE_CONTROLLER_EXTENSION', [controllerName]), 8000);
208
266
  }
209
267
  });
210
268
  return ControllerExtension;
@@ -17,17 +17,19 @@ import JSONModel from 'sap/ui/model/json/JSONModel';
17
17
  /** sap.ui.rta */
18
18
  import type RuntimeAuthoring from 'sap/ui/rta/RuntimeAuthoring';
19
19
 
20
- /** sap.ui.layout */
21
- import type SimpleForm from 'sap/ui/layout/form/SimpleForm';
22
-
23
20
  /** sap.ui.dt */
24
21
  import type ElementOverlay from 'sap/ui/dt/ElementOverlay';
25
22
 
26
23
  import type { CodeExtResponse, ControllersResponse } from '../api-handler';
27
24
  import { getExistingController, readControllers, writeChange, writeController } from '../api-handler';
28
25
  import BaseDialog from './BaseDialog.controller';
29
- import { getControllerInfo } from '../utils';
26
+ import type { ExtendControllerData, DeferredExtendControllerData } from '../extend-controller';
30
27
  import { QuickActionTelemetryData } from '../../cpe/quick-actions/quick-action-definition';
28
+ import { getResourceModel, getTextBundle, TextBundle } from '../../i18n';
29
+ import { notifyUser, checkForExistingChange, getControllerInfo } from '../utils';
30
+ import { getUi5Version, isLowerThanMinimalUi5Version } from '../../utils/version';
31
+ import CommandExecutor from '../command-executor';
32
+ import { getControlById } from '../../utils/core';
31
33
 
32
34
  interface ControllerExtensionService {
33
35
  add: (codeRef: string, viewId: string) => Promise<{ creation: string }>;
@@ -53,11 +55,21 @@ type ControllerModel = JSONModel & {
53
55
  * @namespace open.ux.preview.client.adp.controllers
54
56
  */
55
57
  export default class ControllerExtension extends BaseDialog<ControllerModel> {
56
- constructor(name: string, overlays: UI5Element, rta: RuntimeAuthoring, telemetryData?: QuickActionTelemetryData) {
58
+ public readonly data?: ExtendControllerData;
59
+ private bundle: TextBundle;
60
+
61
+ constructor(
62
+ name: string,
63
+ overlays: UI5Element,
64
+ rta: RuntimeAuthoring,
65
+ data?: ExtendControllerData,
66
+ telemetryData?: QuickActionTelemetryData
67
+ ) {
57
68
  super(name, telemetryData);
58
69
  this.rta = rta;
59
70
  this.overlays = overlays;
60
71
  this.model = new JSONModel();
72
+ this.data = data;
61
73
  }
62
74
 
63
75
  /**
@@ -70,8 +82,12 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
70
82
 
71
83
  this.setEscapeHandler();
72
84
 
85
+ const resourceModel = await getResourceModel('open.ux.preview.client');
86
+ this.bundle = await getTextBundle();
87
+
73
88
  await this.buildDialogData();
74
89
 
90
+ this.dialog.setModel(resourceModel, 'i18n');
75
91
  this.dialog.setModel(this.model);
76
92
 
77
93
  this.dialog.open();
@@ -102,6 +118,13 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
102
118
 
103
119
  const fileExists = controllerList.some((f) => f.controllerName === controllerName);
104
120
 
121
+ const pendingChangeExists = checkForExistingChange(
122
+ this.rta,
123
+ 'codeExt',
124
+ 'content.codeRef',
125
+ `${controllerName}.js`
126
+ );
127
+
105
128
  if (fileExists) {
106
129
  updateDialogState(
107
130
  ValueState.Error,
@@ -110,6 +133,14 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
110
133
  return;
111
134
  }
112
135
 
136
+ if (pendingChangeExists) {
137
+ updateDialogState(
138
+ ValueState.Error,
139
+ 'Enter a different name. The controller name that you entered already exists as a pending change.'
140
+ );
141
+ return;
142
+ }
143
+
113
144
  const isValidName = /^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(controllerName);
114
145
 
115
146
  if (!isValidName) {
@@ -147,7 +178,16 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
147
178
  const controllerName = this.model.getProperty('/newControllerName');
148
179
  const viewId = this.model.getProperty('/viewId');
149
180
 
150
- await this.createNewController(controllerName, viewId);
181
+ const controllerRef = {
182
+ codeRef: `coding/${controllerName}.js`,
183
+ viewId
184
+ };
185
+
186
+ if (this.data) {
187
+ this.data.deferred.resolve(controllerRef);
188
+ } else {
189
+ await this.createNewController(controllerName, controllerRef);
190
+ }
151
191
  } else {
152
192
  const controllerPath = this.model.getProperty('/controllerPath');
153
193
  window.open(`vscode://file${controllerPath}`);
@@ -166,8 +206,17 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
166
206
  const { controllerName, viewId } = getControllerInfo(overlayControl);
167
207
  const data = await this.getExistingController(controllerName);
168
208
 
209
+ const hasPendingChangeForView = checkForExistingChange(
210
+ this.rta,
211
+ 'codeExt',
212
+ 'selector.controllerName',
213
+ controllerName
214
+ );
215
+
169
216
  if (data) {
170
- if (data?.controllerExists) {
217
+ if (hasPendingChangeForView) {
218
+ this.updateModelForExistingPendingChange();
219
+ } else if (data?.controllerExists) {
171
220
  this.updateModelForExistingController(data);
172
221
  } else {
173
222
  this.updateModelForNewController(viewId, data.isTsSupported);
@@ -187,14 +236,9 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
187
236
  this.model.setProperty('/controllerExists', controllerExists);
188
237
  this.model.setProperty('/controllerPath', controllerPath);
189
238
  this.model.setProperty('/controllerPathFromRoot', controllerPathFromRoot);
190
-
191
- const content = this.dialog.getContent();
192
-
193
- const form = content[0] as SimpleForm;
194
- form.setVisible(false);
195
-
196
- const messageForm = content[1] as SimpleForm;
197
- messageForm.setVisible(true);
239
+ this.model.setProperty('/inputFormVisibility', false);
240
+ this.model.setProperty('/pendingChangeFormVisibility', false);
241
+ this.model.setProperty('/existingControllerFormVisibility', true);
198
242
 
199
243
  if (isRunningInBAS) {
200
244
  this.dialog.getBeginButton().setVisible(false);
@@ -204,6 +248,18 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
204
248
  this.dialog.getEndButton().setText('Close');
205
249
  }
206
250
 
251
+ /**
252
+ * Updates the model properties for an existing controller in a pending change.
253
+ */
254
+ private updateModelForExistingPendingChange(): void {
255
+ this.model.setProperty('/inputFormVisibility', false);
256
+ this.model.setProperty('/existingControllerFormVisibility', false);
257
+ this.model.setProperty('/pendingChangeFormVisibility', true);
258
+
259
+ this.dialog.getBeginButton().setVisible(false);
260
+ this.dialog.getEndButton().setText('Close');
261
+ }
262
+
207
263
  /**
208
264
  * Updates the model property for a new controller.
209
265
  *
@@ -213,6 +269,9 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
213
269
  private updateModelForNewController(viewId: string, isTsSupported: boolean): void {
214
270
  this.model.setProperty('/viewId', viewId);
215
271
  this.model.setProperty('/controllerExtension', isTsSupported ? '.ts' : '.js');
272
+ this.model.setProperty('/existingControllerFormVisibility', false);
273
+ this.model.setProperty('/pendingChangeFormVisibility', false);
274
+ this.model.setProperty('/inputFormVisibility', true);
216
275
  }
217
276
 
218
277
  /**
@@ -248,17 +307,20 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
248
307
  * Creates a new fragment for the specified control
249
308
  *
250
309
  * @param controllerName Controller Name
251
- * @param viewId View Id
310
+ * @param controllerRef Controller reference
252
311
  */
253
- private async createNewController(controllerName: string, viewId: string): Promise<void> {
312
+ private async createNewController(
313
+ controllerName: string,
314
+ controllerRef: DeferredExtendControllerData
315
+ ): Promise<void> {
316
+ const ui5Version = await getUi5Version();
317
+ if (!isLowerThanMinimalUi5Version(ui5Version, { major: 1, minor: 135 })) {
318
+ await this.createControllerCommand(controllerName, controllerRef);
319
+ return;
320
+ }
254
321
  try {
255
322
  await writeController({ controllerName });
256
323
 
257
- const controllerRef = {
258
- codeRef: `coding/${controllerName}.js`,
259
- viewId
260
- };
261
-
262
324
  const service = await this.rta.getService<ControllerExtensionService>('controllerExtension');
263
325
 
264
326
  const change = await service.add(controllerRef.codeRef, controllerRef.viewId);
@@ -273,4 +335,30 @@ export default class ControllerExtension extends BaseDialog<ControllerModel> {
273
335
  this.handleError(e);
274
336
  }
275
337
  }
338
+
339
+ /**
340
+ * Creates a controller command and executes it.
341
+ *
342
+ * @param controllerName Controller name
343
+ * @param controllerRef Controller reference
344
+ */
345
+ private async createControllerCommand(
346
+ controllerName: string,
347
+ controllerRef: DeferredExtendControllerData
348
+ ): Promise<void> {
349
+ const flexSettings = this.rta.getFlexSettings();
350
+ const commandExecutor = new CommandExecutor(this.rta);
351
+ const view = getControlById(controllerRef.viewId) as UI5Element;
352
+ const command = await commandExecutor.getCommand<DeferredExtendControllerData>(
353
+ view,
354
+ 'codeExt',
355
+ controllerRef,
356
+ flexSettings
357
+ );
358
+
359
+ await commandExecutor.pushAndExecuteCommand(command);
360
+
361
+ const bundle = await getTextBundle();
362
+ notifyUser(bundle.getText('ADP_CREATE_CONTROLLER_EXTENSION', [controllerName]), 8000);
363
+ }
276
364
  }
@@ -73,7 +73,7 @@ sap.ui.define(["sap/ui/core/Fragment", "../i18n", "./controllers/AddFragment.con
73
73
  }, telemetryData);
74
74
  break;
75
75
  case DialogNames.CONTROLLER_EXTENSION:
76
- controller = new ControllerExtension(`open.ux.preview.client.adp.controllers.${dialogName}`, overlay, rta, telemetryData);
76
+ controller = new ControllerExtension(`open.ux.preview.client.adp.controllers.${dialogName}`, overlay, rta, data, telemetryData);
77
77
  break;
78
78
  case DialogNames.ADD_FRAGMENT_AT_EXTENSION_POINT:
79
79
  controller = new ExtensionPoint(`open.ux.preview.client.adp.controllers.${dialogName}`, overlay, rta, data);
@@ -12,6 +12,7 @@ import ExtensionPoint from './controllers/ExtensionPoint.controller';
12
12
 
13
13
  import type { ExtensionPointData } from './extension-point';
14
14
  import { AddFragmentData } from './add-fragment';
15
+ import { ExtendControllerData } from './extend-controller';
15
16
  import FileExistsDialog, { FileExistsDialogOptions } from './controllers/FileExistsDialog.controller';
16
17
  import AddSubpage, { AddSubpageOptions } from './controllers/AddSubpage.controller';
17
18
  import { QuickActionTelemetryData } from '../cpe/quick-actions/quick-action-definition';
@@ -33,7 +34,7 @@ type Controller =
33
34
  | FileExistsDialog
34
35
  | AddSubpage;
35
36
 
36
- type DialogData = ExtensionPointData | AddFragmentData;
37
+ type DialogData = ExtensionPointData | AddFragmentData | ExtendControllerData;
37
38
 
38
39
  export const OPEN_DIALOG_STATUS_CHANGED = 'OPEN_DIALOG_STATUS_CHANGED';
39
40
 
@@ -106,6 +107,7 @@ export class DialogFactory {
106
107
  `open.ux.preview.client.adp.controllers.${dialogName}`,
107
108
  overlay,
108
109
  rta,
110
+ (data as ExtendControllerData),
109
111
  telemetryData
110
112
  );
111
113
  break;