diff --git a/grails-app/assets/stylesheets/digivol-custom.css b/grails-app/assets/stylesheets/digivol-custom.css index 30982ab07..9b7073d4c 100644 --- a/grails-app/assets/stylesheets/digivol-custom.css +++ b/grails-app/assets/stylesheets/digivol-custom.css @@ -496,3 +496,40 @@ a.fieldHelp { white-space: normal; } +/* Labels */ + +.label-base { + background-color: #777; +} + +.label-green { + background-color: #5cb85c; +} + +.label-red { + background-color: #d9534f; +} + +.label-yellow { + background-color: #f0ad4e; +} + +.label-blue { + background-color: #337ab7; +} + +.label-lightblue { + background-color: #3498db; +} + +.label-orange { + background-color: #e67e22; +} + +.label-purple { + background-color: #9b59b6; +} + +.label-darkgrey { + background-color: #4b5359; +} \ No newline at end of file diff --git a/grails-app/conf/application.yml b/grails-app/conf/application.yml index 37170566b..bb7cd0efd 100644 --- a/grails-app/conf/application.yml +++ b/grails-app/conf/application.yml @@ -1,4 +1,5 @@ --- + grails: profile: web codegen: diff --git a/grails-app/controllers/au/org/ala/volunteer/AdminController.groovy b/grails-app/controllers/au/org/ala/volunteer/AdminController.groovy index f521f302b..4581b0432 100644 --- a/grails-app/controllers/au/org/ala/volunteer/AdminController.groovy +++ b/grails-app/controllers/au/org/ala/volunteer/AdminController.groovy @@ -742,6 +742,11 @@ class AdminController { } } + /** + * @deprecated + * @see {@link ReportRequestService#projectSummaryReport()} + * @return + */ def projectSummaryReport() { if (!checkAdminAccess(true)) { render(view: '/notPermitted') diff --git a/grails-app/controllers/au/org/ala/volunteer/LabelController.groovy b/grails-app/controllers/au/org/ala/volunteer/LabelController.groovy index 05be2f6ba..1f476eaac 100644 --- a/grails-app/controllers/au/org/ala/volunteer/LabelController.groovy +++ b/grails-app/controllers/au/org/ala/volunteer/LabelController.groovy @@ -1,100 +1,305 @@ package au.org.ala.volunteer +import grails.converters.JSON import grails.gorm.transactions.Transactional import static org.springframework.http.HttpStatus.* +import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN +import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND class LabelController { - static allowedMethods = [save: "POST", update: "PUT", delete: "POST"] + static allowedMethods = [saveNewLabel: "POST", updateCategory: "POST", saveCategory: "POST", + saveLabel: "POST", deleteCategory: "POST"] def userService + def labelAdminService - def index(Integer max) { + /** + * Index page for Label/Tag admin + */ + def index() { if (userService.isAdmin()) { - params.max = Math.min(max ?: 25, 100) - respond Label.list(params), model: [labelInstanceCount: Label.count()] + def labelCategories = LabelCategory.list(sort: 'name', order: 'asc') + render(view: 'index', model: [labelCategories: labelCategories, labelInstanceCount: Label.count()]) + } else { + render(view: '/notPermitted') + } + } + + def editCategory() { + if (userService.isAdmin()) { + def categoryId = params.long('id') + def category = LabelCategory.get(categoryId) + if (!category) { + log.debug("No category found, redirecting back to index.") + flash.message = message(code: 'default.not.found.message', + args: [message(code: 'default.label.category.label', default: 'Category'), params.long('id')]) as String + redirect(action: "index") + } else { + log.debug("Edit category: ${category.name}") + final def loadedDefaultLabels = grailsApplication.config.getProperty("bvp.labels.ensureDefault", Boolean, false) + render(view: 'editCategory', model: [labelCategory: category, categoryList: LabelCategory.list(), loadedDefaultLabels: loadedDefaultLabels]) + } } else { render(view: '/notPermitted') } } @Transactional - def save(Label labelInstance) { + def updateCategory() { if (userService.isAdmin()) { + def categoryId = params.long('categoryId') + def category = LabelCategory.get(categoryId) + if (!category) { + flash.message = message(code: 'default.not.found.message', + args: [message(code: 'default.label.category.label', default: 'Category'), params.long('id')]) as String + redirect(action: "index") + } else { + if (!category.isDefault) { + def name = params.get('name')?.toString() + if (!name) { + flash.message = message(code: 'default.not.updated.message', + args: [message(code: 'default.label.category.label', default: 'Category'), category.name]) as String + redirect(action: "index") + return + } + category.name = name + } - if (labelInstance == null) { - notFound() - return + category.labelColour = params.get('labelColour') + category.updatedDate = new Date() + category.save(flush: true, failOnError: true) + + flash.message = message(code: 'default.updated.message', + args: [message(code: 'default.label.category.label', default: 'Category'), category.name]) as String + redirect(view: 'index') } + } else { + render(view: '/notPermitted') + } + } - if (labelInstance.hasErrors()) { - respond labelInstance.errors, view: 'index' - return + def createCategory() { + if (userService.isAdmin()) { + render(view: 'createCategory') + } else { + render(view: '/notPermitted') + } + } + + @Transactional + def saveCategory() { + if (userService.isAdmin()) { + LabelCategory newCategory = new LabelCategory() + def categoryName = params.get("name") as String + if (!categoryName) { + newCategory.errors.rejectValue("name", "label.category.name.notnull", + "A category name must be provided.") } + newCategory.name = categoryName + newCategory.isDefault = false + newCategory.updatedDate = new Date() + newCategory.createdBy = userService.currentUser.id - labelInstance.save(flush: true, failOnError: true) + def catCheck = LabelCategory.findByName(categoryName) + if (catCheck) { + // Name already exists, cannot create it. + newCategory.errors.rejectValue("name", "label.name.unique", + "Category name must be unique.") + } - request.withFormat { - form multipartForm { - flash.message = message(code: 'default.created.message', args: [message(code: 'label.label', default: 'Label'), labelInstance.value]) - redirect action: 'index' - } - '*' { respond labelInstance, [status: CREATED] } + if (newCategory.errors.hasErrors()) { + log.debug("LabelCategory Errors: ${newCategory.errors}") + render(view: 'createCategory', model: [labelCategory: newCategory]) + return + } else { + newCategory.save(flush: true, failOnError: true) + flash.message = message(code: 'default.created.message', + args: [message(code: 'default.label.category.label', default: 'Category'), newCategory.name]) as String + redirect(view: 'index') } } else { - render status: 403 + render(view: '/notPermitted') } } @Transactional - def update(Label labelInstance) { + def saveNewLabel() { + log.debug("Saving new label") if (userService.isAdmin()) { - - if (labelInstance == null) { - notFound() + LabelCategory labelCategory = LabelCategory.get(params.long('categoryId')) + if (!labelCategory) { + flash.message = message(code: 'default.not.found.message', + args: [message(code: 'default.label.category.label', default: 'Category'), params.long('categoryId')]) as String + render(view: 'index') return } - if (labelInstance.hasErrors()) { - respond labelInstance.errors, view: 'index' + String value = params.get('value') as String + if (!value) { + flash.message = message(code: 'label.name.notnull') as String + redirect(view: 'editCategory', params: [id: labelCategory.id]) return } - labelInstance.save(flush: true, failOnError: true) + Label label = new Label() + label.category = labelCategory + label.value = value + label.isDefault = false + label.updatedDate = new Date() + label.createdBy = userService.currentUser.id + label.save(flush: true, failOnError: true) - request.withFormat { - form multipartForm { - flash.message = message(code: 'default.updated.message', args: [message(code: 'Label.label', default: 'Label'), labelInstance.value]) - redirect action: 'index' - } - '*' { respond labelInstance, [status: OK] } + flash.message = message(code: 'default.created.message', + args: [message(code: 'default.label.label', default: 'Tag'), label.value]) as String + redirect(action: 'editCategory', params: [id: labelCategory.id]) + } else { + render(view: '/notPermitted') + } + } + + @Transactional + def saveLabel() { + log.debug("Saving label") + if (userService.isAdmin()) { + log.debug("id: ${params.long('id')}, value: ${params.get('labelName')}") + def label = Label.get(params.long('id')) + log.debug("Label: ${label}") + if (label == null) { + notFound() + return } + + label.value = params.get('labelName') + label.updatedDate = new Date() + label.save(flush: true, failOnError: true) + log.debug("label saved: ${label}") + render([message: "Successfully updated label '${label.value}'"] as JSON) } else { - render status: 403 + response.status = SC_FORBIDDEN + render([message: "Access forbidden."] as JSON) } } @Transactional - def delete(Label labelInstance) { + def deleteLabel() { log.debug("Deleting label id: ${params.id}") if (userService.isAdmin()) { - if (labelInstance == null) { + Label labelInstance = Label.get(params.long('id')) + if (!labelInstance) { notFound() return } + if (labelAdminService.isLabelInUse(labelInstance)) { + flash.message = message(code: 'label.delete.notallowed', args: [labelInstance.value]) as String + redirect(action: 'editCategory', params: [id: labelInstance.category.id]) + return + } + labelInstance.delete(flush: true, failOnError: true) - request.withFormat { - form multipartForm { - flash.message = message(code: 'default.deleted.message', args: [message(code: 'Label.label', default: 'Label'), labelInstance.value]) - redirect action: "index", method: "GET" - } - '*' { render status: NO_CONTENT } + flash.message = message(code: 'default.deleted.message', + args: [message(code: 'default.label.label', default: 'Tag'), labelInstance.value]) as String + redirect(action: 'editCategory', params: [id: labelInstance.category.id]) + + } else { + render(view: '/notPermitted') + } + } + + @Transactional + def deleteCategory() { + log.debug("Deleting category.") + if (userService.isAdmin()) { + LabelCategory labelCategory = LabelCategory.get(params.long('id')) + if (!labelCategory) { + flash.message = message(code: 'default.not.found.message', + args: [message(code: 'default.label.category.label', default: 'Category'), params.long('id')]) as String + redirect(action: "index") + return } + def name = labelCategory.name + // Delete category + if (labelCategory.isDefault) { + // Shouldn't allow deletion due to system default category + flash.message = message(code: 'label.category.delete.notallowed', args: [labelCategory.name]) as String + redirect(action: "index") + return + } + + try { + labelCategory.delete(flush: true, failOnError: true) + } catch (Exception ex) { + flash.message = message(code: 'label.category.delete.notallowed', args: [labelCategory.name]) as String + redirect(action: "index") + return + } + + flash.message = message(code: 'default.deleted.message', + args: [message(code: 'default.label.category.label', default: 'Category'), name]) as String + redirect(action: 'index') } else { - render status: 403 + render(view: '/notPermitted') + } + } + + @Transactional + def changeCategory() { + log.debug("Changing label category.") + if (userService.isAdmin()) { + Label label = Label.get(params.long('labelId')) + LabelCategory labelCategory = LabelCategory.get(params.long('newCategory')) + if (!label || !labelCategory) { + flash.message = "Unable to modify ${message(code: 'default.label.label', default: 'Tag')}, missing parameters: label or category." + redirect(action: "index") + return + } + + // Check if new category already has tag of this value + def labelCheck = labelCategory.labels.find { + it.value == label.value + } + + if (labelCheck) { + // One already exists. Return an error. + flash.message = "Unable to modify ${message(code: 'default.label.label', default: 'Tag')}, new category already has a label with the same name." + redirect(action: "editCategory", params: [id: label.category.id]) + return + } + + label.category = labelCategory + label.updatedDate = new Date() + label.save(flush: true, failOnError: true) + + flash.message = message(code: 'default.updated.message', + args: [message(code: 'default.label.label', default: 'Tag'), label.value]) as String + redirect(action: 'editCategory', params: [id: labelCategory.id]) + } else { + render(view: '/notPermitted') + } + } + + def labelUsage() { + if (userService.isAdmin()) { + def labelId = params.long('id') + + def label = Label.get(labelId) + if (!label) { + // return emtpy map + response.status = SC_NOT_FOUND + render([:] as JSON) + return + } + + def result = labelAdminService.getLabelUsage(label) + if (result.size() > 0) { + render(result as JSON) + } else { + response.status = SC_NOT_FOUND + render([:] as JSON) + } } } diff --git a/grails-app/controllers/au/org/ala/volunteer/LandingPageAdminController.groovy b/grails-app/controllers/au/org/ala/volunteer/LandingPageAdminController.groovy index c62c3f349..12c60c24e 100644 --- a/grails-app/controllers/au/org/ala/volunteer/LandingPageAdminController.groovy +++ b/grails-app/controllers/au/org/ala/volunteer/LandingPageAdminController.groovy @@ -41,15 +41,19 @@ class LandingPageAdminController { } def editSelections(LandingPage landingPageInstance) { - final labelCats = Label.withCriteria { projections { distinct 'category' } } + //final labelCats = Label.withCriteria { projections { distinct 'category' } } + final labelCats = LabelCategory.list(sort: 'name').collect { it.name } ['landingPageInstance': landingPageInstance, 'labels': Label.listOrderByCategory(), 'labelCats': labelCats] } def filterLabelCategory () { + log.info("Filtering by category: ${params['category']}") def list def category = params['category'] ?: null if (category != 'all') { - list = Label.findAllByCategory(category) + //list = Label.findAllByCategory(category) + LabelCategory labelCategory = LabelCategory.findByName(category as String) + list = Label.findAllByCategory(labelCategory) } else { list = Label.listOrderByValue() } @@ -58,19 +62,23 @@ class LandingPageAdminController { @Transactional def saveProjectLabels(LandingPage landingPageInstance) { + log.debug("landing page: ${landingPageInstance}") if (landingPageInstance.hasErrors()) { chain action: "edit", model: ['landingPage': landingPageInstance] } else { - def newLabel = Label.findById(params['tag']) - if (!landingPageInstance.label) { - landingPageInstance.label = new ArrayList