source: trunk/grails-app/controllers/dbnp/importer/ImporterController.groovy @ 1656

Last change on this file since 1656 was 1656, checked in by t.w.abma@…, 11 years ago
  • added 'hack' to fix the file upload via Ajax by stripping HTML from the 'importfile' parameter
  • Property svn:keywords set to Rev Author Date
File size: 23.2 KB
Line 
1package dbnp.importer
2
3import dbnp.studycapturing.*
4import org.dbnp.gdt.*
5import grails.converters.JSON
6import org.codehaus.groovy.grails.plugins.web.taglib.ValidationTagLib
7import grails.plugins.springsecurity.Secured
8
9/**
10 * Wizard Controller
11 *
12 * @author Jeroen Wesbeek
13 * @since 20101206
14 *
15 * Revision information:
16 * $Rev: 1656 $
17 * $Author: t.w.abma@umcutrecht.nl $
18 * $Date: 2011-03-22 08:56:05 +0000 (di, 22 mrt 2011) $
19 */
20@Secured(['IS_AUTHENTICATED_REMEMBERED'])
21class ImporterController {
22        // the pluginManager is used to check if the Grom
23        // plugin is available so we can 'Grom' development
24        // notifications to the unified notifications daemon
25        // (see http://www.grails.org/plugin/grom)
26        def pluginManager
27        def authenticationService
28        def fileService
29        def importerService
30        def validationTagLib = new ValidationTagLib()
31        def gdtService
32
33        /**
34         * index method, redirect to the webflow
35         * @void
36         */
37        def index = {
38                // Grom a development message
39                if (pluginManager.getGrailsPlugin('grom')) "redirecting into the webflow".grom()
40
41                /**
42                 * Do you believe it in your head?
43                 * I can go with the flow
44                 * Don't say it doesn't matter (with the flow) matter anymore
45                 * I can go with the flow (I can go)
46                 * Do you believe it in your head?
47                 */
48                redirect(action: 'pages')
49        }
50
51        /**
52         * WebFlow definition
53         * @void
54         */
55        def pagesFlow = {
56                // start the flow
57                onStart {
58                        // Grom a development message
59                        if (pluginManager.getGrailsPlugin('grom')) "entering the WebFlow".grom()
60
61                        // define variables in the flow scope which is availabe
62                        // throughout the complete webflow also have a look at
63                        // the Flow Scopes section on http://www.grails.org/WebFlow
64                        //
65                        // The following flow scope variables are used to generate
66                        // wizard tabs. Also see common/_tabs.gsp for more information
67                        flow.page = 0
68                        flow.pages = [
69                                [title: 'Import file'],
70                                [title: 'Assign properties'],
71                                [title: 'Check imported data'],
72                                //[title: 'Imported'],
73                                [title: 'Done']
74                        ]
75                        flow.cancel = true;
76                        flow.quickSave = true;
77
78                        success()
79                }
80
81                // render the main wizard page which immediately
82                // triggers the 'next' action (hence, the main
83                // page dynamically renders the study template
84                // and makes the flow jump to the study logic)
85                mainPage {
86                        render(view: "/importer/index")
87                        onRender {
88                                // Grom a development message
89                                if (pluginManager.getGrailsPlugin('grom')) "rendering the main Ajaxflow page (index.gsp)".grom()
90
91                                // let the view know we're in page 1
92                                flow.page = 1
93                                success()
94                        }
95                        on("next").to "pageOne"
96                }
97
98                // File import and entitie template selection page
99                pageOne {
100                        render(view: "_page_one")
101                        onRender {
102                                log.info ".entering import wizard"
103                                // Grom a development message
104                                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial: pages/_page_one.gsp".grom()
105
106                                flow.page = 1
107                                flow.studies = Study.findAllWhere(owner: authenticationService.getLoggedInUser())
108                                flow.importer_fuzzymatching = "false"
109
110                                success()
111                        }
112
113                        on("refresh") {
114
115                                if (params.entity) {
116                                        flash.importer_datatemplates = Template.findAllByEntity(gdtService.getInstanceByEntity(params.entity.decodeURL()))
117                                }
118
119                                flash.importer_params = params
120
121                                // If the file already exists an "existing*" string is added, but we don't
122                                // want that after a refresh of the first step in the import wizard, so remove
123                                // that string
124                                flash.importer_params.importfile = params.importfile.replace('existing*', '')
125                flash.importer_params.importfile = new XmlSlurper().parseText(flash.importer_params.importfile[flash.importer_params.importfile.indexOf('<pre')..-1]).toString()
126
127                                success()
128                        }.to "pageOne"
129
130                        on("next") {
131                                flash.wizardErrors = [:]
132                                flash.importer_params = params
133                                flash.importer_params.importfile = params.importfile.replace('existing*', '')
134                flash.importer_params.importfile = new XmlSlurper().parseText(flash.importer_params.importfile[flash.importer_params.importfile.indexOf('<pre')..-1]).toString()
135
136                                if (params.entity) {
137                                        flash.importer_datatemplates = Template.findAllByEntity(gdtService.getInstanceByEntity(params.entity.decodeURL()))
138                                        def importer_entity_type = gdtService.decryptEntity(params.entity.decodeURL()).toString().split(/\./)
139                                        flow.importer_entity_type = importer_entity_type[importer_entity_type.size()-1]
140                                }
141
142                                // Study selected?
143                                flow.importer_study = (params.study) ? Study.get(params.study.id.toInteger()) : null
144
145                                // Trying to import data into an existing study?
146                                if (flow.importer_study)
147                                        if (flow.importer_study.canWrite(authenticationService.getLoggedInUser()))
148                                                fileImportPage(flow, flash, params) ? success() : error()
149                                        else {
150                                                log.error ".importer wizard wrong permissions"
151                                                this.appendErrorMap(['error': "You don't have the right permissions"], flash.wizardErrors)
152                                                error()
153                                        }
154                                else {
155                                        fileImportPage(flow, flash, params) ? success() : error()
156                                }
157
158                                // put your bussiness logic (if applicable) in here
159                        }.to "pageTwo"
160                }
161
162                // Property to column assignment page
163                pageTwo {
164                        render(view: "_page_two")
165                        onRender {
166                                log.info ".import wizard properties page"
167
168                                def template = Template.get(flow.importer_template_id)
169
170                                // Grom a development message
171                                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial: pages/_page_two.gsp".grom()
172
173                                flow.importer_importmappings = ImportMapping.findAllByTemplate(template)
174
175                                flow.page = 2
176                                success()
177                        }
178                        on("refresh") {
179                                def template = Template.get(flow.importer_template_id)
180                                flow.importer_importmappings = ImportMapping.findAllByTemplate(template)
181
182                                // a name was given to the current property mapping, try to store it
183                                if (params.mappingname) {
184                                        flash.importer_columnproperty = params.columnproperty
185                                        propertiesSaveImportMappingPage(flow, flash, params)
186                                } else // trying to load an existing import mapping
187                                if (params.importmapping_id) {
188                                        propertiesLoadImportMappingPage(flow, flash, params)
189                                }
190
191                                if (params.fuzzymatching == "true")
192                                        flow.importer_fuzzymatching = "true" else
193                                        flow.importer_fuzzymatching = "false"
194
195                                success()
196                        }.to "pageTwo"
197
198                        on("next") {
199                                flow.importer_fuzzymatching = "false"
200                                if (propertiesPage(flow, flash, params)) {
201                                        success()
202                                } else {
203                                        log.error ".import wizard, properties are set wrong"
204                                        error()
205                                }
206                        }.to "pageThree"
207                        on("previous").to "pageOne"
208                }
209
210                // Mapping page
211                pageThree {
212                        render(view: "_page_three")
213                        onRender {
214                                log.info ".import wizard mapping page"
215                                // Grom a development message
216                                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_page_three.gsp".grom()
217
218                                flow.page = 3
219                                success()
220                        }
221                        on("refresh") {
222                                success()
223                        }.to "pageThree"
224                        on("next") {
225                                if (mappingsPage(flow, flash, params)) {
226                                        flow.page = 4
227                                        success()
228                                } else {
229                                        log.error ".import wizard mapping error, could not validate all entities"
230                                        error()
231                                }
232                        }.to "save"
233                        on("previous").to "pageTwo"
234                }
235
236                // Imported data overview page
237                pageFour {
238                        render(view: "_page_four")
239                        onRender {
240                                // Grom a development message
241                                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_page_four.gsp".grom()
242
243                                flow.page = 4
244                                success()
245                        }
246                        on("next") {
247                                if (importedPage(flow, params)) {
248                                        flow.page = 4
249                                        success()
250                                } else {
251                                        log.error ".import wizard imported error, something went wrong showing the imported entities"
252                                        error()
253                                }
254                        }.to "save"
255                        on("previous").to "pageThree"
256                }
257
258                // Save the imported data
259                save {
260                        action {
261                                // here you can validate and save the
262                                // instances you have created in the
263                                // ajax flow.
264                                // Grom a development message
265                                if (pluginManager.getGrailsPlugin('grom')) ".persisting instances to the database...".grom()
266
267                                // Always delete the uploaded file in the save step to be sure it doesn't reside there anymore
268                                fileService.delete(flow.importer_importedfile)
269
270                                // Save all entities
271                                if (saveEntities(flow, params)) {
272                                        success()
273                                } else {
274                                        log.error ".import wizard, could not save entities:\n"
275                                        flow.page = 4
276                                        error()
277                                }
278                        }
279                        on("error").to "error"
280                        on(Exception).to "error"
281                        on("success").to "finalPage"
282                }
283
284                // render errors
285                error {
286                        render(view: "_error")
287                        onRender {
288                                // Grom a development message
289                                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_error.gsp".grom()
290
291                                // set page to 4 so that the navigation
292                                // works (it is disabled on the final page)
293                                flow.page = 4
294                        }
295                        on("next").to "save"
296                        on("previous").to "pageFour"
297                }
298
299                // last wizard page
300                finalPage {
301                        render(view: "_final_page")
302                        onRender {
303                                // Grom a development message
304                                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_final_page.gsp".grom()
305                                success()
306                        }
307                        onEnd {
308                                // clean flow scope
309                                flow.clear()
310                        }
311                }
312        }
313
314        def propertiesManager = {
315                render(view: "common/_propertiesManager")
316        }
317
318        /**
319         * Return templates which belong to a certain entity type
320         *
321         * @param entity entity name string (Sample, Subject, Study et cetera)
322         * @return JSON object containing the found templates
323         */
324        def ajaxGetTemplatesByEntity = {
325                // fetch all templates for a specific entity
326                def templates = Template.findAllByEntity(gdtService.getInstanceByEntity(params.entity.decodeURL()))
327
328                // set output header to json
329                response.contentType = 'application/json'
330
331                // render as JSON
332                render templates as JSON
333        }
334
335        /**
336         * Handle the file import page.
337         *
338         * @param Map LocalAttributeMap (the flow scope)
339         * @param Map GrailsParameterMap (the flow parameters = form data)
340         * @returns boolean true if correctly validated, otherwise false
341         */
342        boolean fileImportPage(flow, flash, params) {
343                def importedfile = fileService.get(params['importfile'])
344                flow.importer_importedfile = params['importfile']
345
346                if (importedfile.exists()) {
347                        try {
348                                session.importer_workbook = importerService.getWorkbook(new FileInputStream(importedfile))
349                        } catch (Exception e) {
350                                log.error ".importer wizard could not load file: " + e
351                                this.appendErrorMap(['error': "Wrong file (format), the importer requires an Excel file as input"], flash.wizardErrors)
352                                return false
353                        }
354                }
355
356                if (params.entity && params.template_id) {
357
358                        try {
359                                session.importer_workbook = importerService.getWorkbook(new FileInputStream(importedfile))
360                        } catch (Exception e) {
361                                log.error ".importer wizard could not load file: " + e
362                                this.appendErrorMap(['error': "Excel file required as input"], flash.wizardErrors)
363                                return false
364                        }
365
366                        def selectedentities = []
367
368                        def entityName = gdtService.decryptEntity(params.entity.decodeURL())
369                        def entityClass = gdtService.getInstanceByEntityName(entityName)
370
371                        // Initialize some session variables
372                        //flow.importer_workbook = wb // workbook object must be serialized for this to work
373
374                        flow.importer_template_id = params.template_id
375                        flow.importer_sheetindex = params.sheetindex.toInteger() - 1 // 0 == first sheet
376                        flow.importer_datamatrix_start = params.datamatrix_start.toInteger() - 1 // 0 == first row
377                        flow.importer_headerrow = params.headerrow.toInteger()
378                        flow.importer_entityclass = entityClass
379                        flow.importer_entity = gdtService.cachedEntities.find { it.entity == entityName }
380
381                        // Get the header from the Excel file using the arguments given in the first step of the wizard
382                        flow.importer_header = importerService.getHeader(session.importer_workbook,
383                                flow.importer_sheetindex,
384                                flow.importer_headerrow,
385                                flow.importer_datamatrix_start,
386                                entityClass)
387
388                        session.importer_datamatrix = importerService.getDatamatrix(
389                                session.importer_workbook, flow.importer_header,
390                                flow.importer_sheetindex,
391                                flow.importer_datamatrix_start,
392                                5)
393
394                        flow.importer_templates = Template.get(flow.importer_template_id)
395                        flow.importer_allfieldtypes = "true"
396
397                        return true
398                }
399
400
401                log.error ".importer wizard not all fields are filled in"
402                this.appendErrorMap(['error': "Not all fields are filled in, please fill in or select all fields"], flash.wizardErrors)
403                return false
404        }
405
406        /**
407         * Load an existing import mapping
408         *
409         * @param Map LocalAttributeMap (the flow scope)
410         * @param Map GrailsParameterMap (the flow parameters = form data)
411         * @returns boolean true if correctly validated, otherwise false
412         */
413        boolean propertiesLoadImportMappingPage(flow, flash, params) {
414                def im = ImportMapping.get(params.importmapping_id.toInteger())
415                im.refresh()
416
417                im.mappingcolumns.each { mappingcolumn ->
418                        //def mc = new MappingColumn()
419                        //mc.properties = mappingcolumn.properties
420
421                        flow.importer_header[mappingcolumn.index.toInteger()] = mappingcolumn
422                }
423        }
424
425        /**
426         * Save the properties as an import mapping.
427         *
428         * @param Map LocalAttributeMap (the flow scope)
429         * @param Map GrailsParameterMap (the flow parameters = form data)
430         * @returns boolean true if correctly validated, otherwise false
431         */
432        boolean propertiesSaveImportMappingPage(flow, flash, params) {
433                flash.wizardErrors = [:]
434                def isPreferredIdentifier = false
435
436                // Find actual Template object from the chosen template name
437                def template = Template.get(flow.importer_template_id)
438
439                // Create new ImportMapping instance and persist it
440                def im = new ImportMapping(name: params.mappingname, entity: flow.importer_entityclass, template: template).save()
441
442                params.columnproperty.index.each { columnindex, property ->
443                        // Create an actual class instance of the selected entity with the selected template
444                        // This should be inside the closure because in some cases in the advanced importer, the fields can have different target entities                     
445                        //def entityClass = gdtService.getInstanceByEntityName(flow.importer_header[columnindex.toInteger()].entity.getName())
446                        def entityObj = flow.importer_entityclass.newInstance(template: template)
447
448                        def dontimport = (property == "dontimport") ? true : false
449
450                        // Loop through all fields and find the preferred identifier
451                        entityObj.giveFields().each {
452                                isPreferredIdentifier = (it.preferredIdentifier && (it.name == property)) ? true : false
453                        }
454
455                        // Create new MappingColumn instance
456                        def mc = new MappingColumn(importmapping: im,
457                                name: flow.importer_header[columnindex.toInteger()].name,
458                                property: property,
459                                index: columnindex,
460                                entityclass: flow.importer_entityclass,
461                                templatefieldtype: entityObj.giveFieldType(property),
462                                dontimport: dontimport,
463                                identifier: isPreferredIdentifier)
464
465                        // Save mappingcolumn
466                        if (mc.validate()) {
467                                im.addToMappingcolumns(mc)
468                        }
469                        else {
470                                mc.errors.allErrors.each {
471                                        println it
472                                }
473                        }
474
475                        // Save importmapping
476                        if (im.validate()) {
477                                try {
478                                        im.save(flush: true)
479                                } catch (Exception e) {
480                                        //getNextException
481                                        log.error "importer wizard save importmapping error: " + e
482                                }
483                        }
484                        else {
485                                im.errors.allErrors.each {
486                                        println it
487                                }
488                        }
489
490                }
491        }
492
493        /**
494         * Handle the property mapping page.
495         *
496         * @param Map LocalAttributeMap (the flow scope)
497         * @param Map GrailsParameterMap (the flow parameters = form data)
498         * @returns boolean true if correctly validated, otherwise false
499         */
500        boolean propertiesPage(flow, flash, params) {
501                flash.wizardErrors = [:]
502
503                // Find actual Template object from the chosen template name
504                def template = Template.get(flow.importer_template_id)
505
506                params.columnproperty.index.each { columnindex, property ->
507                        // Create an actual class instance of the selected entity with the selected template
508                        // This should be inside the closure because in some cases in the advanced importer, the fields can have different target entities
509                        def entityClass = Class.forName(flow.importer_header[columnindex.toInteger()].entityclass.getName(), true, this.getClass().getClassLoader())
510                        def entityObj = entityClass.newInstance(template: template)
511
512                        // Store the selected property for this column into the column map for the ImporterService
513                        flow.importer_header[columnindex.toInteger()].property = property
514
515                        // Look up the template field type of the target TemplateField and store it also in the map
516                        flow.importer_header[columnindex.toInteger()].templatefieldtype = entityObj.giveFieldType(property)
517
518                        // Is a "Don't import" property assigned to the column?
519                        flow.importer_header[columnindex.toInteger()].dontimport = (property == "dontimport") ? true : false
520
521                        //if it's an identifier set the mapping column true or false
522                        entityObj.giveFields().each {
523                                (it.preferredIdentifier && (it.name == property)) ? flow.importer_header[columnindex.toInteger()].identifier = true : false
524                        }
525                }
526
527                // Import the workbook and store the table with entity records and store the failed cells
528                def (table, failedcells) = importerService.importData(flow.importer_template_id,
529                        session.importer_workbook,
530                        flow.importer_sheetindex,
531                        flow.importer_datamatrix_start,
532                        flow.importer_header)
533
534                flow.importer_importeddata = table
535
536                // loop through all entities to validate them and add them to wizardErrors flash when invalid
537                /*table.each { record ->
538                                        record.each { entity ->
539                                                if (!entity.validate()) {
540                                                this.appendErrors(entity, flash.wizardErrors, 'entity_' + entity.getIdentifier() + '_')
541                                                }
542                                        }
543                                }*/
544
545                flow.importer_failedcells = failedcells
546
547                return true
548        }
549
550        /**
551         * Handle the mapping page.
552         *
553         * @param Map LocalAttributeMap (the flow scope)
554         * @param Map GrailsParameterMap (the flow parameters = form data)
555         * @returns boolean true if correctly validated, otherwise false
556         */
557        boolean mappingsPage(flow, flash, params) {
558                flash.wizardErrors = [:]
559                flow.importer_invalidentities = 0
560
561                flow.importer_importeddata.each { table ->
562                        table.each { entity ->
563                                def invalidfields = 0
564
565                                // Set the fields for this entity by retrieving values from the params
566                                entity.giveFields().each { field ->
567
568                                        // field is a date field, try to set it with the value, if someone enters a non-date value it throws
569                                        // an error, this should be caught to prevent a complete breakdown
570                                        if (field.type == org.dbnp.gdt.TemplateFieldType.DATE) {
571                                                try {
572                                                        entity.setFieldValue(field.toString(), params["entity_" + entity.getIdentifier() + "_" + field.escapedName()])
573                                                } catch (Exception e) {
574                                                        log.error ".importer wizard could not set date field with value: " +
575                                                                params["entity_" + entity.getIdentifier() + "_" + field.escapedName()]
576                                                }
577                                        } else
578
579                                        // field of type ontology and value "#invalidterm"?
580                                        if (field.type == org.dbnp.gdt.TemplateFieldType.ONTOLOGYTERM &&
581                                                params["entity_" + entity.getIdentifier() + "_" + field.escapedName()] == "#invalidterm"
582                                        ) {
583                                                invalidfields++
584                                        } else
585                                        if (field.type == org.dbnp.gdt.TemplateFieldType.ONTOLOGYTERM &&
586                                                params["entity_" + entity.getIdentifier() + "_" + field.escapedName()] != "#invalidterm") {
587                                                if (entity) removeFailedCell(flow.importer_failedcells, entity, field)
588                                                entity.setFieldValue(field.toString(), params["entity_" + entity.getIdentifier() + "_" + field.escapedName()])
589                                        }
590                                        else
591
592                                        if (field.type == org.dbnp.gdt.TemplateFieldType.STRINGLIST &&
593                                                params["entity_" + entity.getIdentifier() + "_" + field.escapedName()] != "#invalidterm") {
594                                                if (entity) removeFailedCell(flow.importer_failedcells, entity, field)
595                                                entity.setFieldValue(field.toString(), params["entity_" + entity.getIdentifier() + "_" + field.escapedName()])
596                                        } else
597                                        if (field.type == org.dbnp.gdt.TemplateFieldType.STRINGLIST &&
598                                                params["entity_" + entity.getIdentifier() + "_" + field.escapedName()] == "#invalidterm"
599                                        ) {
600                                                invalidfields++
601                                        } else
602
603                                                entity.setFieldValue(field.toString(), params["entity_" + entity.getIdentifier() + "_" + field.escapedName()])
604                                }
605
606                                // Determine entity class and add a parent (defined as Study in first step of wizard)
607                                switch (entity.getClass()) {
608                                        case [Subject, Sample, Event]: entity.parent = flow.importer_study
609                                }
610
611                                // Try to validate the entity now all fields have been set
612                                if (!entity.validate() || invalidfields) {
613                                        flow.importer_invalidentities++
614
615                                        // add errors to map
616                                        this.appendErrors(entity, flash.wizardErrors, "entity_" + entity.getIdentifier() + "_")
617
618                                        entity.errors.getAllErrors().each() {
619                                                log.error ".import wizard imported validation error:" + it
620                                        }
621                                } else {
622                                        //removeFailedCell(flow.importer_failedcells, entity)
623                                } // end else if
624
625                        } // end of record
626                } // end of table
627
628                return (flow.importer_invalidentities == 0) ? true : false
629        } // end of method
630
631        /**
632         * @param failedcell failed ontology cells
633         * @param entity entity to remove from the failedcells list
634         */
635        def removeFailedCell(failedcells, entity, field) {
636                // Valid entity, remove it from failedcells
637                def entityidfield = "entity_" + entity.getIdentifier() + "_" + field.name.toLowerCase()
638
639                failedcells.each { record ->
640                        def tempimportcells = []
641
642
643                        record.importcells.each { cell ->
644                                // remove the cell from the failed cells session
645                                if (cell.entityidentifier != entityidfield) {
646                                        //record.removeFromImportcells(cell)
647                                        tempimportcells.add(cell)
648                                }
649                        }
650
651                        record.importcells = tempimportcells
652                        // } // end of importcells
653                } // end of failedcells
654        }
655
656        /**
657         * Handle the imported entities page.
658         *
659         * @param Map LocalAttributeMap (the flow scope)
660         * @param Map GrailsParameterMap (the flow parameters = form data)
661         * @returns boolean true if correctly validated, otherwise false
662         */
663        boolean importedPage(flow, params) {
664                return true
665        }
666
667        boolean saveEntities(flow, params) {
668                //def (validatedSuccesfully, updatedEntities, failedToPersist) =
669                try {
670                        importerService.saveDatamatrix(flow.importer_study, flow.importer_entity_type, flow.importer_importeddata, authenticationService, log)
671                } catch (Exception e) {
672                        log.error ".import wizard saveEntities error\n" + e.dump()
673                        return false
674                }
675
676                //flow.importer_validatedsuccesfully = validatedSuccesfully
677                //flow.importer_failedtopersist = failedToPersist
678                //flow.imported_updatedentities = updatedEntities
679                //flow.importer_totalrows = flow.importer_importeddata.size
680                //flow.importer_referer = ""
681
682                return true
683        }
684
685        /**
686         * append errors of a particular object to a map
687         * @param object
688         * @param map linkedHashMap
689         * @void
690         */
691        def appendErrors(object, map) {
692                this.appendErrorMap(getHumanReadableErrors(object), map)
693        }
694
695        def appendErrors(object, map, prepend) {
696                this.appendErrorMap(getHumanReadableErrors(object), map, prepend)
697        }
698
699        /**
700         * append errors of one map to another map
701         * @param map linkedHashMap
702         * @param map linkedHashMap
703         * @void
704         */
705        def appendErrorMap(map, mapToExtend) {
706                map.each() {key, value ->
707                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
708                }
709        }
710
711        def appendErrorMap(map, mapToExtend, prepend) {
712                map.each() {key, value ->
713                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
714                }
715        }
716
717        /**
718         * transform domain class validation errors into a human readable
719         * linked hash map
720         * @param object validated domain class
721         * @return object  linkedHashMap
722         */
723        def getHumanReadableErrors(object) {
724                def errors = [:]
725                object.errors.getAllErrors().each() { error ->
726                        // error.codes.each() { code -> println code }           
727
728                        // generally speaking g.message(...) should work,
729                        // however it fails in some steps of the wizard
730                        // (add event, add assay, etc) so g is not always
731                        // availably. Using our own instance of the
732                        // validationTagLib instead so it is always
733                        // available to us
734                        errors[error.getArguments()[0]] = validationTagLib.message(error: error)
735                }
736
737                return errors
738        }
739
740}
Note: See TracBrowser for help on using the repository browser.