@acorex/modules 20.7.12 → 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.
@@ -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();
@@ -7144,55 +7152,321 @@ const attachmentsPlugin = {
7144
7152
  },
7145
7153
  };
7146
7154
 
7147
- // ---------------- 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 ----------------
7148
7358
  const DocumentFileUploaderConfigureProvider = {
7149
7359
  key: 'file-uploader.configure',
7150
7360
  priority: 0,
7151
7361
  async execute(payload) {
7152
- const isEnabled = Array.isArray(payload.plugins) && payload.plugins.some((p) => p.name === 'document');
7153
- const isExcluded = Array.isArray(payload.excludePlugins) && payload.excludePlugins.includes('document');
7154
- if (!isEnabled || isExcluded)
7155
- return payload;
7156
- const opts = (payload.plugins.find((p) => p.name === 'document')?.options ?? {});
7157
- if (!opts.docTypeName)
7362
+ const opts = getDocumentPluginOptions(payload);
7363
+ const names = getDocTypeNames(opts);
7364
+ if (!names.length)
7158
7365
  return payload;
7159
7366
  const documentManagementService = inject(AXPDocumentManagementService);
7160
- const docType = await documentManagementService.getDocumentTypeByName(opts.docTypeName);
7161
- const selected = docType?.type?.selectedItems ?? [];
7162
- const extensions = Array.from(new Set(selected
7163
- .map((s) => s?.name)
7164
- .filter((n) => typeof n === 'string' && n.includes('.'))
7165
- .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));
7166
7378
  payload.overrides = payload.overrides || {};
7167
7379
  payload.overrides['accept'] = extensions.join(',');
7168
7380
  return payload;
7169
7381
  },
7170
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
+ };
7171
7457
  // --------------- After Files Added: scaffold for post-add processing ---------------
