@acorex/modules 20.7.11 → 20.7.13

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 (17) hide show
  1. package/data-management/index.d.ts +250 -4
  2. package/document-management/index.d.ts +4 -2
  3. package/fesm2022/acorex-modules-data-management.mjs +1 -1
  4. package/fesm2022/acorex-modules-data-management.mjs.map +1 -1
  5. package/fesm2022/acorex-modules-document-management.mjs +344 -132
  6. package/fesm2022/acorex-modules-document-management.mjs.map +1 -1
  7. package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-BAIx0BJe.mjs → acorex-modules-platform-management-acorex-modules-platform-management-ndbiBVUt.mjs} +13 -9
  8. package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-ndbiBVUt.mjs.map +1 -0
  9. package/fesm2022/{acorex-modules-platform-management-menu-list.component-BxGLun0f.mjs → acorex-modules-platform-management-menu-list.component-w-Ix2VZA.mjs} +2 -2
  10. package/fesm2022/{acorex-modules-platform-management-menu-list.component-BxGLun0f.mjs.map → acorex-modules-platform-management-menu-list.component-w-Ix2VZA.mjs.map} +1 -1
  11. package/fesm2022/acorex-modules-platform-management.mjs +1 -1
  12. package/fesm2022/{acorex-modules-product-catalog-product.entity-BXDh4Wlu.mjs → acorex-modules-product-catalog-product.entity-CVW7-ye7.mjs} +2 -28
  13. package/fesm2022/acorex-modules-product-catalog-product.entity-CVW7-ye7.mjs.map +1 -0
  14. package/fesm2022/acorex-modules-product-catalog.mjs +1 -1
  15. package/package.json +2 -2
  16. package/fesm2022/acorex-modules-platform-management-acorex-modules-platform-management-BAIx0BJe.mjs.map +0 -1
  17. package/fesm2022/acorex-modules-product-catalog-product.entity-BXDh4Wlu.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { AXPSessionService, AXPAuthGuard, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex/platform/auth';
2
- import { AXPEntityService, AXMEntityCrudServiceImpl, entityMasterCrudActions, entityMasterRecordActions, cloneLayoutArrays, ensureLayoutSection, ensureLayoutPropertyView, ensureListActions, actionExists, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
2
+ import { AXPEntityService, AXMEntityCrudServiceImpl, entityMasterCrudActions, entityMasterRecordActions, cloneLayoutArrays, ensureLayoutSection, ensureLayoutPropertyView, ensureListActions, actionExists, AXPEntityDefinitionRegistryService, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_STORAGE_MIDDLEWARE, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
3
3
  import { AXPHookService, AXPPlatformScope, AXPDataGenerator, AXP_MODULE_MANIFEST_PROVIDER, AXP_FEATURE_DEFINITION_PROVIDER, AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, AXPDeviceService, resolvePlatformScopeKey, resolvePlatformScopeName } from '@acorex/platform/core';
4
4
  import { AXPSearchCommandProvider, createAllQueryView, createQueryView, AXPEntityCommandScope, AXPEntityQueryType, AXPFileTypeProviderService, AXP_FILE_TYPE_INFO_PROVIDER, AXPFileStorageService, AXPFilterOperatorMiddlewareService, AXPSettingsService, AXPLockService, UploadFromComputerActionProvider, AXP_FILE_ACTION_PROVIDER, AXP_MENU_PROVIDER, AXP_SEARCH_PROVIDER } from '@acorex/platform/common';
5
5
  import * as i0 from '@angular/core';
@@ -1370,7 +1370,7 @@ async function documentTypeFactory(injector) {
1370
1370
  actions: [...entityMasterRecordActions()],
1371
1371
  },
1372
1372
  list: {
1373
- actions: [...entityMasterCrudActions()],
1373
+ actions: [...entityMasterCrudActions({ edit: true })],
1374
1374
  views: [createAllQueryView()],
1375
1375
  },
1376
1376
  },
@@ -4120,7 +4120,7 @@ class AXMDocumentDialogService {
4120
4120
  if (showMeta) {
4121
4121
  flex.formField('@document-management:terms.common.meta-data', field => {
4122
4122
  field.path(`file-${index}.metaDataForm`);
4123
- field.customWidget('meta-data-form-editor', documentType?.metaDataList);
4123
+ field.customWidget('meta-data-form-editor', documentType?.metaDataList ?? { groups: [] });
4124
4124
  });
4125
4125
  }
4126
4126
  flex.formField('@document-management:terms.common.file', field => {
@@ -4203,10 +4203,16 @@ class AXMDocumentDialogService {
4203
4203
  .map((f) => `.${f.name.split('.')[1]}`)
4204
4204
  .join(',');
4205
4205
  const fileList = Array.isArray(files) ? files : [];
4206
- // Build initial context for all files
4206
+ /** Get metaDataForm from a file item (widget may store under meta or metaDataForm). */
4207
+ const getFileMeta = (item) => item?.metaDataForm ?? (item?.meta && typeof item.meta === 'object' && !Array.isArray(item.meta) ? item.meta : undefined);
4208
+ // Build initial context for all files: each file-${index} gets that file's metaDataForm so form shows existing meta
4207
4209
  const initialContext = {};
4208
4210
  if (showMeta) {
4209
- initialContext['file-0'] = { metaDataForm: metaData };
4211
+ fileList.forEach((item, index) => {
4212
+ initialContext[`file-${index}`] = { metaDataForm: getFileMeta(item) ?? metaData };
4213
+ });
4214
+ if (fileList.length === 0)
4215
+ initialContext['file-0'] = { metaDataForm: metaData };
4210
4216
  }
4211
4217
  // Initialize files array context with default values
4212
4218
  initialContext['files'] = fileList.map(item => ({ file: [item] }));
@@ -4225,7 +4231,7 @@ class AXMDocumentDialogService {
4225
4231
  if (showMeta) {
4226
4232
  flex.formField('@document-management:terms.common.meta-data', field => {
4227
4233
  field.path(`file-${index}.metaDataForm`);
4228
- field.customWidget('meta-data-form-editor', documentType?.metaDataList);
4234
+ field.customWidget('meta-data-form-editor', documentType?.metaDataList ?? { groups: [] });
4229
4235
  });
4230
4236
  }
4231
4237
  flex.formField('File', field => {
@@ -4252,15 +4258,17 @@ class AXMDocumentDialogService {
4252
4258
  };
4253
4259
  }
4254
4260
  const ctx = dialogRef.context();
4255
- return {
4256
- data: {
4257
- cancel: false,
4258
- files: (ctx?.['files']).map((item, index) => ({
4259
- ...item,
4260
- ...(showMeta ? { metaDataForm: ctx?.[`file-${index}`]?.['metaDataForm'] } : {}),
4261
- })),
4262
- },
4263
- };
4261
+ const outFiles = (ctx?.['files']).map((item, index) => ({
4262
+ ...item,
4263
+ ...(showMeta ? { metaDataForm: ctx?.[`file-${index}`]?.['metaDataForm'] } : {}),
4264
+ }));
4265
+ if (showMeta && outFiles.length) {
4266
+ console.debug('[document-dialog] openAttachmentDialogWithoutSave result', {
4267
+ filesCount: outFiles.length,
4268
+ firstFileMetaDataForm: outFiles[0]?.metaDataForm,
4269
+ });
4270
+ }
4271
+ return { data: { cancel: false, files: outFiles } };
4264
4272
  });
4265
4273
  })
4266
4274
  .show();
@@ -5581,49 +5589,13 @@ class AXMDocumentManagerService {
5581
5589
  const ext = item.name.split('.').pop()?.toLowerCase();
5582
5590
  return ext && AXMDocumentManagerService.GALLERY_EXTENSIONS.includes(ext);
5583
5591
  });
5584
- // Fetch documents with null fileId.source.value
5585
- const documentsWithNullFileId = filteredDocuments.filter((doc) => !doc.fileId.source.value);
5586
- // Fetch missing documents and replace them in the array
5587
- if (documentsWithNullFileId.length > 0) {
5588
- const fetchedDocuments = await Promise.all(documentsWithNullFileId.map((doc) => this.documentService.getOne(doc.id)));
5589
- // Replace documents in filteredDocuments with fetched ones
5590
- fetchedDocuments.forEach((fetchedDoc) => {
5591
- const index = filteredDocuments.findIndex((doc) => doc.id === fetchedDoc.id);
5592
- if (index !== -1) {
5593
- filteredDocuments[index] = fetchedDoc;
5594
- }
5595
- });
5596
- }
5597
- // Collect all fileIds that need to be fetched
5598
- const fileIds = filteredDocuments.filter((doc) => doc.fileId.source.value).map((doc) => doc.fileId.source.value);
5599
- // Fetch all file info at once (need URL for media viewer)
5600
- const fileInfosMap = new Map();
5601
- if (fileIds.length > 0) {
5602
- const infos = await Promise.all(fileIds.map((id) => this.fileService.getInfo(id)));
5603
- infos.forEach((info) => {
5604
- if (info?.fileId) {
5605
- fileInfosMap.set(info.fileId, info);
5606
- }
5607
- });
5608
- }
5609
5592
  // Convert documents to AXPFileListItem[] format for the gallery widget
5610
- const fileListItems = await Promise.all(filteredDocuments.map(async (doc) => {
5611
- let url = '';
5612
- if (doc.fileId?.source?.kind === 'fileId') {
5613
- const fileId = doc.fileId.source.value;
5614
- const fileInfo = fileInfosMap.get(fileId);
5615
- url = fileInfo?.url ?? '';
5616
- }
5617
- return {
5618
- id: doc.id ?? '',
5619
- name: doc.name,
5620
- size: doc.size,
5621
- status: 'attached',
5622
- source: {
5623
- kind: 'url',
5624
- value: url,
5625
- },
5626
- };
5593
+ const fileListItems = filteredDocuments.map((doc) => ({
5594
+ id: doc.id ?? '',
5595
+ name: doc.name,
5596
+ size: doc.size,
5597
+ status: 'attached',
5598
+ source: doc.fileId?.source,
5627
5599
  }));
5628
5600
  // Calculate start index
5629
5601
  const startIndex = isVirtualFolder
@@ -7180,55 +7152,321 @@ const attachmentsPlugin = {
7180
7152
  },
7181
7153
  };
7182
7154
 
7183
- // ---------------- Configure: set accept based on docTypeId ----------------
7155
+ /** Extract metadata for server (metaDataForm) from widget item (meta or metaDataForm). */
7156
+ function getMetaDataFormForServer(item) {
7157
+ if (item?.meta?.metaDataForm != null)
7158
+ return item.meta.metaDataForm;
7159
+ if (item?.metaDataForm != null)
7160
+ return item.metaDataForm;
7161
+ if (item?.meta != null && typeof item.meta === 'object' && !Array.isArray(item.meta))
7162
+ return item.meta;
7163
+ return undefined;
7164
+ }
7165
+ /** Resolve list of attachment field names from entity extensions.attachments. */
7166
+ function getAttachmentFields(attachCfg) {
7167
+ if (!attachCfg || typeof attachCfg !== 'object')
7168
+ return [];
7169
+ if (typeof attachCfg.field === 'string')
7170
+ return [attachCfg.field];
7171
+ return Object.keys(attachCfg).filter((k) => k && typeof k === 'string');
7172
+ }
7173
+ /** Fallback: get attachment field names from properties with file-uploader interface (when extensions.attachments is missing e.g. cached def). */
7174
+ function getAttachmentFieldsFromProperties(def) {
7175
+ const props = def?.properties;
7176
+ if (!Array.isArray(props))
7177
+ return [];
7178
+ return props
7179
+ .filter((p) => p?.schema?.interface?.type === 'file-uploader' && p?.name)
7180
+ .map((p) => p.name);
7181
+ }
7182
+ /**
7183
+ * Handles file list fields configured via entity.extensions.attachments.
7184
+ * Preserves metaDataForm from widget; uploads pending blobs after persistence and writes final references.
7185
+ * Runs before file-cast (order 25) so blobs are uploaded before file-cast sees the data.
7186
+ */
7187
+ // Run before file-cast (order 25) so blobs are uploaded and refs written before file-cast sees the data
7188
+ const attachmentsUploadMiddleware = {
7189
+ target: { ops: ['create', 'update'], order: 22 },
7190
+ execute: async (ctx, next) => {
7191
+ const registry = inject(AXPEntityDefinitionRegistryService);
7192
+ const storage = inject(AXPFileStorageService);
7193
+ const [moduleName, entityNameOnly] = ctx.entityName.split('.');
7194
+ const def = await registry.resolve(moduleName, entityNameOnly).catch(() => null);
7195
+ const attachCfg = def?.extensions?.attachments;
7196
+ let fields = getAttachmentFields(attachCfg);
7197
+ if (!fields.length && def)
7198
+ fields = getAttachmentFieldsFromProperties(def);
7199
+ if (!fields.length) {
7200
+ await next();
7201
+ return;
7202
+ }
7203
+ const pendingByField = ctx.locals.attach_pending ?? {};
7204
+ ctx.locals.attach_pending = pendingByField;
7205
+ if (ctx.data && typeof ctx.data === 'object') {
7206
+ for (const field of fields) {
7207
+ const list = Array.isArray(ctx.data[field]) ? ctx.data[field] : [];
7208
+ pendingByField[field] = [];
7209
+ const normalized = [];
7210
+ for (const item of list) {
7211
+ const metaDataForm = getMetaDataFormForServer(item);
7212
+ if (item?.source?.kind === 'blob' && item.source.value instanceof Blob) {
7213
+ const blob = item.source.value;
7214
+ const name = item.name || `file-${Date.now()}`;
7215
+ pendingByField[field].push({ fileItem: { ...item, name, metaDataForm }, blob });
7216
+ const entry = {
7217
+ tempId: item.id,
7218
+ name,
7219
+ size: item.size ?? blob.size,
7220
+ status: 'pending_upload',
7221
+ };
7222
+ if (metaDataForm)
7223
+ entry.metaDataForm = metaDataForm;
7224
+ normalized.push(entry);
7225
+ }
7226
+ else if (item?.source?.kind === 'fileId' && item.id && typeof item.source.value === 'string') {
7227
+ const entry = {
7228
+ id: item.id,
7229
+ name: item.name,
7230
+ size: item.size,
7231
+ status: item.status || 'uploaded',
7232
+ source: { kind: 'fileId', value: item.source.value },
7233
+ };
7234
+ if (metaDataForm)
7235
+ entry.metaDataForm = metaDataForm;
7236
+ normalized.push(entry);
7237
+ }
7238
+ else if (item?.id && item?.name) {
7239
+ const entry = {
7240
+ id: item.id,
7241
+ name: item.name,
7242
+ size: item.size,
7243
+ status: item.status || 'uploaded',
7244
+ source: item.source ? { kind: item.source.kind, value: item.source.value } : undefined,
7245
+ };
7246
+ if (metaDataForm)
7247
+ entry.metaDataForm = metaDataForm;
7248
+ normalized.push(entry);
7249
+ }
7250
+ }
7251
+ const dataForBackend = normalized.filter((x) => x.status !== 'pending_upload');
7252
+ ctx.data[field] = dataForBackend;
7253
+ }
7254
+ }
7255
+ await next();
7256
+ const refId = ctx.op === 'create' ? String(ctx.result) : String(ctx.id);
7257
+ const refType = ctx.entityName;
7258
+ for (const field of fields) {
7259
+ const pending = pendingByField[field] ?? [];
7260
+ if (!pending.length)
7261
+ continue;
7262
+ if (!storage)
7263
+ continue;
7264
+ const finalRefs = (ctx.data[field] ?? []).filter((x) => x.status !== 'pending_upload');
7265
+ for (const { fileItem, blob } of pending) {
7266
+ const metaDataForm = fileItem.metaDataForm ?? getMetaDataFormForServer(fileItem);
7267
+ try {
7268
+ const info = await storage.save({
7269
+ file: blob,
7270
+ refId,
7271
+ refType,
7272
+ category: '',
7273
+ name: fileItem.name,
7274
+ });
7275
+ await storage.commit(info.fileId);
7276
+ const entry = {
7277
+ id: info.fileId,
7278
+ name: info.name || fileItem.name,
7279
+ size: info.size || blob.size,
7280
+ type: info.mimeType || blob.type,
7281
+ status: 'uploaded',
7282
+ source: { kind: 'fileId', value: info.fileId },
7283
+ };
7284
+ if (metaDataForm)
7285
+ entry.metaDataForm = metaDataForm;
7286
+ finalRefs.push(entry);
7287
+ }
7288
+ catch (err) {
7289
+ finalRefs.push({
7290
+ tempId: fileItem.id,
7291
+ name: fileItem.name,
7292
+ size: fileItem.size,
7293
+ type: blob.type,
7294
+ status: 'upload_failed',
7295
+ error: err?.message,
7296
+ });
7297
+ }
7298
+ }
7299
+ const entityId = ctx.op === 'create' ? ctx.result : ctx.id;
7300
+ if (entityId != null) {
7301
+ const payload = { [field]: finalRefs };
7302
+ const withMeta = finalRefs.filter((r) => r?.metaDataForm != null).length;
7303
+ console.log('[attachments-upload] ذخیره — الان میخوام ctx.backend.updateOne بزنم (پچ با metaDataForm)', {
7304
+ entityName: ctx.entityName,
7305
+ entityId,
7306
+ field,
7307
+ finalRefsLength: finalRefs.length,
7308
+ withMetaCount: withMeta,
7309
+ payloadSample: payload[field]?.map((r, i) => ({ i, hasMeta: !!r?.metaDataForm })),
7310
+ });
7311
+ try {
7312
+ await ctx.backend.updateOne(ctx.entityName, entityId, payload);
7313
+ console.log('[attachments-upload] ذخیره — ctx.backend.updateOne انجام شد (ok)');
7314
+ }
7315
+ catch (err) {
7316
+ console.warn('[attachments-upload] ذخیره — ctx.backend.updateOne خطا', err);
7317
+ // Result already persisted; optional patch for final refs failed
7318
+ }
7319
+ }
7320
+ if (typeof ctx.result === 'object' && ctx.result !== null) {
7321
+ ctx.result = { ...ctx.result, [field]: finalRefs };
7322
+ }
7323
+ }
7324
+ },
7325
+ };
7326
+
7327
+ //#region ---- Plugin Guard Utilities ----
7328
+ /**
7329
+ * Check if the 'document' plugin is active (enabled and not excluded) in the payload.
7330
+ */
7331
+ function isDocumentPluginActive(payload) {
7332
+ const isEnabled = Array.isArray(payload.plugins) && payload.plugins.some((p) => p.name === 'document');
7333
+ const isExcluded = Array.isArray(payload.excludePlugins) && payload.excludePlugins.includes('document');
7334
+ return isEnabled && !isExcluded;
7335
+ }
7336
+ /**
7337
+ * Extract document plugin options from payload.
7338
+ * Returns undefined if plugin is not active or options are missing.
7339
+ */
7340
+ function getDocumentPluginOptions(payload) {
7341
+ if (!isDocumentPluginActive(payload)) {
7342
+ return undefined;
7343
+ }
7344
+ return (payload.plugins.find((p) => p.name === 'document')?.options ?? {});
7345
+ }
7346
+ /** Resolve list of document type names from plugin options (single or multiple). */
7347
+ function getDocTypeNames(opts) {
7348
+ if (!opts)
7349
+ return [];
7350
+ if (Array.isArray(opts.docTypeNames) && opts.docTypeNames.length > 0)
7351
+ return opts.docTypeNames;
7352
+ if (typeof opts.docTypeName === 'string' && opts.docTypeName.trim())
7353
+ return [opts.docTypeName.trim()];
7354
+ return [];
7355
+ }
7356
+ //#endregion
7357
+ // ---------------- Configure: set accept based on docTypeId / docTypeNames ----------------
7184
7358
  const DocumentFileUploaderConfigureProvider = {
7185
7359
  key: 'file-uploader.configure',
7186
7360
  priority: 0,
7187
7361
  async execute(payload) {
7188
- const isEnabled = Array.isArray(payload.plugins) && payload.plugins.some((p) => p.name === 'document');
7189
- const isExcluded = Array.isArray(payload.excludePlugins) && payload.excludePlugins.includes('document');
7190
- if (!isEnabled || isExcluded)
7191
- return payload;
7192
- const opts = (payload.plugins.find((p) => p.name === 'document')?.options ?? {});
7193
- if (!opts.docTypeName)
7362
+ const opts = getDocumentPluginOptions(payload);
7363
+ const names = getDocTypeNames(opts);
7364
+ if (!names.length)
7194
7365
  return payload;
7195
7366
  const documentManagementService = inject(AXPDocumentManagementService);
7196
- const docType = await documentManagementService.getDocumentTypeByName(opts.docTypeName);
7197
- const selected = docType?.type?.selectedItems ?? [];
7198
- const extensions = Array.from(new Set(selected
7199
- .map((s) => s?.name)
7200
- .filter((n) => typeof n === 'string' && n.includes('.'))
7201
- .map((n) => `.${n.split('.').pop()?.toLowerCase()}`)));
7367
+ const allExtensions = [];
7368
+ for (const name of names) {
7369
+ const docType = await documentManagementService.getDocumentTypeByName(name);
7370
+ const selected = docType?.type?.selectedItems ?? [];
7371
+ const extList = selected
7372
+ .map((s) => s?.name)
7373
+ .filter((n) => typeof n === 'string' && n.includes('.'))
7374
+ .map((n) => `.${n.split('.').pop()?.toLowerCase()}`);
7375
+ allExtensions.push(...extList);
7376
+ }
7377
+ const extensions = Array.from(new Set(allExtensions));
7202
7378
  payload.overrides = payload.overrides || {};
7203
7379
  payload.overrides['accept'] = extensions.join(',');
7204
7380
  return payload;
7205
7381
  },
7206
7382
  };
7383
+ // ---------------- Actions: one "Upload" button per document type (replaces generic Upload from Device) ----------------
7384
+ const DocumentFileUploaderActionsByTypeProvider = {
7385
+ key: 'file-uploader.actions',
7386
+ priority: 5,
7387
+ execute: async (payload) => {
7388
+ const opts = getDocumentPluginOptions({
7389
+ plugins: payload.plugins,
7390
+ excludePlugins: payload.excludePlugins ?? [],
7391
+ capabilities: payload.capabilities,
7392
+ });
7393
+ const docTypeNames = getDocTypeNames(opts);
7394
+ if (docTypeNames.length === 0)
7395
+ return payload;
7396
+ const documentManagementService = inject(AXPDocumentManagementService);
7397
+ const documentDialogService = inject(AXMDocumentDialogService);
7398
+ const fileService = inject(AXFileService);
7399
+ const docTypes = await Promise.all(docTypeNames.map((name) => documentManagementService.getDocumentTypeByName(name)));
7400
+ const byTypeActions = docTypes
7401
+ .filter((dt) => dt != null)
7402
+ .map((docType) => {
7403
+ const selected = docType?.type?.selectedItems ?? [];
7404
+ const accept = Array.from(new Set(selected
7405
+ .map((s) => s?.name)
7406
+ .filter((n) => typeof n === 'string' && n.includes('.'))
7407
+ .map((n) => `.${n.split('.').pop()?.toLowerCase()}`))).join(',');
7408
+ const typeName = docType?.name;
7409
+ const title = docType?.title ?? typeName;
7410
+ const hasMeta = Array.isArray(docType?.metaDataList?.groups) &&
7411
+ docType.metaDataList.groups.length > 0;
7412
+ return {
7413
+ plugin: 'document',
7414
+ global: false,
7415
+ text: title,
7416
+ icon: 'fa-light fa-file-arrow-up',
7417
+ run: async (capabilities) => {
7418
+ const files = await fileService.choose({
7419
+ multiple: payload.options?.multiple ?? true,
7420
+ accept: accept || undefined,
7421
+ });
7422
+ if (files.length === 0)
7423
+ return;
7424
+ const fileItems = files.map((file) => ({
7425
+ id: AXPDataGenerator.uuid(),
7426
+ name: file.name,
7427
+ size: file.size,
7428
+ type: file.type,
7429
+ status: 'attached',
7430
+ source: { kind: 'blob', value: file },
7431
+ __documentTypeName: typeName,
7432
+ }));
7433
+ if (hasMeta) {
7434
+ const result = await documentDialogService.openAttachmentDialogWithoutSave('Document Type Choose File', {
7435
+ documentType: docType,
7436
+ files: fileItems,
7437
+ });
7438
+ if (result.data?.cancel != null && !result.data.cancel && result.data?.files) {
7439
+ result.data.files.forEach((f, i) => {
7440
+ if (fileItems[i])
7441
+ set(fileItems[i], 'metaDataForm', f?.metaDataForm);
7442
+ });
7443
+ }
7444
+ else if (result.data?.cancel) {
7445
+ return;
7446
+ }
7447
+ }
7448
+ capabilities.addFiles(fileItems);
7449
+ },
7450
+ };
7451
+ });
7452
+ payload.actions = payload.actions.filter((a) => a.plugin !== 'upload-from-computer');
7453
+ payload.actions = [...byTypeActions, ...payload.actions];
7454
+ return payload;
7455
+ },
7456
+ };
7207
7457
  // --------------- After Files Added: scaffold for post-add processing ---------------
7208
7458
  const DocumentFileUploaderBeforeFilesAddedProvider = {
7209
7459
  key: 'file-uploader.beforeFilesAdded',
7210
7460
  priority: 0,
7211
7461
  async execute(payload) {
7212
- // Debug: trace before-files-added hook entry
7213
- // eslint-disable-next-line no-console
7214
- console.log('[DocumentFileUploaderBeforeFilesAdded] payload', {
7215
- plugins: payload.plugins,
7216
- excludePlugins: payload.excludePlugins,
7217
- newFiles: payload.newFiles,
7218
- });
7219
- const isEnabled = Array.isArray(payload.plugins) && payload.plugins.some((p) => p.name === 'document');
7220
- const isExcluded = Array.isArray(payload.excludePlugins) && payload.excludePlugins.includes('document');
7221
- if (!isEnabled || isExcluded)
7222
- return payload;
7223
- const opts = (payload.plugins.find((p) => p.name === 'document')?.options ?? {});
7224
- // Debug: trace resolved options
7225
- // eslint-disable-next-line no-console
7226
- console.log('[DocumentFileUploaderBeforeFilesAdded] options', opts);
7227
- if (!opts.docTypeName)
7462
+ const opts = getDocumentPluginOptions(payload);
7463
+ const docTypeName = payload.newFiles[0]?.__documentTypeName ??
7464
+ opts?.docTypeName;
7465
+ if (!docTypeName)
7228
7466
  return payload;
7229
7467
  const injector = inject(Injector);
7230
7468
  const documentManagementService = injector.get(AXPDocumentManagementService);
7231
- const docType = await documentManagementService.getDocumentTypeByName(opts.docTypeName);
7469
+ const docType = await documentManagementService.getDocumentTypeByName(docTypeName);
7232
7470
  const documentDialogService = injector.get(AXMDocumentDialogService);
7233
7471
  const result = await documentDialogService.openAttachmentDialogWithoutSave('Document Type Choose File', {
7234
7472
  documentType: docType,
@@ -7247,41 +7485,18 @@ const DocumentFileUploaderEditDialogAfterFormProvider = {
7247
7485
  key: 'file-uploader.edit-dialog.groups.after-form',
7248
7486
  priority: 0,
7249
7487
  async execute(payload) {
7250
- // Debug: trace edit-dialog hook entry
7251
- // eslint-disable-next-line no-console
7252
- console.log('[DocumentFileUploaderEditDialogAfterForm] payload (before)', {
7253
- plugins: payload.plugins,
7254
- excludePlugins: payload.excludePlugins,
7255
- file: payload.file,
7256
- groups: payload.groups,
7257
- });
7258
- const isEnabled = Array.isArray(payload.plugins) && payload.plugins.some((p) => p.name === 'document');
7259
- const isExcluded = Array.isArray(payload.excludePlugins) && payload.excludePlugins.includes('document');
7260
- if (!isEnabled || isExcluded)
7261
- return payload;
7262
7488
  const metaObj = payload.file?.metaDataForm;
7263
- // Load document type (similar to document-type-choose-file) when available via plugin options
7264
- const opts = (payload.plugins.find((p) => p.name === 'document')?.options ?? {});
7265
- // Debug: trace resolved options and meta object
7266
- // eslint-disable-next-line no-console
7267
- console.log('[DocumentFileUploaderEditDialogAfterForm] options & meta', {
7268
- options: opts,
7269
- metaObj,
7270
- });
7489
+ const opts = getDocumentPluginOptions(payload);
7490
+ const docTypeName = payload.file?.__documentTypeName ?? opts?.docTypeName;
7271
7491
  const injector = inject(Injector);
7272
7492
  const documentManagementService = injector.get(AXPDocumentManagementService);
7273
- const docType = opts.docTypeName
7274
- ? await documentManagementService.getDocumentTypeByName(opts.docTypeName)
7493
+ const docType = docTypeName
7494
+ ? await documentManagementService.getDocumentTypeByName(docTypeName)
7275
7495
  : undefined;
7276
- // Debug: trace loaded document type and final decision
7277
- // eslint-disable-next-line no-console
7278
- console.log('[DocumentFileUploaderEditDialogAfterForm] loaded docType', {
7279
- docTypeName: opts.docTypeName,
7280
- hasMetaObj: !!metaObj && Object.keys(metaObj).length > 0,
7281
- docType,
7282
- });
7283
7496
  if (!docType && (!metaObj || Object.keys(metaObj).length === 0))
7284
7497
  return payload;
7498
+ const metaDataList = docType?.metaDataList ?? { groups: [] };
7499
+ const hasDefinitions = Array.isArray(metaDataList.groups) && metaDataList.groups.length > 0;
7285
7500
  const fields = [
7286
7501
  {
7287
7502
  path: 'metaDataForm',
@@ -7289,11 +7504,7 @@ const DocumentFileUploaderEditDialogAfterFormProvider = {
7289
7504
  widget: {
7290
7505
  name: 'metaDataForm',
7291
7506
  type: 'meta-data-form-editor',
7292
- // NOTE:
7293
- // - documentType.metaDataList structure is used directly in other dialogs:
7294
- // AXMDocumentDialogService → field.customWidget('meta-data-form-editor', documentType?.metaDataList)
7295
- // - So we pass the whole metaDataList object here as well for consistency.
7296
- options: { definitions: docType?.metaDataList },
7507
+ options: metaDataList,
7297
7508
  defaultValue: metaObj,
7298
7509
  },
7299
7510
  },
@@ -7303,19 +7514,8 @@ const DocumentFileUploaderEditDialogAfterFormProvider = {
7303
7514
  title: docType?.title ?? '@document-management:terms.common.meta-data',
7304
7515
  parameters: fields,
7305
7516
  };
7306
- // metaDataList is an object with a `groups` array; align condition with that structure
7307
- const hasDefinitions = !!docType &&
7308
- !!docType.metaDataList &&
7309
- Array.isArray(docType.metaDataList.groups) &&
7310
- docType.metaDataList.groups.length > 0;
7311
7517
  if (hasDefinitions) {
7312
7518
  payload.groups = [...(payload.groups ?? []), group];
7313
- // Debug: confirm metadata group injection
7314
- // eslint-disable-next-line no-console
7315
- console.log('[DocumentFileUploaderEditDialogAfterForm] metadata group injected', {
7316
- group,
7317
- groupsAfter: payload.groups,
7318
- });
7319
7519
  }
7320
7520
  return payload;
7321
7521
  },
@@ -7410,6 +7610,7 @@ class AXMDocumentManagementModule {
7410
7610
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXMDocumentManagementModule, providers: [
7411
7611
  // Module-specific providers (not part of manifest system)
7412
7612
  { provide: AXP_ENTITY_ACTION_PLUGIN, multi: true, useValue: attachmentsPlugin },
7613
+ { provide: AXP_ENTITY_STORAGE_MIDDLEWARE, multi: true, useValue: attachmentsUploadMiddleware },
7413
7614
  {
7414
7615
  provide: ROUTES,
7415
7616
  multi: true,
@@ -7420,6 +7621,11 @@ class AXMDocumentManagementModule {
7420
7621
  useValue: p,
7421
7622
  multi: true,
7422
7623
  })),
7624
+ {
7625
+ provide: AXP_FILE_ACTION_PROVIDER,
7626
+ useValue: DocumentFileUploaderActionsByTypeProvider,
7627
+ multi: true,
7628
+ },
7423
7629
  // Module Manifest Provider
7424
7630
  {
7425
7631
  provide: AXP_MODULE_MANIFEST_PROVIDER,
@@ -7508,6 +7714,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7508
7714
  providers: [
7509
7715
  // Module-specific providers (not part of manifest system)
7510
7716
  { provide: AXP_ENTITY_ACTION_PLUGIN, multi: true, useValue: attachmentsPlugin },
7717
+ { provide: AXP_ENTITY_STORAGE_MIDDLEWARE, multi: true, useValue: attachmentsUploadMiddleware },
7511
7718
  {
7512
7719
  provide: ROUTES,
7513
7720
  multi: true,
@@ -7518,6 +7725,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7518
7725
  useValue: p,
7519
7726
  multi: true,
7520
7727
  })),
7728
+ {
7729
+ provide: AXP_FILE_ACTION_PROVIDER,
7730
+ useValue: DocumentFileUploaderActionsByTypeProvider,
7731
+ multi: true,
7732
+ },
7521
7733
  // Module Manifest Provider
7522
7734
  {
7523
7735
  provide: AXP_MODULE_MANIFEST_PROVIDER,