Mostra paginaRevisioni precedentiPuntano quiEsporta in PDFTorna su Questa pagina è in sola lettura. Puoi visualizzare il sorgente, ma non puoi modificarlo. Contatta l'amministratore se pensi che ci sia un errore. ====== ''openUploadUI()'' ====== ===== Panoramica ===== ''openUploadUI()'' apre un'interfaccia grafica per il caricamento di file che invia i file selezionati al server, dove vengono elaborati da uno script **Groovy** specificato dallo sviluppatore. L'UI offre: * **Area drag & drop** con selezione via file picker dell'OS * **Lista dei file selezionati** con possibilità di rimozione singola * **Validazione client-side** su estensione e dimensione totale * **Upload automatico** oppure su click di un pulsante "Carica" * Apertura in **FloatingPane** (default) oppure in un **contenitore esistente** * **Notifiche** via dialog, MessageToaster o callback personalizzato UI minimale, nessun parametro: {{ :custom:openuploadui_1.jpg }} UI che mostra la selezione corrente {{ :custom:openuploadui_2.jpg }} Notifica standard si avvenuto caricamento in base al messaggio ritornato dal groovy {{ :custom:openuploadui_3.jpg }} ---- ===== Firma della funzione ===== <code javascript> openUploadUI(scriptName, options) </code> ^ Parametro ^ Tipo ^ Obbligatorio ^ Descrizione ^ | ''scriptName'' | ''String'' | Sì | Nome in notazione dot del Groovy script da eseguire sul server (es. ''"mypackage.ImportScript"''). L'estensione ''.groovy'' viene aggiunta automaticamente. | | ''options'' | ''Object'' | No | Oggetto di configurazione opzionale. Tutti i sotto-parametri sono opzionali. | ---- ===== Parametri di ''options'' ===== ==== Testo e istruzioni ==== ^ Parametro ^ Tipo ^ Default ^ Descrizione ^ | ''instructions'' | ''String'' | ''null'' | Testo di istruzioni mostrato all'utente in un'area stilizzata (''gwInstructionsArea'') sopra la drop zone. Accetta HTML. | {{ :custom:openuploadui_4.jpg }} ---- ==== Comportamento upload ==== ^ Parametro ^ Tipo ^ Default ^ Descrizione ^ | ''autoupload'' | ''Boolean'' | ''false'' | Se ''true'', l'upload parte automaticamente non appena i file vengono selezionati o trascinati. Il pulsante "Carica" non viene mostrato. | | ''multiple'' | ''Boolean'' | ''false'' | Se ''true'', permette la selezione e il caricamento di più file contemporaneamente. Se ''false'' e l'utente trascina più file, viene preso solo il primo. | | ''maxUploadSizeMB'' | ''Number'' | ''null'' | Dimensione massima totale in MB (somma di tutti i file selezionati). Se superata, il pulsante "Carica" viene disabilitato e viene mostrato un messaggio di errore. ''null'' = nessun limite. | | ''allowedExtensions'' | ''String[]'' | ''null'' | Array di estensioni consentite senza il punto iniziale (es. ''['pdf', 'xlsx']''). La restrizione viene propagata anche al file picker dell'OS tramite l'attributo ''accept''. ''null'' = tutti i tipi consentiti. | ---- ==== Posizionamento UI ==== ^ Parametro ^ Tipo ^ Default ^ Descrizione ^ | ''containerRef'' | ''String %%|%% Widget %%|%% Node'' | ''null'' | Se assente, l'UI si apre in un FloatingPane. Se presente, l'UI viene inserita all'interno del contenitore indicato. Valori accettati: widget id (String), DOM node id (String), oggetto widget Dojo (ha ''.domNode''), nodo DOM diretto. | | ''dimensions'' | ''Object'' | ''{w:600, h:600}'' | Dimensioni del FloatingPane in pixel. Ignorato se ''containerRef'' è presente. Formato: ''{w: 800, h: 500}''. | | ''modal'' | ''Boolean'' | ''false'' | Se ''true'', il FloatingPane apre in modalità modale. Ignorato se ''containerRef'' è presente. | | ''useCookie'' | ''Boolean'' | ''false'' | Se ''true'', la posizione e le dimensioni del FloatingPane vengono salvate in localStorage e ripristinate alla successiva apertura. Ignorato se ''containerRef'' è presente. | ---- Esempio di validazione fallita {{ :custom:openuploadui_5.jpg }} Esempio con utilizzo esteso dei parametri: {{ :custom:openuploadui_6.jpg }} ==== Notifiche e callback ==== ^ Parametro ^ Tipo ^ Default ^ Descrizione ^ | ''useToaster'' | ''Boolean'' | ''false'' | Se ''true'', la notifica di successo viene mostrata via ''messageToaster'' invece di ''showOkDialog()''. Gli errori usano sempre ''showErrorDialog()'' salvo ''errorCallback'' personalizzato. | | ''callback'' | ''Function'' | ''null'' | Funzione chiamata dopo una risposta di successo. Riceve come unico argomento il ''JsonServerResponse'' completo. Se assente, viene usata la notifica predefinita (dialog o toaster). | | ''errorCallback'' | ''Function'' | ''null'' | Funzione chiamata in caso di errore. Riceve ''{success: false, description: String}''. Se assente, viene chiamata ''showErrorDialog()''. | ---- ==== Parametri aggiuntivi per il Groovy ==== ^ Parametro ^ Tipo ^ Default ^ Descrizione ^ | ''parameters'' | ''Object'' | ''null'' | Mappa di parametri aggiuntivi da passare allo script Groovy nel binding. Vedi sezione [[#variabili disponibili nel groovy|Variabili disponibili nel Groovy]]. | ---- ===== Comportamento al successo ===== ^ Modalità apertura ^ Comportamento on success ^ | **FloatingPane** + ''showOkDialog'' (default) | Il FloatingPane si chiude quando l'utente clicca OK nel dialog. | | **FloatingPane** + ''useToaster: true'' | Il FloatingPane si chiude immediatamente; poi compare il toaster. | | **FloatingPane** + ''callback'' personalizzato | Il FloatingPane si chiude immediatamente dopo il callback. | | **containerRef** + ''showOkDialog'' (default) | L'UI si reimposta allo stato iniziale (vuota) quando l'utente clicca OK. | | **containerRef** + ''useToaster: true'' | L'UI si reimposta allo stato iniziale immediatamente; poi compare il toaster. | | **containerRef** + ''callback'' personalizzato | L'UI si reimposta allo stato iniziale immediatamente dopo il callback. | ---- ===== Variabili disponibili nel Groovy ===== Lo script Groovy riceve nel ''Binding'' le seguenti variabili: ==== Specifiche dell'upload ==== ^ Variabile ^ Tipo ^ Descrizione ^ | ''file'' | ''MultipartFile'' | Il primo file caricato (o ''null'' se nessun file). | | ''files'' | ''MultipartFile[]'' | Array di tutti i file caricati. | | ''parametersMap'' | ''HashMap<String, Object>'' | Mappa dei parametri aggiuntivi passati tramite ''options.parameters''. | | ''parameters'' | ''HashMap<String, Object>'' | Alias di ''parametersMap''. | | ''<chiave>'' | ''Object'' | Ogni entry di ''parameters'' è anche disponibile come variabile individuale nel binding (es. ''parameters: {userId: 42}'' → la variabile ''userId'' vale ''42''). | | ''jsonServerResponse'' | ''JsonServerResponse'' | L'oggetto di risposta. Disponibile per lettura; ''success'' e ''description'' vengono applicati dal Map restituito dallo script. | ==== Variabili di sessione (aggiunte da ''addSessionObjectInfo'') ==== ^ Variabile ^ Tipo ^ Descrizione ^ | ''gw_activeUser'' | ''String'' | Utente attivo. | | ''gw_activeGroup'' | ''String'' | Gruppo attivo. | | ''gw_activeScopes'' | ''List'' | Scope attivi (definizioni). | | ''gw_activeScopeList'' | ''List<Map>'' | Scope attivi (forma leggibile). | | ''gw_projectName'' | ''String'' | Nome del progetto. | | ''gw_solutionCode'' | ''String'' | Codice soluzione dalla configurazione. | ==== Servizi (aggiunti automaticamente da ''runScript'') ==== ^ Variabile ^ Tipo ^ Descrizione ^ | ''services'' | ''HashMap'' | Tutti i servizi registrati nel ''GwRegistry''. | | ''log'' | ''Logger'' | Logger Log4j per il Groovy script. | | ''applicationContext'' | ''ApplicationContext'' | Contesto Spring. | | ''environment'' | ''Environment'' | Variabili d'ambiente Spring. | | //ogni servizio// | vario | Ogni servizio è anche disponibile come variabile individuale. | ==== Contratto della risposta ==== Il controller legge **esclusivamente il Map restituito** dallo script per determinare successo e messaggio: ^ Chiave ^ Tipo ^ Descrizione ^ | ''success'' | ''Boolean'' | ''true'' → operazione riuscita; ''false'' → fallimento | | ''message'' | ''String'' | Messaggio mostrato all'utente (nel dialog o nel toaster) | Lo script può aggiungere dati extra alla risposta tramite ''jsonServerResponse.addResponseObject("key", value)'', accessibili nel callback JS come ''resp.responseHashMap.key''. <note warning> **Attenzione:** se lo script restituisce ''null'' o un tipo non-Map, il controller lancia una ''RuntimeException'' e l'upload viene considerato fallito. </note> ---- ===== Template standard di Groovy script ===== <code groovy> // Script in: mypackage/ImportScript.groovy → scriptName: "mypackage.ImportScript" def result = [:] def success = false def message = null try { // Variabili disponibili nel binding: // file {MultipartFile} – primo file caricato (o null se assente) // files {MultipartFile[]} – tutti i file caricati // parametersMap {Map} – parametri extra inviati dal JS (alias: parameters) // <chiave> {Object} – ogni entry di parametersMap è disponibile individualmente // gw_activeUser, gw_activeGroup, gw_projectName, gw_projectPath, ... // services, log, applicationContext, environment, ... def parameterValue = parametersMap.parameter_name // ... elaborazione ... byte[] bytes = file.getBytes() // ... success = true message = 'file(s) correctly processed' } catch (Exception e) { log.error(e.getMessage(), e) success = false message = 'ko' } finally { result.success = success result.message = message } return result </code> ---- ===== Esempi di utilizzo ===== ==== 1 — Uso minimo (solo ''scriptName'') ==== <code javascript> // Apre il FloatingPane default 600×600. // L'utente seleziona un file e clicca "Carica". // Al successo compare showOkDialog con il messaggio del server. openUploadUI('mypackage.SimpleImport'); </code> ---- ==== 2 — ''instructions'' ==== <code javascript> // Mostra un'area di testo con istruzioni sopra la drop zone. openUploadUI('mypackage.SimpleImport', { instructions: 'Carica il file di importazione in formato Excel.<br>' + 'Il file non deve superare le 1000 righe.' }); </code> ---- ==== 3 — ''autoupload'' ==== <code javascript> // L'upload parte automaticamente non appena il file viene selezionato/trascinato. // Il pulsante "Carica" non compare. openUploadUI('mypackage.QuickProcess', { autoupload: true }); </code> ---- ==== 4 — ''multiple'' ==== <code javascript> // Permette la selezione e il caricamento di più file in una sola chiamata. // Il server riceve files[] con tutti i file. openUploadUI('mypackage.BatchImport', { multiple: true }); </code> ---- ==== 5 — ''maxUploadSizeMB'' ==== <code javascript> // Impedisce l'upload se la dimensione totale supera 5 MB. // Il bottone "Carica" viene disabilitato e compare un messaggio localizzato. openUploadUI('mypackage.DocumentUpload', { maxUploadSizeMB: 5 }); </code> ---- ==== 6 — ''allowedExtensions'' ==== <code javascript> // Accetta solo file PDF e Word. La restrizione vale sia per il drag&drop // sia per il file picker dell'OS (attributo accept). openUploadUI('mypackage.DocumentImport', { allowedExtensions: ['pdf', 'doc', 'docx'] }); </code> ---- ==== 7 — ''containerRef'' (widget id) ==== <code javascript> // L'UI viene inserita nel widget Dojo con id "myContentPane" // invece di aprire un FloatingPane. openUploadUI('mypackage.EmbeddedUpload', { containerRef: 'myContentPane' }); </code> ---- ==== 8 — ''containerRef'' (nodo DOM) ==== <code javascript> // L'UI viene inserita direttamente in un nodo DOM. var container = document.getElementById('uploadArea'); openUploadUI('mypackage.EmbeddedUpload', { containerRef: container }); </code> ---- ==== 9 — ''dimensions'' ==== <code javascript> // Il FloatingPane viene aperto con dimensioni personalizzate. openUploadUI('mypackage.LargeImport', { dimensions: { w: 800, h: 500 } }); </code> ---- ==== 10 — ''modal'' ==== <code javascript> // Il FloatingPane viene aperto in modalità modale: // l'utente non può interagire con il resto della pagina finché non chiude il pannello. openUploadUI('mypackage.CriticalImport', { modal: true }); </code> ---- ==== 11 — ''useCookie'' ==== <code javascript> // La posizione e le dimensioni del FloatingPane vengono salvate in localStorage. // Alla successiva apertura il pannello si riposiziona dove era stato lasciato. openUploadUI('mypackage.RecurringImport', { useCookie: true }); </code> ---- ==== 12 — ''useToaster'' ==== <code javascript> // Il messaggio di successo appare nel MessageToaster invece che in un dialog. // Utile quando si vuole una notifica non bloccante. openUploadUI('mypackage.SilentImport', { useToaster: true }); </code> ---- ==== 13 — ''parameters'' ==== <code javascript> // Passa parametri aggiuntivi al Groovy script. // Nel binding saranno disponibili come: // parametersMap["targetTable"], parameters["targetTable"] → "anagrafica" // targetTable → "anagrafica" (variabile individuale) // overwrite → true (variabile individuale) openUploadUI('mypackage.TableImport', { parameters: { targetTable: 'anagrafica', overwrite: true, userId: 42 } }); </code> Lato Groovy: <code groovy> log.info("Importazione in tabella: ${targetTable}, overwrite: ${overwrite}") // oppure log.info("Tabella: ${parametersMap.targetTable}") </code> ---- ==== 14 — ''callback'' ==== <code javascript> // Callback personalizzato al successo: riceve il JsonServerResponse completo. // Il FloatingPane si chiude ugualmente dopo il callback. openUploadUI('mypackage.ImportWithResult', { callback: function(resp) { var importedCount = resp.responseHashMap.result.importedCount; showOkDialog({ message: 'Importati ' + importedCount + ' record.' }); // aggiorna la datagrid aperta refreshDatagrid(); } }); </code> ---- ==== 15 — ''errorCallback'' ==== <code javascript> // Gestione personalizzata degli errori di rete o di script. openUploadUI('mypackage.CriticalImport', { errorCallback: function(err) { console.error('Upload fallito:', err.description); showErrorDialog('Errore durante il caricamento: ' + err.description); // logica di ripristino personalizzata rollbackLocalState(); } }); </code> ---- ==== 16 — Tutti i parametri combinati ==== <code javascript> openUploadUI('reportistica.ImportazioneExcel', { // Istruzioni per l'utente instructions: 'Carica il file Excel con i dati da importare.<br>' + 'Sono ammessi solo file .xlsx e .xls, max 10 MB.', // Selezione e validazione multiple: false, allowedExtensions: ['xlsx', 'xls'], maxUploadSizeMB: 10, autoupload: false, // Posizionamento: FloatingPane personalizzato containerRef: null, dimensions: { w: 750, h: 550 }, modal: true, useCookie: false, // Parametri extra per il Groovy parameters: { projectCode: gwProjectName, targetEntity: 'Dipendenti', dryRun: false }, // Notifica di successo non bloccante useToaster: false, // Callback di successo personalizzato callback: function(resp) { var result = resp.responseHashMap.result; showOkDialog({ message: 'Importazione completata.<br>' + 'Record inseriti: <b>' + result.insertedRows + '</b><br>' + 'Errori: <b>' + result.errorRows + '</b>' }); topic.publish('myApp/dataChanged', { entity: 'Dipendenti' }); }, // Gestione errori personalizzata errorCallback: function(err) { showErrorDialog('Importazione fallita: ' + err.description); } }); </code> ---- ===== Casi d'uso ===== ==== CU-01 — Importazione dati da file Excel ==== L'utente carica un file ''.xlsx'' contenente anagrafiche. Il Groovy legge il file riga per riga (con Apache POI, già disponibile tramite i servizi nel binding) e inserisce o aggiorna i record nel database. <code javascript> openUploadUI('import.AnagraficaImport', { instructions: 'File Excel con le colonne: Cognome, Nome, CF, Email.', allowedExtensions: ['xlsx', 'xls'], maxUploadSizeMB: 2, useToaster: true, callback: function(resp) { datagridAnagrafica.refresh(); } }); </code> ---- ==== CU-02 — Caricamento allegato a una scheda (embedded) ==== L'UI viene visualizzata all'interno di un pannello del dettaglio scheda già aperto, senza aprire un nuovo FloatingPane. Al successo, la lista allegati viene aggiornata. <code javascript> openUploadUI('allegati.CaricaAllegato', { containerRef: dijit.registry.byId('attachmentsTab'), allowedExtensions: ['pdf', 'jpg', 'png', 'docx'], maxUploadSizeMB: 20, parameters: { itemId: currentItemId, className: 'Pratica' }, callback: function(resp) { refreshAttachmentsList(); } }); </code> ---- ==== CU-03 — Upload multiplo di immagini con auto-invio ==== L'utente trascina più immagini contemporaneamente. L'upload parte senza necessità di cliccare alcun pulsante. Il Groovy ridimensiona e archivia le immagini. <code javascript> openUploadUI('media.ImageUploader', { instructions: 'Trascina le immagini o usa il pulsante Sfoglia. Formati ammessi: JPG, PNG, WEBP.', multiple: true, autoupload: true, allowedExtensions: ['jpg', 'jpeg', 'png', 'webp'], maxUploadSizeMB: 50, dimensions: { w: 700, h: 480 }, useToaster: true }); </code> ---- ==== CU-04 — Importazione configurazione da JSON ==== Un amministratore carica un file di configurazione. Il Groovy valida il JSON, applica la configurazione e restituisce un report dettagliato. L'UI si apre in modalità modale per impedire operazioni concorrenti. <code javascript> openUploadUI('admin.ConfigImport', { instructions: "Carica il file di configurazione esportato. Attenzione: l'operazione è irreversibile.", allowedExtensions: ['json'], maxUploadSizeMB: 1, modal: true, parameters: { environment: 'production', validateOnly: false }, callback: function(resp) { var report = resp.responseHashMap.result; showOkDialog({ message: 'Configurazione applicata.<br>Voci aggiornate: ' + report.updatedKeys }); }, errorCallback: function(err) { showErrorDialog('Configurazione non valida: ' + err.description); } }); </code> ---- ==== CU-05 — Sostituzione di un documento con versionamento ==== L'utente rimpiazza un documento esistente. Il Groovy archivia la versione precedente e salva quella nuova, restituendo metadati sulla versione creata. <code javascript> openUploadUI('documenti.SostituisciDocumento', { allowedExtensions: ['pdf'], maxUploadSizeMB: 30, useCookie: true, parameters: { documentId: currentDocumentId, keepHistory: true }, callback: function(resp) { var meta = resp.responseHashMap.result; messageToaster.setContent( 'Versione ' + meta.versionNumber + ' salvata.', 'message' ); messageToaster.show(); reloadDocumentDetail(currentDocumentId); } }); </code> ---- ==== CU-06 — Importazione batch da più file CSV ==== L'ufficio carica più file CSV (uno per categoria) in un'unica operazione. Il Groovy li elabora in sequenza e produce un report consolidato. <code javascript> openUploadUI('batch.MultiCSVImport', { instructions: 'Seleziona tutti i file CSV da importare in una volta sola.', multiple: true, allowedExtensions: ['csv'], maxUploadSizeMB: 25, dimensions: { w: 700, h: 500 }, parameters: { separator: ';', encoding: 'UTF-8' }, callback: function(resp) { var r = resp.responseHashMap.result; showOkDialog({ message: 'Elaborati ' + r.filesProcessed + ' file.<br>' + 'Righe importate: ' + r.totalRows + '<br>' + 'Errori: ' + r.totalErrors }); refreshReportDashboard(); } }); </code> ---- ==== CU-07 — Validazione preventiva (dry run) ==== Prima di un import definitivo, l'utente può eseguire una validazione "dry run" che non scrive sul database ma restituisce l'elenco degli errori trovati. <code javascript> function apriValidazione(isDryRun) { openUploadUI('import.ValidatedImport', { instructions: isDryRun ? 'Modalità VERIFICA: nessun dato verrà scritto nel database.' : 'Modalità IMPORTAZIONE: i dati verranno scritti nel database.', allowedExtensions: ['xlsx'], maxUploadSizeMB: 5, parameters: { dryRun: isDryRun, targetTable: 'Contratti' }, callback: function(resp) { var r = resp.responseHashMap.result; if (isDryRun) { showOkDialog({ message: 'Verifica completata.<br>' + 'Righe valide: ' + r.validRows + '<br>' + 'Righe con errori: ' + r.errorRows }); } else { messageToaster.setContent('Importati ' + r.insertedRows + ' record.', 'message'); messageToaster.show(); } } }); } // Uso: apriValidazione(true); // solo verifica apriValidazione(false); // importazione reale </code> ---- ===== Note tecniche ===== ==== Formato della risposta del server ==== Il controller restituisce sempre un ''JsonServerResponse'' in formato JSON: <code json> { "success": true, "description": "Operazione eseguita con successo", "errorDetailList": [], "responseHashMap": { "result": { ... } } } </code> Il campo ''result'' contiene il valore di ritorno dello script Groovy. ==== Dimensione massima upload lato server ==== La dimensione massima lato server è configurata in ''dispatcher-servlet.xml'' tramite ''CommonsMultipartResolver''. Il parametro ''maxUploadSizeMB'' in ''openUploadUI'' è una **validazione client-side** che si aggiunge (non sostituisce) al limite server. ==== Compatibilità con ''containerRef'' ==== Quando si usa ''containerRef'', l'UI occupa il 100% dello spazio disponibile nel contenitore (''width: 100%; height: 100%''). Il contenitore deve avere una dimensione definita (es. ''height'' esplicita o essere un ''ContentPane'' Dojo con layout calcolato). ==== Variabili globali richieste ==== La funzione dipende dalle seguenti variabili globali definite dalla pagina JSP: ^ Variabile ^ Provenienza ^ Uso ^ | ''gwContextPath'' | ''i18nGlobalMessages.jsp'' | Base URL delle richieste | | ''gwProjectName'' | ''project.jsp'' | Path variable del controller | | ''selectLabel'' | ''i18nGlobalMessages.jsp'' | Titolo del FloatingPane | | ''uploadLabel'' | ''i18nGlobalMessages.jsp'' | Label del pulsante "Carica" | | ''gwUploadDropFilesLabel'' | ''i18nGlobalMessages.jsp'' | Testo della drop zone | | ''gwUploadBrowseLabel'' | ''i18nGlobalMessages.jsp'' | Label pulsante "Sfoglia" | | ''gwUploadSizeLimitExceededLabel'' | ''i18nGlobalMessages.jsp'' | Messaggio di errore dimensione | | ''gwUploadInvalidExtensionLabel'' | ''i18nGlobalMessages.jsp'' | Messaggio di errore estensione | | ''generalErrorMessage'' | ''i18nGlobalMessages.jsp'' | Messaggio di errore generico | ==== Note ==== **Issue di riferimento:** #1824\\ **File sorgente JS:** ''gw-commons-web/.../debug/js/gwCommons.js''\\ **Controller:** ''gw-webclient/.../controller/commons/UploadToGroovyController.java''\\ **Endpoint:** ''POST /{projectName}/uploadToGroovy'' custom/openuploadui.txt Ultima modifica: 2026/06/19 23:36da giorgio.scali