@topconsultnpm/sdkui-react 6.21.0-dev2.51 → 6.21.0-dev2.53

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.
@@ -144,7 +144,7 @@ const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationView
144
144
  const pdfFiles = [];
145
145
  const rfo = new RetrieveFileOptions();
146
146
  rfo.retrieveReason = DcmtOpers.None;
147
- rfo.generalRetrieveFormat = GeneralRetrieveFormats.Original;
147
+ rfo.generalRetrieveFormat = GeneralRetrieveFormats.OriginalUnsigned;
148
148
  rfo.cvtFormat = FileFormats.PDF;
149
149
  rfo.invoiceRetrieveFormat = SDKUI_Globals.userSettings?.searchSettings.invoiceRetrieveFormat;
150
150
  rfo.orderRetrieveFormat = SDKUI_Globals.userSettings?.searchSettings.orderRetrieveFormat;
@@ -16,6 +16,23 @@ export declare const isDirectoryPickerSupported: () => boolean;
16
16
  * Verifica se un'estensione (con o senza punto iniziale, case-insensitive) è "pdf".
17
17
  */
18
18
  export declare const isPdfExt: (ext: string | null | undefined) => boolean;
19
+ /**
20
+ * Estrae l'estensione completa da un nome file, gestendo estensioni composite
21
+ * come .PDF.P7M, .DOCX.p7m, .xml.p7m, etc.
22
+ *
23
+ * Esempi:
24
+ * - "documento.pdf" -> ".pdf"
25
+ * - "DCMT_123.PDF.P7M" -> ".PDF.P7M"
26
+ * - "fattura.xml.p7m" -> ".xml.p7m"
27
+ * - "file.tar.gz" -> ".gz" (tar.gz non è gestito come firma)
28
+ * - "file" -> ""
29
+ */
30
+ export declare const getFullFileExtension: (fileName: string) => string;
31
+ /**
32
+ * Estrae il nome base del file (senza estensione completa).
33
+ * Gestisce estensioni composite come .PDF.P7M
34
+ */
35
+ export declare const getFileBaseName: (fileName: string) => string;
19
36
  /** Sanitizza il nome file per Windows (rimuove caratteri illegali e limita la lunghezza) */
20
37
  export declare const sanitizeFileName: (fileName: string, fallbackName: string, maxLength?: number) => string;
21
38
  /** Verifica se un file esiste nella cartella (FileSystemDirectoryHandle) */
@@ -58,6 +58,53 @@ export const isPdfExt = (ext) => {
58
58
  const normalized = ext.trim().toLowerCase().replace(/^\./, '');
59
59
  return normalized === 'pdf';
60
60
  };
61
+ /**
62
+ * Estensioni di firma/marca temporale note che possono avvolgere altre estensioni.
63
+ * Es: file.pdf.p7m, file.xml.p7m, file.docx.p7m
64
+ */
65
+ const SIGNATURE_EXTENSIONS = new Set(['p7m', 'p7s', 'm7m', 'tsd', 'tsr']);
66
+ /**
67
+ * Estrae l'estensione completa da un nome file, gestendo estensioni composite
68
+ * come .PDF.P7M, .DOCX.p7m, .xml.p7m, etc.
69
+ *
70
+ * Esempi:
71
+ * - "documento.pdf" -> ".pdf"
72
+ * - "DCMT_123.PDF.P7M" -> ".PDF.P7M"
73
+ * - "fattura.xml.p7m" -> ".xml.p7m"
74
+ * - "file.tar.gz" -> ".gz" (tar.gz non è gestito come firma)
75
+ * - "file" -> ""
76
+ */
77
+ export const getFullFileExtension = (fileName) => {
78
+ if (!fileName)
79
+ return '';
80
+ const lastDotIndex = fileName.lastIndexOf('.');
81
+ if (lastDotIndex <= 0)
82
+ return '';
83
+ const lastExtension = fileName.substring(lastDotIndex + 1).toLowerCase();
84
+ // Se l'ultima estensione è una firma/marca, cerca anche l'estensione precedente
85
+ if (SIGNATURE_EXTENSIONS.has(lastExtension)) {
86
+ const nameWithoutSignature = fileName.substring(0, lastDotIndex);
87
+ const secondLastDotIndex = nameWithoutSignature.lastIndexOf('.');
88
+ if (secondLastDotIndex > 0) {
89
+ // Restituisce entrambe le estensioni: .PDF.P7M
90
+ return fileName.substring(secondLastDotIndex);
91
+ }
92
+ }
93
+ // Estensione singola
94
+ return fileName.substring(lastDotIndex);
95
+ };
96
+ /**
97
+ * Estrae il nome base del file (senza estensione completa).
98
+ * Gestisce estensioni composite come .PDF.P7M
99
+ */
100
+ export const getFileBaseName = (fileName) => {
101
+ if (!fileName)
102
+ return '';
103
+ const fullExtension = getFullFileExtension(fileName);
104
+ if (!fullExtension)
105
+ return fileName;
106
+ return fileName.substring(0, fileName.length - fullExtension.length);
107
+ };
61
108
  // ============================================================