7172
7458
  const DocumentFileUploaderBeforeFilesAddedProvider = {
7173
7459
  key: 'file-uploader.beforeFilesAdded',
7174
7460
  priority: 0,
7175
7461
  async execute(payload) {
7176
- // Debug: trace before-files-added hook entry
7177
- // eslint-disable-next-line no-console
7178
- console.log('[DocumentFileUploaderBeforeFilesAdded] payload', {
7179
- plugins: payload.plugins,
7180
- excludePlugins: payload.excludePlugins,
7181
- newFiles: payload.newFiles,
7182
- });
7183
- const isEnabled = Array.isArray(payload.plugins) && payload.plugins.some((p) => p.name === 'document');
7184
- const isExcluded = Array.isArray(payload.excludePlugins) && payload.excludePlugins.includes('document');
7185
- if (!isEnabled || isExcluded)
7186
- return payload;
7187
- const opts = (payload.plugins.find((p) => p.name === 'document')?.options ?? {});
7188
- // Debug: trace resolved options
7189
- // eslint-disable-next-line no-console
7190
- console.log('[DocumentFileUploaderBeforeFilesAdded] options', opts);
7191
- if (!opts.docTypeName)
7462
+ const opts = getDocumentPluginOptions(payload);
7463
+ const docTypeName = payload.newFiles[0]?.__documentTypeName ??
7464
+ opts?.docTypeName;
7465
+ if (!docTypeName)
7192
7466
  return payload;
7193
7467
  const injector = inject(Injector);
7194
7468
  const documentManagementService = injector.get(AXPDocumentManagementService);
7195
- const docType = await documentManagementService.getDocumentTypeByName(opts.docTypeName);
7469
+ const docType = await documentManagementService.getDocumentTypeByName(docTypeName);
7196
7470
  const documentDialogService = injector.get(AXMDocumentDialogService);
7197
7471
  const result = await documentDialogService.openAttachmentDialogWithoutSave('Document Type Choose File', {
7198
7472
  documentType: docType,
@@ -7211,41 +7485,18 @@ const DocumentFileUploaderEditDialogAfterFormProvider = {
7211
7485
  key: 'file-uploader.edit-dialog.groups.after-form',
7212
7486
  priority: 0,
7213
7487
  async execute(payload) {
7214
- // Debug: trace edit-dialog hook entry
7215
- // eslint-disable-next-line no-console
7216
- console.log('[DocumentFileUploaderEditDialogAfterForm] payload (before)', {
7217
- plugins: payload.plugins,
7218
- excludePlugins: payload.excludePlugins,
7219
- file: payload.file,
7220
- groups: payload.groups,
7221
- });
7222
- const isEnabled = Array.isArray(payload.plugins) && payload.plugins.some((p) => p.name === 'document');
7223
- const isExcluded = Array.isArray(payload.excludePlugins) && payload.excludePlugins.includes('document');
7224
- if (!isEnabled || isExcluded)
7225
- return payload;
7226
7488
  const metaObj = payload.file?.metaDataForm;
7227
- // Load document type (similar to document-type-choose-file) when available via plugin options
7228
- const opts = (payload.plugins.find((p) => p.name === 'document')?.options ?? {});
7229
- // Debug: trace resolved options and meta object
7230
- // eslint-disable-next-line no-console
7231
- console.log('[DocumentFileUploaderEditDialogAfterForm] options & meta', {
7232
- options: opts,
7233
- metaObj,
7234
- });
7489
+ const opts = getDocumentPluginOptions(payload);
7490
+ const docTypeName = payload.file?.__documentTypeName ?? opts?.docTypeName;
7235
7491
  const injector = inject(Injector);
7236
7492
  const documentManagementService = injector.get(AXPDocumentManagementService);
7237
- const docType = opts.docTypeName
7238
- ? await documentManagementService.getDocumentTypeByName(opts.docTypeName)
7493
+ const docType = docTypeName
7494
+ ? await documentManagementService.getDocumentTypeByName(docTypeName)
7239
7495
  : undefined;
7240
- // Debug: trace loaded document type and final decision
7241
- // eslint-disable-next-line no-console
7242
- console.log('[DocumentFileUploaderEditDialogAfterForm] loaded docType', {
7243
- docTypeName: opts.docTypeName,
7244
- hasMetaObj: !!metaObj && Object.keys(metaObj).length > 0,
7245
- docType,
7246
- });
7247
7496
  if (!docType && (!metaObj || Object.keys(metaObj).length === 0))
7248
7497
  return payload;
7498
+ const metaDataList = docType?.metaDataList ?? { groups: [] };
7499
+ const hasDefinitions = Array.isArray(metaDataList.groups) && metaDataList.groups.length > 0;
7249
7500
  const fields = [
7250
7501
  {
7251
7502
  path: 'metaDataForm',
@@ -7253,11 +7504,7 @@ const DocumentFileUploaderEditDialogAfterFormProvider = {
7253
7504
  widget: {
7254
7505
  name: 'metaDataForm',
7255
7506
  type: 'meta-data-form-editor',
7256
- // NOTE:
7257
- // - documentType.metaDataList structure is used directly in other dialogs:
7258
- // AXMDocumentDialogService → field.customWidget('meta-data-form-editor', documentType?.metaDataList)
7259
- // - So we pass the whole metaDataList object here as well for consistency.
7260
- options: { definitions: docType?.metaDataList },
7507
+ options: metaDataList,
7261
7508
  defaultValue: metaObj,
7262
7509
  },
7263
7510
  },
@@ -7267,19 +7514,8 @@ const DocumentFileUploaderEditDialogAfterFormProvider = {
7267
7514
  title: docType?.title ?? '@document-management:terms.common.meta-data',
7268
7515
  parameters: fields,
7269
7516
  };
7270
- // metaDataList is an object with a `groups` array; align condition with that structure
7271
- const hasDefinitions = !!docType &&
7272
- !!docType.metaDataList &&
7273
- Array.isArray(docType.metaDataList.groups) &&
7274
- docType.metaDataList.groups.length > 0;
7275
7517
  if (hasDefinitions) {
7276
7518
  payload.groups = [...(payload.groups ?? []), group];
7277
- // Debug: confirm metadata group injection
7278
- // eslint-disable-next-line no-console
7279
- console.log('[DocumentFileUploaderEditDialogAfterForm] metadata group injected', {
7280
- group,
7281
- groupsAfter: payload.groups,
7282
- });
7283
7519
  }
7284
7520
  return payload;
7285
7521
  },
@@ -7374,6 +7610,7 @@ class AXMDocumentManagementModule {
7374
7610
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXMDocumentManagementModule, providers: [
7375
7611
  // Module-specific providers (not part of manifest system)
7376
7612
  { provide: AXP_ENTITY_ACTION_PLUGIN, multi: true, useValue: attachmentsPlugin },
7613
+ { provide: AXP_ENTITY_STORAGE_MIDDLEWARE, multi: true, useValue: attachmentsUploadMiddleware },
7377
7614
  {
7378
7615
  provide: ROUTES,
7379
7616
  multi: true,
@@ -7384,6 +7621,11 @@ class AXMDocumentManagementModule {
7384
7621
  useValue: p,
7385
7622
  multi: true,
7386
7623
  })),
7624
+ {
7625
+ provide: AXP_FILE_ACTION_PROVIDER,
7626
+ useValue: DocumentFileUploaderActionsByTypeProvider,
7627
+ multi: true,
7628
+ },
7387
7629
  // Module Manifest Provider
7388
7630
  {
7389
7631
  provide: AXP_MODULE_MANIFEST_PROVIDER,
@@ -7472,6 +7714,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7472
7714
  providers: [
7473
7715
  // Module-specific providers (not part of manifest system)
7474
7716
  { provide: AXP_ENTITY_ACTION_PLUGIN, multi: true, useValue: attachmentsPlugin },
7717
+ { provide: AXP_ENTITY_STORAGE_MIDDLEWARE, multi: true, useValue: attachmentsUploadMiddleware },
7475
7718
  {
7476
7719
  provide: ROUTES,
7477
7720
  multi: true,
@@ -7482,6 +7725,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7482
7725
  useValue: p,
7483
7726
  multi: true,
7484
7727
  })),
7728
+ {
7729
+ provide: AXP_FILE_ACTION_PROVIDER,
7730
+ useValue: DocumentFileUploaderActionsByTypeProvider,
7731
+ multi: true,
7732
+ },
7485
7733
  // Module Manifest Provider
7486
7734
  {
7487
7735
  provide: AXP_MODULE_MANIFEST_PROVIDER,