Index: /trunk/grails-app/controllers/AssetDetailedController.groovy =================================================================== --- /trunk/grails-app/controllers/AssetDetailedController.groovy (revision 289) +++ /trunk/grails-app/controllers/AssetDetailedController.groovy (revision 290) @@ -14,9 +14,12 @@ static allowedMethods = [delete:'POST', save:'POST', update:'POST', saveCopy:'POST'] + def overview = { + } + + @Secured(['ROLE_Manager','ROLE_AppAdmin']) def importAssetTree = { } - def overview = { - } - + + @Secured(['ROLE_Manager','ROLE_AppAdmin']) def importAssetTreeSave = { def result = csvService.importAssetTree(request) Index: /trunk/grails-app/i18n/messages.properties =================================================================== --- /trunk/grails-app/i18n/messages.properties (revision 289) +++ /trunk/grails-app/i18n/messages.properties (revision 290) @@ -1,4 +1,4 @@ asset.tree.import.success=Asset tree imported. -asset.tree.import.failure=Could not create asset tree from supplied file. +asset.tree.import.failure=Could not create asset tree from supplied file, failed on line {0}. asset.tree.import.file.over.max.size=Supplied file is greater than max size of {0} Mbytes. asset.tree.import.file.not.supplied=No file supplied. @@ -22,6 +22,5 @@ person.personGroups.help=Groups may be assigned to tasks and \ may also provide a record of persons qualified or trained in a specific area. \ -Use Ctrl or Shift to select multiple groups. \ -Groups provide no application authorisations. + Groups provide no application authorisations. person.loginName=Login Name person.loginName.help=This is the id or name that the person will use to login to the application. Index: /trunk/grails-app/services/AssetSubItemService.groovy =================================================================== --- /trunk/grails-app/services/AssetSubItemService.groovy (revision 289) +++ /trunk/grails-app/services/AssetSubItemService.groovy (revision 290) @@ -22,5 +22,5 @@ def save(params) { - Asset.withTransaction { status -> + AssetSubItem.withTransaction { status -> def result = [:] Index: /trunk/grails-app/services/CreateDataService.groovy =================================================================== --- /trunk/grails-app/services/CreateDataService.groovy (revision 289) +++ /trunk/grails-app/services/CreateDataService.groovy (revision 290) @@ -156,4 +156,5 @@ saveAndTest(authInstance) + // #3 authInstance = new Authority(description:"Application User, all application users need this base role to allow login.", authority:"ROLE_AppUser") Index: /trunk/grails-app/services/CsvService.groovy =================================================================== --- /trunk/grails-app/services/CsvService.groovy (revision 289) +++ /trunk/grails-app/services/CsvService.groovy (revision 290) @@ -16,58 +16,85 @@ */ def importAssetTree(request) { - def result = [:] - - def megaByteMultiplier = 1000 * 1000 - def fileMaxSize = 10 * megaByteMultiplier //Mb - - def multiPartFile = request.getFile('file') - - InputStreamReader sr = new InputStreamReader(multiPartFile.inputStream) - CSVReader reader = new CSVReader(sr) - - def fail = { Map m -> - //status.setRollbackOnly() - reader.close() - result.error = [ code: m.code, args: m.args ] - return result - } - - if(!multiPartFile || multiPartFile.isEmpty()) - return fail(code: "asset.tree.import.file.not.supplied") - - if (multiPartFile.getSize() > fileMaxSize) - return fail(code: "asset.tree.import.file.over.max.size", args: [fileMaxSize/megaByteMultiplier]) - - def line = reader.readNext() - def lineNumber = 1 - - def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"] - - if(line != header) - return fail(code: "asset.tree.import.no.header") - - log.info "Import checks passed, start processing asset file." - - // Prepare the first body line. - line = reader.readNext() - lineNumber ++ - - while(line) { - def lineSize = line.size() -// log.info lineNumber+ "(" + lineSize + ")" + " : " + line - - if(line[0]) { - if( !Site.findByName(line[0]) ) - new Site(name: line[0]).save() - } - + Asset.withTransaction { status -> + def result = [:] + + def megaByteMultiplier = 1000 * 1000 + def fileMaxSize = 10 * megaByteMultiplier //Mb + + def multiPartFile = request.getFile('file') + + InputStreamReader sr = new InputStreamReader(multiPartFile.inputStream) + CSVReader reader = new CSVReader(sr) + + def fail = { Map m -> + status.setRollbackOnly() + reader.close() + result.error = [ code: m.code, args: m.args ] + return result + } + + if(!multiPartFile || multiPartFile.isEmpty()) + return fail(code: "asset.tree.import.file.not.supplied") + + if (multiPartFile.getSize() > fileMaxSize) + return fail(code: "asset.tree.import.file.over.max.size", args: [fileMaxSize/megaByteMultiplier]) + + def line = reader.readNext() + def lineNumber = 1 + + def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"] + + if(line != header) + return fail(code: "asset.tree.import.no.header") + + log.info "Import checks passed, start processing asset file." + + // Prepare the first body line. line = reader.readNext() lineNumber ++ - } //while(line) - - // Success. - reader.close() - return result - + + def siteInstance + def sectionInstance + def assetInstance + + while(line) { + def lineSize = line.size() + // log.info lineNumber+ "(" + lineSize + ")" + " : " + line + + if(line[0]) { + if( !Site.findByName(line[0]) ) + siteInstance = new Site(name: line[0]) + if(!siteInstance.save()) + return fail(code: "asset.tree.import.failure", args: [lineNumber]) + } + else continue + + if(line[1]) { + if( !Section.findByName(line[1]) ) + sectionInstance = new Section(name: line[1], + site: siteInstance) + if(!sectionInstance.save()) + return fail(code: "asset.tree.import.failure", args: [lineNumber]) + } + else continue + + if(line[2]) { + if( !Asset.findByName(line[2]) ) + assetInstance = new Asset(name: line[2], + section: sectionInstance) + if(!sectionInstance.save()) + return fail(code: "asset.tree.import.failure", args: [lineNumber]) + } + else continue + + line = reader.readNext() + lineNumber ++ + } //while(line) + + // Success. + reader.close() + return result + + } //end withTransaction } // end importAssetTree() Index: /trunk/grails-app/taglib/CustomTagLib.groovy =================================================================== --- /trunk/grails-app/taglib/CustomTagLib.groovy (revision 289) +++ /trunk/grails-app/taglib/CustomTagLib.groovy (revision 290) @@ -36,4 +36,8 @@ def isChecked, ht, wd, style, html + def displayFields = attrs.displayFields + + def displayValue = " " + // sets the style to override height and/or width if either of them // is specified, else the default from the CSS is taken @@ -54,4 +58,15 @@ from.each { obj -> + displayValue = " " + + if( displayFields?.contains("id") ) { + displayValue += obj.id + " - " + } + + if(displayFields?.contains("name")) { + displayValue += obj.name + } + else displayValue += obj + // if we wanted to select the checkbox using a click anywhere on the label (also hover effect) // but grails does not recognize index suffix in the name as an array: @@ -61,5 +76,5 @@ isChecked = (value?.contains(obj."${attrs.optionKey}"))? true: false - out << "