62
109
  // Sanitizzazione e gestione nomi file
63
110
  // ============================================================
@@ -72,10 +119,9 @@ export const sanitizeFileName = (fileName, fallbackName, maxLength = 255) => {
72
119
  .trim();
73
120
  sanitized = sanitized.replace(/[. ]+$/, '');
74
121
  if (sanitized.length > maxLength) {
75
- const lastDotIndex = sanitized.lastIndexOf('.');
76
- if (lastDotIndex > 0) {
77
- const extension = sanitized.substring(lastDotIndex);
78
- const baseName = sanitized.substring(0, lastDotIndex);
122
+ const extension = getFullFileExtension(sanitized);
123
+ if (extension) {
124
+ const baseName = getFileBaseName(sanitized);
79
125
  const maxBaseLength = maxLength - extension.length;
80
126
  sanitized = baseName.substring(0, maxBaseLength) + extension;
81
127
  }
@@ -101,14 +147,9 @@ export const fileExists = async (dirHandle, fileName) => {
101
147
  };
102
148
  /** Genera un nome file univoco aggiungendo un suffisso numerico se già esistente */
103
149
  export const generateUniqueFileName = async (dirHandle, originalName) => {
104
- const splitName = (name) => {
105
- const lastDotIndex = name.lastIndexOf('.');
106
- const baseName = lastDotIndex > 0 ? name.substring(0, lastDotIndex) : name;
107
- const extension = lastDotIndex > 0 ? name.substring(lastDotIndex) : '';
108
- return { baseName, extension };
109
- };
110
150
  try {
111
- const { baseName, extension } = splitName(originalName);
151
+ const baseName = getFileBaseName(originalName);
152
+ const extension = getFullFileExtension(originalName);
112
153
  const MAX_ATTEMPTS = 1000;
113
154
  for (let counter = 1; counter <= MAX_ATTEMPTS; counter++) {
114
155
  const candidateName = counter === 1 ? originalName : `${baseName} (${counter - 1})${extension}`;
@@ -122,7 +163,8 @@ export const generateUniqueFileName = async (dirHandle, originalName) => {
122
163
  return `${baseName}_${Date.now()}${extension}`;
123
164
  }
124
165
  catch {
125
- const { baseName, extension } = splitName(originalName);
166
+ const baseName = getFileBaseName(originalName);
167
+ const extension = getFullFileExtension(originalName);
126
168
  return `${baseName}_${Date.now()}${extension}`;
127
169
  }
128
170
  };
@@ -174,8 +216,8 @@ export const getFilteredMetadata = async (tid, did, separatorChar) => {
174
216
  /** Genera il nome file di destinazione in base alle impostazioni di naming */
175
217
  export const generateTargetFileName = async (file, dcmtInfo, options) => {
176
218
  const { fileNamingMode, separatorChar } = options;
177
- const lastDotIndex = file.name.lastIndexOf('.');
178
- const fileExtension = lastDotIndex > 0 ? file.name.substring(lastDotIndex) : '';
219
+ // Usa getFullFileExtension per gestire estensioni composite come .PDF.P7M, .DOCX.p7m
220
+ const fileExtension = getFullFileExtension(file.name);
179
221
  const fallbackName = dcmtInfo.DID + fileExtension;
180
222
  let targetFileName = file.name;
181
223
  switch (fileNamingMode) {
@@ -597,6 +597,7 @@ export declare class SDKUI_Localizator {
597
597
  static get Previous(): "Vorherige" | "Previous" | "Anterior" | "Précédent" | "Precedente";
598
598
  static get Priority(): string;
599
599
  static get PriorityLegend(): "Legende der Prioritäten" | "Priority Legend" | "Leyenda de prioridades" | "Légende des priorités" | "Lenda das prioridades" | "Legenda delle priorità";
600
+ static get Print(): "Drucken" | "Print" | "Imprimir" | "Imprimer" | "Stampa";
600
601
  static get ProceedAnyway(): "Dennoch fortfahren?" | "Proceed anyway?" | "¿Proceder de todos modos?" | "Procéder quand même ?" | "Prosseguir mesmo assim?" | "Procedere comunque?";
601
602
  static get ProcessedItems(): "Durchdachte Elemente" | "Processed items" | "Elementos elaborados" | "Items traités" | "Itens processados" | "Elementi elaborati";
602
603
  static get Properties(): "Eigenschaften" | "Properties" | "Propiedades" | "Propriétés" | "Propriedades" | "Proprietà";
@@ -5942,6 +5942,16 @@ export class SDKUI_Localizator {
5942
5942
  default: return "Legenda delle priorità";
5943
5943
  }
5944
5944
  }
5945
+ static get Print() {
5946
+ switch (this._cultureID) {
5947
+ case CultureIDs.De_DE: return "Drucken";
5948
+ case CultureIDs.En_US: return "Print";
5949
+ case CultureIDs.Es_ES: return "Imprimir";
5950
+ case CultureIDs.Fr_FR: return "Imprimer";
5951
+ case CultureIDs.Pt_PT: return "Imprimir";
5952
+ default: return "Stampa";
5953
+ }
5954
+ }
5945
5955
  static get ProceedAnyway() {
5946
5956
  switch (this._cultureID) {
5947
5957
  case CultureIDs.De_DE: return "Dennoch fortfahren?";
@@ -23,6 +23,7 @@ import { useInputAttachmentsDialog, useInputCvtFormatDialog } from "./useInputDi
23
23
  import { useRelatedDocuments } from "./useRelatedDocuments";
24
24
  import { convertSearchResultDescriptorToFileItems, dcmtsFileCachePreview, getDcmtCicoStatus, getMoreInfoTasksForDocument, IconActivity, IconArchiveDetail, IconArchiveDoc, IconArchiveMaster, IconBatchUpdate, IconCheck, IconCheckFile, IconCheckIn, IconCircleInfo, IconCloseCircle, IconConvertFilePdf, IconCopy, IconCustom, IconDelete, IconDetailDcmts, IconDotsVerticalCircleOutline, IconDownload, IconEdit, IconExportTo, IconFileDots, IconHide, IconInfo, IconMenuCAArchive, IconMoveToFolder, IconPair, IconPin, IconPlatform, IconPreview, IconRelation, IconSearch, IconShare, IconSharedDcmt, IconShow, IconSignaturePencil, IconStar, IconSubstFile, IconUndo, IconUnpair, IconUserGroupOutline, isPdfEditorAvailable, SDKUI_Localizator, searchResultToMetadataValues, TMImageLibrary } from '../helper';
25
25
  import { isXMLFileExt } from "../helper/dcmtsHelper";
26
+ import { isPdfExt } from "../components/features/documents/copyAndMergeDcmtsShared";
26
27
  import TMCopyToFolderForm from "../components/features/documents/TMCopyToFolderForm";
27
28
  import TMMergeToPdfForm from "../components/features/documents/TMMergeToPdfForm";
28
29
  export const getSelectedDcmtsOrFocused = (selectedItems, focusedItem, fileFormat) => {
@@ -567,6 +568,41 @@ export const useDocumentOperations = (props) => {
567
568
  onClick: () => openEditPdfCallback(selectedDcmtInfos, refreshDocumentPreview),
568
569
  };
569
570
  };
571
+ const printMenuItem = () => {
572
+ const isPdf = isPdfExt(selectedDcmtInfos?.[0]?.FILEEXT);
573
+ return {
574
+ id: 'print',
575
+ icon: _jsx("i", { className: "dx-icon-print", style: { fontSize: '20px' } }),
576
+ name: SDKUI_Localizator.Print,
577
+ operationType: 'singleRow',
578
+ disabled: !isPdf || isDisabledForSingleRow(),
579
+ onClick: isPdf ? async () => {
580
+ const handlePrint = async (file, _dcmtInfo) => {
581
+ const fileURL = window.URL.createObjectURL(file);
582
+ // Usa un iframe nascosto per stampare senza aprire un nuovo tab
583
+ const iframe = document.createElement('iframe');
584
+ iframe.style.position = 'fixed';
585
+ iframe.style.right = '0';
586
+ iframe.style.bottom = '0';
587
+ iframe.style.width = '0';
588
+ iframe.style.height = '0';
589
+ iframe.style.border = 'none';
590
+ iframe.src = fileURL;
591
+ document.body.appendChild(iframe);
592
+ iframe.onload = () => {
593
+ iframe.contentWindow?.focus();
594
+ iframe.contentWindow?.print();
595
+ // Rimuovi l'iframe dopo un breve delay per permettere la stampa
596
+ setTimeout(() => {
597
+ document.body.removeChild(iframe);
598
+ window.URL.revokeObjectURL(fileURL);
599
+ }, 1000);
600
+ };
601
+ };
602
+ await downloadDcmtsAsync(selectedDcmtInfos, DownloadTypes.Dcmt, "download", handlePrint, undefined, true);
603
+ } : undefined,
604
+ };
605
+ };
570
606
  const handleSignApprove = () => {
571
607
  if (!onOpenS4TViewerRequest) {
572
608
  ShowAlert({
@@ -1082,6 +1118,7 @@ export const useDocumentOperations = (props) => {
1082
1118
  batchUpdateMenuItem(),
1083
1119
  passToArchive(),
1084
1120
  ...(selectedDcmtInfos.length > 0 && isPdfEditorAvailable(dtd, selectedDcmtInfos[0]?.FILEEXT) && onOpenPdfEditorRequest ? [pdfEditorMenuItem(onOpenPdfEditorRequest)] : []),
1121
+ printMenuItem()
1085
1122
  ]
1086
1123
  },
1087
1124
  signatureMenuItem(),
@@ -1125,6 +1162,7 @@ export const useDocumentOperations = (props) => {
1125
1162
  commentFromWgMenuItem(false),
1126
1163
  relationsMenuItem(true),
1127
1164
  removeFromWgMenuItem(SDKUI_Localizator.RemoveFromWorkgroup),
1165
+ printMenuItem()
1128
1166
  ].sort((a, b) => a.name.localeCompare(b.name));
1129
1167
  return [
1130
1168
  ...sortedItems,
@@ -1148,6 +1186,7 @@ export const useDocumentOperations = (props) => {
1148
1186
  downloadFileMenuItem(),
1149
1187
  downloadXMLAttachmentsMenuItem(),
1150
1188
  ...(selectedDcmtInfos.length > 0 && isPdfEditorAvailable(dtd, selectedDcmtInfos[0]?.FILEEXT) && onOpenPdfEditorRequest ? [pdfEditorMenuItem(onOpenPdfEditorRequest)] : []),
1189
+ printMenuItem()
1151
1190
  ]
1152
1191
  },
1153
1192
  signatureMenuItem(),
@@ -1173,7 +1212,8 @@ export const useDocumentOperations = (props) => {
1173
1212
  ...(SDK_Globals.tmSession?.SessionDescr?.appModuleID === AppModules.SURFER ? [createContextualTaskMenuItem()] : []),
1174
1213
  downloadFileMenuItem(),
1175
1214
  downloadXMLAttachmentsMenuItem(),
1176
- ...(selectedDcmtInfos.length > 0 && isPdfEditorAvailable(dtd, selectedDcmtInfos[0]?.FILEEXT) && onOpenPdfEditorRequest ? [pdfEditorMenuItem(onOpenPdfEditorRequest)] : [])
1215
+ ...(selectedDcmtInfos.length > 0 && isPdfEditorAvailable(dtd, selectedDcmtInfos[0]?.FILEEXT) && onOpenPdfEditorRequest ? [pdfEditorMenuItem(onOpenPdfEditorRequest)] : []),
1216
+ printMenuItem()
1177
1217
  ]
1178
1218
  },
1179
1219
  signatureMenuItem(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.21.0-dev2.51",
3
+ "version": "6.21.0-dev2.53",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",