How can I create a map with all i18n-messages in Grails - grails

I need this to render a part of it in a controller like:
class MessageController {
def index = {
def messageMap = listAlli18nMessages() // the question
render (contentType: "text/xml") {
messageMap {key, message ->
..
}
}
}
}

Finally I found an answer - override the default Grails messageSource:
class ExtendedPluginAwareResourceBundleMessageSource extends PluginAwareResourceBundleMessageSource {
Map<String, String> listMessageCodes(Locale locale) {
Properties properties = getMergedProperties(locale).properties
Properties pluginProperties = getMergedPluginProperties(locale).properties
return properties.plus(pluginProperties)
}
}
In grails-app/conf/spring/resources.groovy:
beans = {
messageSource(ExtendedPluginAwareResourceBundleMessageSource) {
basenames = "WEB-INF/grails-app/i18n/messages"
}
}
Corresponding controller code:
class MessageController {
def messageSource
def index = {
def messageMap = messageSource.listMessageCodes(request.locale)
render (contentType: "text/xml") {
messageMap {key, message ->
..
}
}
}
}

The approach you are taking doesn't look to be possible based on the API docs for PluginAwareResourceBundleMessageSource. This will get you close to a solution
class MessageController {
def messageSource
def index = {
Locale locale = new Locale('en');
List codes = ['default.paginate.prev','default.paginate.next','default.boolean.true','default.boolean.false']
def messageMap = messagesForCodes(codes,locale)
render (contentType: "text/xml") {
messageMap {key, message ->
..
}
}
}
private def messagesForCodes(codes, locale){
Map messages = [:]
codes.each{code->
messages[code] = messageSource.getMessage(code,null,locale)
}
messages
}
}

Related

GroovyDynamicMethodsInterceptor

