@sap/ux-ui5-tooling 1.24.0 → 1.25.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.
- package/CHANGELOG.md +7 -0
- package/dist/adp-tooling/templates/cf/package.json +1 -1
- package/dist/cli/index.cjs +80636 -72229
- package/dist/control-property-editor/app.css +1 -1
- package/dist/control-property-editor/app.js +43 -43
- package/dist/middlewares/fiori-tools-appreload.js +9127 -8610
- package/dist/middlewares/fiori-tools-preview.js +66066 -56705
- package/dist/middlewares/fiori-tools-proxy.js +52290 -43887
- package/dist/preview-middleware/dist/client/adp/change-file-validator.js +128 -0
- package/dist/preview-middleware/dist/client/adp/change-file-validator.ts +158 -0
- package/dist/preview-middleware/dist/client/adp/init.js +5 -1
- package/dist/preview-middleware/dist/client/adp/init.ts +5 -0
- package/dist/preview-middleware/dist/client/cpe/changes/service.js +85 -57
- package/dist/preview-middleware/dist/client/cpe/changes/service.ts +91 -52
- package/dist/preview-middleware/dist/client/cpe/control-data.js +183 -158
- package/dist/preview-middleware/dist/client/cpe/control-data.ts +221 -163
- package/dist/preview-middleware/dist/client/flp/init.js +2 -2
- package/dist/preview-middleware/dist/client/flp/init.ts +2 -2
- package/dist/preview-middleware/dist/client/messagebundle.properties +2 -0
- package/dist/tasks/cf-deploy/index.js +85092 -76768
- package/dist/tasks/deploy/index.js +57335 -49007
- package/package.json +24 -24
|
@@ -200,14 +200,217 @@ interface NewControlData {
|
|
|
200
200
|
newValue: unknown;
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
type PropertyDocumentation = {
|
|
204
|
+
defaultValue: string;
|
|
205
|
+
description: string;
|
|
206
|
+
propertyName: string;
|
|
207
|
+
type?: string;
|
|
208
|
+
propertyType?: string;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
interface ProcessedProperty {
|
|
212
|
+
analyzedType: AnalyzedType;
|
|
213
|
+
isEnabled: boolean;
|
|
214
|
+
value: unknown;
|
|
215
|
+
propertyType: PropertyType;
|
|
216
|
+
docu?: PropertyDocumentation;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Build the documentation object for a configuration (manifest) property.
|
|
221
|
+
*
|
|
222
|
+
* @param property - manifest property
|
|
223
|
+
* @returns PropertyDocumentation
|
|
224
|
+
*/
|
|
225
|
+
function buildConfigPropertyDocumentation(property: MergedSetting): PropertyDocumentation {
|
|
226
|
+
const defValue = ['undefined', 'null'].includes(String(property.defaultValue))
|
|
227
|
+
? '-'
|
|
228
|
+
: String(property.defaultValue);
|
|
229
|
+
return {
|
|
230
|
+
description: property.description,
|
|
231
|
+
propertyName: property.id,
|
|
232
|
+
type: property.type,
|
|
233
|
+
defaultValue: defValue || '-'
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Process a manifest configuration property.
|
|
239
|
+
*
|
|
240
|
+
* @param property - manifest property
|
|
241
|
+
* @param control - ui5 control
|
|
242
|
+
* @returns ProcessedProperty or undefined if the property should be skipped
|
|
243
|
+
*/
|
|
244
|
+
function processConfigProperty(property: MergedSetting, control: ManagedObject): ProcessedProperty | undefined {
|
|
245
|
+
const analyzedType = analyzeManifestProperty(property);
|
|
246
|
+
if (
|
|
247
|
+
!analyzedType ||
|
|
248
|
+
(property?.restrictedTo?.length &&
|
|
249
|
+
!property?.restrictedTo?.includes(getV4PageType(control) as TemplateType))
|
|
250
|
+
) {
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
analyzedType,
|
|
255
|
+
isEnabled: true,
|
|
256
|
+
value: property.value,
|
|
257
|
+
propertyType: PropertyType.Configuration,
|
|
258
|
+
docu: buildConfigPropertyDocumentation(property)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Process a UI5 control property.
|
|
264
|
+
*
|
|
265
|
+
* @param property - control metadata property
|
|
266
|
+
* @param control - ui5 control
|
|
267
|
+
* @param hasStableId - whether the control has a stable ID
|
|
268
|
+
* @param controlProperties - overlay design-time property config
|
|
269
|
+
* @param controlOverlay - element overlay
|
|
270
|
+
* @returns ProcessedProperty or undefined if the property should be skipped
|
|
271
|
+
*/
|
|
272
|
+
function processControlProperty(
|
|
273
|
+
property: ManagedObjectMetadataProperties,
|
|
274
|
+
control: ManagedObject,
|
|
275
|
+
hasStableId: boolean,
|
|
276
|
+
controlProperties: Record<string, { ignore: boolean }> | undefined,
|
|
277
|
+
controlOverlay: ElementOverlay | undefined
|
|
278
|
+
): ProcessedProperty | undefined {
|
|
279
|
+
const analyzedType = analyzePropertyType(property);
|
|
280
|
+
if (!analyzedType) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
const ignore = controlProperties?.[property.name]?.ignore ?? false;
|
|
284
|
+
|
|
285
|
+
//updating i18n text for the control if bindingInfo has bindingString
|
|
286
|
+
const controlNewData: NewControlData = {
|
|
287
|
+
id: control.getId(),
|
|
288
|
+
name: property.name,
|
|
289
|
+
newValue: control.getProperty(property.name)
|
|
290
|
+
};
|
|
291
|
+
const bindingInfo: { bindingString?: string } = control.getBindingInfo(controlNewData.name) as {
|
|
292
|
+
bindingString?: string;
|
|
293
|
+
};
|
|
294
|
+
if (bindingInfo?.bindingString !== undefined) {
|
|
295
|
+
controlNewData.newValue = bindingInfo.bindingString;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// A property is enabled if:
|
|
299
|
+
// 1. The property supports changes
|
|
300
|
+
// 2. The control has stable ID
|
|
301
|
+
// 3. It is not configured to be ignored in design time
|
|
302
|
+
// 4. And control overlay is selectable
|
|
303
|
+
return {
|
|
304
|
+
analyzedType,
|
|
305
|
+
isEnabled: isControlEnabled(analyzedType, hasStableId, ignore, controlOverlay),
|
|
306
|
+
value: normalizeObjectPropertyValue(controlNewData.newValue),
|
|
307
|
+
propertyType: PropertyType.ControlProperty
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Build a ControlProperty entry from an analyzed property.
|
|
313
|
+
*
|
|
314
|
+
* @param property - the raw property (manifest or control metadata)
|
|
315
|
+
* @param processed - the processed property data
|
|
316
|
+
* @param selectedControlName - name of the UI5 control class
|
|
317
|
+
* @returns ControlProperty or undefined if the type is not supported
|
|
318
|
+
*/
|
|
319
|
+
function buildPropertyEntry(
|
|
320
|
+
property: ManagedObjectMetadataProperties | MergedSetting,
|
|
321
|
+
processed: ProcessedProperty,
|
|
322
|
+
selectedControlName: string
|
|
323
|
+
): ControlProperty | undefined {
|
|
324
|
+
const { analyzedType, isEnabled, value, propertyType, docu } = processed;
|
|
325
|
+
const isIcon =
|
|
326
|
+
testIconPattern(property.name) &&
|
|
327
|
+
selectedControlName !== 'sap.m.Image' &&
|
|
328
|
+
analyzedType.ui5Type === 'sap.ui.core.URI';
|
|
329
|
+
const ui5Type = analyzedType.ui5Type || undefined;
|
|
330
|
+
const readableName = convertCamelCaseToPascalCase(property.name);
|
|
331
|
+
const docuSpread = docu ? { documentation: docu } : {};
|
|
332
|
+
|
|
333
|
+
switch (analyzedType.primitiveType) {
|
|
334
|
+
case 'enum': {
|
|
335
|
+
const values = analyzedType.enumValues ?? {};
|
|
336
|
+
const options: { key: string; text: string }[] = Object.keys(values).map((key) => ({
|
|
337
|
+
key,
|
|
338
|
+
text: values[key]
|
|
339
|
+
}));
|
|
340
|
+
return {
|
|
341
|
+
type: STRING_VALUE_TYPE,
|
|
342
|
+
editor: DROPDOWN_EDITOR_TYPE,
|
|
343
|
+
propertyType,
|
|
344
|
+
name: property.name,
|
|
345
|
+
readableName,
|
|
346
|
+
value: value as string,
|
|
347
|
+
isEnabled,
|
|
348
|
+
ui5Type,
|
|
349
|
+
options,
|
|
350
|
+
...docuSpread
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
case 'string':
|
|
354
|
+
return {
|
|
355
|
+
type: STRING_VALUE_TYPE,
|
|
356
|
+
editor: INPUT_EDITOR_TYPE,
|
|
357
|
+
propertyType,
|
|
358
|
+
name: property.name,
|
|
359
|
+
readableName,
|
|
360
|
+
value: value as string,
|
|
361
|
+
isEnabled,
|
|
362
|
+
isIcon,
|
|
363
|
+
ui5Type,
|
|
364
|
+
...docuSpread
|
|
365
|
+
};
|
|
366
|
+
case 'int':
|
|
367
|
+
return {
|
|
368
|
+
type: INTEGER_VALUE_TYPE,
|
|
369
|
+
editor: INPUT_EDITOR_TYPE,
|
|
370
|
+
propertyType,
|
|
371
|
+
name: property.name,
|
|
372
|
+
readableName,
|
|
373
|
+
value: value as number,
|
|
374
|
+
isEnabled,
|
|
375
|
+
ui5Type,
|
|
376
|
+
...docuSpread
|
|
377
|
+
};
|
|
378
|
+
case 'float':
|
|
379
|
+
return {
|
|
380
|
+
type: FLOAT_VALUE_TYPE,
|
|
381
|
+
editor: INPUT_EDITOR_TYPE,
|
|
382
|
+
propertyType,
|
|
383
|
+
name: property.name,
|
|
384
|
+
readableName,
|
|
385
|
+
value: value as number,
|
|
386
|
+
isEnabled,
|
|
387
|
+
ui5Type,
|
|
388
|
+
...docuSpread
|
|
389
|
+
};
|
|
390
|
+
case 'boolean':
|
|
391
|
+
return {
|
|
392
|
+
type: BOOLEAN_VALUE_TYPE,
|
|
393
|
+
editor: CHECKBOX_EDITOR_TYPE,
|
|
394
|
+
propertyType,
|
|
395
|
+
name: property.name,
|
|
396
|
+
readableName,
|
|
397
|
+
value: value as boolean,
|
|
398
|
+
isEnabled,
|
|
399
|
+
ui5Type,
|
|
400
|
+
...docuSpread
|
|
401
|
+
};
|
|
402
|
+
default:
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
203
407
|
/**
|
|
204
408
|
* Build control data.
|
|
205
409
|
*
|
|
206
410
|
* @param control - ui5 control
|
|
207
411
|
* @param changeService - Changeservice for change stack event handling.
|
|
208
412
|
* @param controlOverlay - element overlay
|
|
209
|
-
* @
|
|
210
|
-
* @returns Promise<Control>
|
|
413
|
+
* @returns Control
|
|
211
414
|
*/
|
|
212
415
|
export function buildControlData(
|
|
213
416
|
control: ManagedObject,
|
|
@@ -218,182 +421,37 @@ export function buildControlData(
|
|
|
218
421
|
const selectedControlName = controlMetadata.getName();
|
|
219
422
|
const hasStableId = Utils.checkControlId(control);
|
|
220
423
|
const overlayData = controlOverlay?.getDesignTimeMetadata().getData();
|
|
221
|
-
|
|
222
424
|
const controlProperties = controlOverlay ? overlayData?.properties : undefined;
|
|
425
|
+
|
|
223
426
|
const manifestProperties = getManifestProperties(control, changeService, controlOverlay);
|
|
224
|
-
// Add the control's properties/manifest properties
|
|
225
427
|
const allProperties = {
|
|
226
428
|
...(controlMetadata.getAllProperties() as unknown as {
|
|
227
429
|
[name: string]: ManagedObjectMetadataProperties;
|
|
228
430
|
}),
|
|
229
431
|
...manifestProperties
|
|
230
432
|
};
|
|
231
|
-
|
|
232
|
-
const propertyNames = Object.keys(allProperties);
|
|
433
|
+
|
|
233
434
|
const properties: ControlProperty[] = [];
|
|
234
|
-
for (const propertyName of
|
|
435
|
+
for (const propertyName of Object.keys(allProperties)) {
|
|
235
436
|
const property = allProperties[propertyName];
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
propertyType = PropertyType.Configuration;
|
|
241
|
-
analyzedType = analyzeManifestProperty(property);
|
|
242
|
-
if (
|
|
243
|
-
!analyzedType ||
|
|
244
|
-
(property?.restrictedTo?.length &&
|
|
245
|
-
!property?.restrictedTo?.includes(getV4PageType(control) as TemplateType))
|
|
246
|
-
) {
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
isEnabled = true;
|
|
250
|
-
value = property.value;
|
|
251
|
-
} else {
|
|
252
|
-
propertyType = PropertyType.ControlProperty;
|
|
253
|
-
// the default behavior is that the property is enabled
|
|
254
|
-
// meaning it's not ignored during design time
|
|
255
|
-
analyzedType = analyzePropertyType(property);
|
|
256
|
-
if (!analyzedType) {
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
let ignore = false;
|
|
260
|
-
if (controlProperties?.[property.name]) {
|
|
261
|
-
// check whether the property should be ignored in design time or not
|
|
262
|
-
// if it's 'undefined' then it's not considered when building isEnabled because it's 'true'
|
|
263
|
-
ignore = controlProperties[property.name].ignore;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
//updating i18n text for the control if bindingInfo has bindingString
|
|
267
|
-
const controlNewData: NewControlData = {
|
|
268
|
-
id: control.getId(),
|
|
269
|
-
name: property.name,
|
|
270
|
-
newValue: control.getProperty(property.name)
|
|
271
|
-
};
|
|
272
|
-
const bindingInfo: { bindingString?: string } = control.getBindingInfo(controlNewData.name) as {
|
|
273
|
-
bindingString?: string;
|
|
274
|
-
};
|
|
275
|
-
if (bindingInfo?.bindingString !== undefined) {
|
|
276
|
-
controlNewData.newValue = bindingInfo.bindingString;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// A property is enabled if:
|
|
280
|
-
// 1. The property supports changes
|
|
281
|
-
// 2. The control has stable ID
|
|
282
|
-
// 3. It is not configured to be ignored in design time
|
|
283
|
-
// 4. And control overlay is selectable
|
|
284
|
-
isEnabled = isControlEnabled(analyzedType, hasStableId, ignore, controlOverlay);
|
|
437
|
+
const processed =
|
|
438
|
+
property && 'configuration' in property
|
|
439
|
+
? processConfigProperty(property, control)
|
|
440
|
+
: processControlProperty(property, control, hasStableId, controlProperties, controlOverlay);
|
|
285
441
|
|
|
286
|
-
|
|
442
|
+
if (!processed) {
|
|
443
|
+
continue;
|
|
287
444
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const ui5Type = analyzedType.ui5Type || undefined;
|
|
293
|
-
const readableName = convertCamelCaseToPascalCase(property.name);
|
|
294
|
-
let docu:
|
|
295
|
-
| {
|
|
296
|
-
defaultValue: string;
|
|
297
|
-
description: string;
|
|
298
|
-
propertyName: string;
|
|
299
|
-
type?: string;
|
|
300
|
-
propertyType?: string;
|
|
301
|
-
}
|
|
302
|
-
| undefined;
|
|
303
|
-
if ('configuration' in property) {
|
|
304
|
-
const defValue = ['undefined', 'null'].includes(String(property.defaultValue))
|
|
305
|
-
? '-'
|
|
306
|
-
: String(property.defaultValue);
|
|
307
|
-
docu = {
|
|
308
|
-
description: property.description,
|
|
309
|
-
propertyName: property.id,
|
|
310
|
-
type: property.type,
|
|
311
|
-
defaultValue: defValue || '-'
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
switch (analyzedType.primitiveType) {
|
|
315
|
-
case 'enum': {
|
|
316
|
-
const values = analyzedType.enumValues ?? {};
|
|
317
|
-
const options: { key: string; text: string }[] = Object.keys(values).map((key) => ({
|
|
318
|
-
key,
|
|
319
|
-
text: values[key]
|
|
320
|
-
}));
|
|
321
|
-
properties.push({
|
|
322
|
-
type: STRING_VALUE_TYPE,
|
|
323
|
-
editor: DROPDOWN_EDITOR_TYPE,
|
|
324
|
-
propertyType,
|
|
325
|
-
name: property.name,
|
|
326
|
-
readableName,
|
|
327
|
-
value: value as string,
|
|
328
|
-
isEnabled,
|
|
329
|
-
ui5Type,
|
|
330
|
-
options,
|
|
331
|
-
...(docu && { documentation: docu })
|
|
332
|
-
});
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
case 'string': {
|
|
336
|
-
properties.push({
|
|
337
|
-
type: STRING_VALUE_TYPE,
|
|
338
|
-
editor: INPUT_EDITOR_TYPE,
|
|
339
|
-
propertyType,
|
|
340
|
-
name: property.name,
|
|
341
|
-
readableName,
|
|
342
|
-
value: value as string,
|
|
343
|
-
isEnabled,
|
|
344
|
-
isIcon,
|
|
345
|
-
ui5Type,
|
|
346
|
-
...(docu && { documentation: docu })
|
|
347
|
-
});
|
|
348
|
-
break;
|
|
349
|
-
}
|
|
350
|
-
case 'int': {
|
|
351
|
-
properties.push({
|
|
352
|
-
type: INTEGER_VALUE_TYPE,
|
|
353
|
-
editor: INPUT_EDITOR_TYPE,
|
|
354
|
-
propertyType,
|
|
355
|
-
name: property.name,
|
|
356
|
-
readableName,
|
|
357
|
-
value: value as number,
|
|
358
|
-
isEnabled,
|
|
359
|
-
ui5Type,
|
|
360
|
-
...(docu && { documentation: docu })
|
|
361
|
-
});
|
|
362
|
-
break;
|
|
363
|
-
}
|
|
364
|
-
case 'float': {
|
|
365
|
-
properties.push({
|
|
366
|
-
type: FLOAT_VALUE_TYPE,
|
|
367
|
-
editor: INPUT_EDITOR_TYPE,
|
|
368
|
-
propertyType,
|
|
369
|
-
name: property.name,
|
|
370
|
-
readableName,
|
|
371
|
-
value: value as number,
|
|
372
|
-
isEnabled,
|
|
373
|
-
ui5Type,
|
|
374
|
-
...(docu && { documentation: docu })
|
|
375
|
-
});
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
case 'boolean': {
|
|
379
|
-
properties.push({
|
|
380
|
-
type: BOOLEAN_VALUE_TYPE,
|
|
381
|
-
editor: CHECKBOX_EDITOR_TYPE,
|
|
382
|
-
propertyType,
|
|
383
|
-
name: property.name,
|
|
384
|
-
readableName,
|
|
385
|
-
value: value as boolean,
|
|
386
|
-
isEnabled,
|
|
387
|
-
ui5Type,
|
|
388
|
-
...(docu && { documentation: docu })
|
|
389
|
-
});
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
445
|
+
|
|
446
|
+
const entry = buildPropertyEntry(property, processed, selectedControlName);
|
|
447
|
+
if (entry) {
|
|
448
|
+
properties.push(entry);
|
|
392
449
|
}
|
|
393
450
|
}
|
|
451
|
+
|
|
394
452
|
return {
|
|
395
|
-
id: control.getId(),
|
|
396
|
-
type: selectedControlName,
|
|
453
|
+
id: control.getId(),
|
|
454
|
+
type: selectedControlName,
|
|
397
455
|
properties: [...properties].sort((a, b) => (a.name > b.name ? 1 : -1)),
|
|
398
456
|
name: selectedControlName
|
|
399
457
|
};
|
|
@@ -150,9 +150,9 @@ sap.ui.define(["sap/base/Log", "open/ux/preview/client/thirdparty/@sap-ux-privat
|
|
|
150
150
|
async function registerComponentDependencyPaths(appUrls, urlParams) {
|
|
151
151
|
const libs = await getManifestLibs(appUrls);
|
|
152
152
|
if (libs && libs.length > 0) {
|
|
153
|
-
let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs;
|
|
153
|
+
let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + encodeURIComponent(libs);
|
|
154
154
|
const sapClient = urlParams.get('sap-client');
|
|
155
|
-
if (sapClient?.length === 3) {
|
|
155
|
+
if (sapClient?.length === 3 && /^\d+$/.test(sapClient)) {
|
|
156
156
|
url = url + '&sap-client=' + sapClient;
|
|
157
157
|
}
|
|
158
158
|
const response = await fetch(url);
|
|
@@ -185,9 +185,9 @@ export async function resetAppState(container: typeof sap.ushell.Container): Pro
|
|
|
185
185
|
export async function registerComponentDependencyPaths(appUrls: string[], urlParams: URLSearchParams): Promise<void> {
|
|
186
186
|
const libs = await getManifestLibs(appUrls);
|
|
187
187
|
if (libs && libs.length > 0) {
|
|
188
|
-
let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs;
|
|
188
|
+
let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + encodeURIComponent(libs);
|
|
189
189
|
const sapClient = urlParams.get('sap-client');
|
|
190
|
-
if (sapClient?.length === 3) {
|
|
190
|
+
if (sapClient?.length === 3 && /^\d+$/.test(sapClient)) {
|
|
191
191
|
url = url + '&sap-client=' + sapClient;
|
|
192
192
|
}
|
|
193
193
|
const response = await fetch(url);
|
|
@@ -67,6 +67,8 @@ ADP_CREATE_CONTROLLER_EXTENSION_TITLE = Create Controller Extension
|
|
|
67
67
|
ADP_CREATE_CONTROLLER_EXTENSION_DESCRIPTION = Controller extension with name ''{0}'' was created.
|
|
68
68
|
ADP_ODATA_HEALTH_CHECK_TITLE = OData Service Health Check
|
|
69
69
|
ADP_ODATA_SERVICE_DOWN_DESCRIPTION = The OData service with the {0} endpoint is down. Error: {1}.
|
|
70
|
+
ADP_ORPHANED_CHANGE_ERROR_TITLE = Missing File Detected
|
|
71
|
+
ADP_ORPHANED_FILE_DESCRIPTION = The "{0}" file referenced by the "{1}" change was not found. To resolve this error, delete the change file.
|
|
70
72
|
ADP_ADD_ACTION_DIALOG_ACTION_ID_LABEL = Action ID
|
|
71
73
|
ADP_ADD_ACTION_DIALOG_BUTTON_TEXT_LABEL = Button Text
|
|
72
74
|
ADP_ADD_ACTION_DIALOG_HANDLER_METHOD_LABEL = Handler Method
|