is there anyway to use GroovyDynamicMethodsInterceptor in grails 3.0.17.
if not , what can I use instead?
this is part of my code ClinicianService.groovy :
package org.imedcom.server.provider
import org.codehaus.groovy.grails.web.metaclass.GroovyDynamicMethodsInterceptor
import org.codehaus.groovy.grails.web.metaclass.BindDynamicMethod
import grails.web.databinding.DataBinder
import org.imedcom.server.core.command.ClinicianCommand
import org.imedcom.server.core.command.CreateClinicianCommand
import org.imedcom.server.core.exception.ClinicianException
import org.imedcom.server.model.*
import org.springframework.transaction.annotation.Transactional
#Transactional
class ClinicianService {
def springSecurityService
def passwordService
ClinicianService() {
GroovyDynamicMethodsInterceptor i = new GroovyDynamicMethodsInterceptor(this)
i.addDynamicMethodInvocation(new BindDynamicMethod())
}
Clinician createClinician(CreateClinicianCommand command) {
def clinicianInstance = new Clinician(command.properties)
clinicianInstance.user = new User(command.properties)
clinicianInstance.user.enabled = true
if (!clinicianInstance.user.validate()) {
clinicianInstance.user.errors.fieldErrors.each {
clinicianInstance.errors.rejectValue("user.${it.field}", it.code, it.arguments, it.defaultMessage)
}
}
if (clinicianInstance.user.save(flush: true) && clinicianInstance.save(flush: true)) {
updateRoleLinks(command.roleIds, clinicianInstance)
updatePatientGroupLinks(command.groupIds, clinicianInstance)
}
return clinicianInstance
}
def updatePatientGroupLinks(List<Long> groupIds, Clinician clinician) {
def oldPatientGroups = Clinician2PatientGroup.findAllByClinician(clinician)*.patientGroup
def newPatientGroups = PatientGroup.findAllByIdInList(groupIds)
newPatientGroups.each { patientGroup ->
Clinician2PatientGroup.link(clinician, patientGroup)
}
def obsoletePatientGroups = oldPatientGroups - newPatientGroups
obsoletePatientGroups.each {
Clinician2PatientGroup.unlink(clinician, it)
}
}
def updateRoleLinks(List<Long> roleIds, Clinician clinician) {
UserRole.removeAll(clinician.user, true)
Role.findAllByIdInList(roleIds).each { role ->
def userRole = new UserRole(user: clinician.user, role: role)
if (!userRole.save()) {
throw new ClinicianException("clinician.could.not.assign.clinician.rights", userRole.errors)
}
}
}
}
and this is my ClinicianController.grroy where I also use
``def clinicianService```
import grails.plugin.springsecurity.annotation.Secured
import org.imedcom.server.core.command.CreateClinicianCommand
import org.imedcom.server.model.Clinician
import org.imedcom.server.model.Clinician2PatientGroup
import org.imedcom.server.core.command.ClinicianCommand
import org.imedcom.server.core.model.types.PermissionName
import org.springframework.dao.DataIntegrityViolationException
#Secured(PermissionName.NONE)
class ClinicianController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def clinicianService
def passwordService
def springSecurityService
def authenticationManager
#Secured(PermissionName.CLINICIAN_CREATE)
def create() {
if (!params.cleartextPassword) {
params.cleartextPassword = passwordService.generateTempPassword()
}
def command = new CreateClinicianCommand()
bindData(command, params, ["action", "controller"])
[cmd: command]
}
#Secured(PermissionName.CLINICIAN_CREATE)
def save(CreateClinicianCommand command) {
if (!command.validate()) {
render(view: "create", model: [cmd: command])
return;
}
def clinicianInstance = clinicianService.createClinician(command)
if (!clinicianInstance.hasErrors()) {
flash.message = message(code: 'default.created.message', args: [message(code: 'clinician.label'), clinicianInstance.firstName])
redirect(action: "show", id: clinicianInstance.id)
} else {
render(view: "create", model: [cmd: command])
}
}
}
and I am also using the clinicianService in other controllers too.
this all was in grails 2.4.4.
If what you want to do is invoke data binding from a Grails Service there are a few ways to do that. One is to implement DataBinder.
import grails.web.databinding.DataBinder
class SomeService implements DataBinder {
def serviceMethod(someMap) {
def obj = new SomeClass()
bindData obj, params
obj
}
}
Another option is to inject the data binder into your service:
import org.grails.databinding.SimpleMapDataBindingSource
class SomeService {
def grailsWebDataBinder
TestObject getNewTestObject(Map someMap) {
def obj = new SomeClass()
grailsWebDataBinder.bind obj, someMap as SimpleMapDataBindingSource
obj
}
}

HTTP Status 404 - Grails

I was trying to get into "http://localhost:8080/Twillio/smsService/index" from Grails view.
and I get errors like below.
HTTP Status 404 - "/WEB-INF/grails-app/views/smsService/index.gsp" not found.
My codes used for SmsServiceController.groovy is below.
package twillio
class SmsServiceController {
def index () {}
def smsService
def twilioHttpEndpointBean
def read = { withFormat { html {} } }
def create = { SendSmsCommand cmd ->
def validMessage = cmd.validate();
log.debug "Incoming message is ${validMessage ? 'valid' : 'invalid'}"
log.debug "Format is ${request.format}"
withFormat {
json {
if (validMessage) {
def smsResponse
try {
smsResponse = smsService.send(cmd.destination, cmd.message)
render(contentType: "application/json") {
result(success: true)
}
} catch (Exception e) {
render(contentType: "application/json", status: 500) {
result(success: false, message: e.message)
}
}
} else {
render(contentType: "application/json", status: 500) { result(success: false) }
}
}
}
}
}
class SendSmsCommand {
String destination
String message
static constraints = {
message(size: 1..140)
}
}
You have to place an index.gsp at grails-app/views/smsService/index.gsp

duplicate entries error from the database

I have something like the following domain-structure (it's a legacy database!):
class Contract implements Serializable {
int type
...
hasMany = [attachments: Attachment]
static mapping = {
attachments cascade: "all-delete-orphan"
}
}
class Attachment implements Serializable {
static belongsTo = [contract: Contract]
int attNo
String name
static mapping = {
id generator: 'assigned', composite: ['contract', 'attNo']
}
}
The contract has - depending on it's type - a few default attachments which should be deleted and recreated if the type changes.
For this I created a service which is called just before saving the edited contract. (The service doesn't have the #Transactional annotation, to have it running in the same transaction than the save/update of the contract.)
class AttachmentService {
def removeAttRecords(int contractId) {
Contract contract = Contract.get(contractId)
def records = contract.getAttachments()
for (int i = 0; i < records.size(); i++) {
Attachment record = records[i]
contract.removeFromAttachments(record)
}
}
def insertAttRecord(int contractId, String name) {
def contract = Contract.get(contractId)
def records = contract.attachments
int maxNo = 0
for (attachment record: records) {
if (record.attNo > maxNo)
maxNo = record.attNo
}
Attachment att = new Attachment()
att.contract = contract
att.attNo = maxNo +1
att.name = name
att.validate()
att.save(insert: true, flush: true, failOnError: true)
}
def createDefaultRecords(int newType, int contractId) {
if (newType == 1) {
removeAttRecords(contractId)
} else if (newType == 2) {
removeAttRecords(contractId)
insertAttRecord(contractId, "Attachment XYZ")
insertAttRecord(contractId, "Attachment ABC")
} else if (newType == 3) {
removeAttRecords(contractId)
insertAttRecord(contractId, "Attachment DEF")
}
}
}
My problem is now, that I get a duplicate entries error from the database in the insertAttRecord->att.save call, if I call the createDefaultRecords method in my service. Because of the deletion and the recreation.
I currently have no idea, how I could work around it.
You're using id generator: 'assigned' for the Attachment class. That means you have to set its id (the primary key) yourself. It looks like since you're not assigning it the new entries will all have the same id, hence the duplicate entries failure.
I worked around my problem with the following code:
class AttachmentService {
def removeAttRecords(int contractId) {
Attachment.executeUpdate("delete from Attachment att where att.contract.id = ?", [contractId])
}
def insertAttRecord(int contractId, int nextMaxNo, String name) {
Attachment att = new Attachment()
att.contract = Contract.get(contractId)
att.attNo = nextMaxNo
att.name = name
att.validate()
att.save(insert: true, flush: true, failOnError: true)
}
def createDefaultRecords(int newType, int contractId) {
def result = Attachment.executeQuery("select max(att.attNo) from Attachment att where att.contract.id = ?", contractId)
if (result[0] == null) result[0] = 0;
int currMaxNo = result[0]
if (newType == 1) {
removeAttRecords(contractId)
} else if (newType == 2) {
removeAttRecords(contractId)
insertAttRecord(contractId, currMaxNo +1, "Attachment XYZ")
insertAttRecord(contractId, currMaxNo +2, "Attachment ABC")
} else if (newType == 3) {
removeAttRecords(contractId)
insertAttRecord(contractId, currMaxNo +1, "Attachment DEF")
}
}
}
After discussing with the db-designer, it would be ok to handle that in this way.
(but I'd still like to know, why it wasn't working, even with a session-flush... :-)

Call namedQuery inside a criteria in controller

Is possible to call namedQuery on grails inside a controller? I know that I can call a namedQuery inside another namedQuery, but i dont want to do that. Any ideas? Thanks
User.groovy
static namedQueries = {
filterUsers{
eq("age", 21)
}
}
MyController.groovy
def r = User.createCriteria().list {
eq("id", 1)
filterUsers() //not possible
}
or..
MyController.groovy
//not possible too
//Cannot invoke method createCriteria() on null object
def r = User.filterUsers().createCriteria().list {
eq("id", 1)
}
Here's an example:
Domain:
class User {
int age
String userName
static namedQueries = {
filterUsers {
eq("age", 21)
}
}
static constraints = {
}
}
Controller:
class TestController {
def index = {
def users = User.filterUsers {
and {
like 'userName', 'Derek%'
}
}
render users as JSON
}
}
Also, you can find more about this here: Reference Documentation

idiom for save and update methods in grails

Are there in any idioms in grails which help us with saving domain objects ?
For example
i may want to do something like
if(candidate.hasErrors || !candidate.save)
{
candidate.errors.each {
log it
}
However i do not want to spread the logic across all the places i do domainObject.save.
I also do not want seperate class like say repo to which I pass this domainObject and put in this logic
Thanks
Sudarshan
Here's a service method that I've used to validate and save, but log resolved validation messages on failure. It's helpful to use this instead of just println error or log.warn error since the toString() for error objects is very verbose and you just want to see what would be displayed on the GSP:
class MyService {
def messageSource
def saveOrUpdate(bean, flush = false) {
return validate(bean) ? bean.save(flush: flush) : null
}
boolean validate(bean) {
bean.validate()
if (bean.hasErrors()) {
if (log.isEnabledFor(Level.WARN)) {
def message = new StringBuilder(
"problem ${bean.id ? 'updating' : 'creating'} ${bean.getClass().simpleName}: $bean")
def locale = Locale.getDefault()
for (fieldErrors in bean.errors) {
for (error in fieldErrors.allErrors) {
message.append("\n\t")
message.append(messageSource.getMessage(error, locale))
}
}
log.warn message
}
bean.discard()
return false
}
return true
}
And here's an example in a controller:
class MyController {
def myService
def actionName = {
def thing = new Thing(params)
if (myService.saveOrUpdate(thing)) {
redirect action: 'show', id: thing.id
}
else {
render view: 'create', model: [thing: thing]
}
}
}
Edit: It's also possible to add these methods to the MetaClass, e.g. in BootStrap.groovy:
class BootStrap {
def grailsApplication
def messageSource
def init = { servletContext ->
for (dc in grailsApplication.domainClasses) {
dc.metaClass.saveOrUpdate = { boolean flush = false ->
validateWithWarnings() ? delegate.save(flush: flush) : null
}
dc.metaClass.validateWithWarnings = { ->
delegate.validate()
if (delegate.hasErrors()) {
def message = new StringBuilder(
"problem ${delegate.id ? 'updating' : 'creating'} ${delegate.getClass().simpleName}: $delegate")
def locale = Locale.getDefault()
for (fieldErrors in delegate.errors) {
for (error in fieldErrors.allErrors) {
message.append("\n\t")
message.append(messageSource.getMessage(error, locale))
}
}
log.warn message
delegate.discard()
return false
}
return true
}
}
}
}
This depends on a 'log' variable being in scope, which will be true in any Grails artifact. This changes the controller usage slightly:
class MyController {
def actionName = {
def thing = new Thing(params)
if (thing.saveOrUpdate()) {
redirect action: 'show', id: thing.id
}
else {
render view: 'create', model: [thing: thing]
}
}
}
As a metaclass method it may make more sense to rename it, e.g. saveWithWarnings().

Resources