Although this or similar questions have been asked before for much older versions of grails, I wonder what would be the best approach to access a configuration value from application.yml in a grails domain class with modern grails 4.0.3.
So, let's say we have a voucher.groovy like
class Voucher implements HibernateEntity<Voucher> {
...
Date reservedAt
static transients = ['validUntil']
Date getValidUntil() {
Integer expirationTime = // TODO: fill in the configuration value
DateTime expirationDateTime = new DateTime(this.reservedAt)
.plusDays(expirationTime)
.withHourOfDay(23)
.withMinuteOfHour(59)
.withSecondOfMinute(59)
.withMillisOfSecond(999)
return expirationDateTime.toDate()
}
}
and a configuration value named voucher.expirationTime in our application.yml like
...
voucher.expirationTime: 10
...
How could I access the config value in my getValidUntil() method?
EDIT
As #JeffScottBrown mentioned in his comment, you shouldn't access config values in your domain class. So I ended up with his suggested apporach using a custom gsp tag. (See the answer below)
How to access configuration values in domain classes? You shouldn't!
In my case I needed to display a derived value as a combination of a domain attribute and a configuration value reservedAt + expirationTime.
Thanks to Jeff Scott Brown's comment, I managed to create a custom gsp tag for my purpose:
class VoucherTagLib {
static returnObjectForTags = ['validUntil']
static namespace = "voucher"
#Value('${voucher.expirationTime}')
Integer expirationTime
GrailsTagDateHelper grailsTagDateHelper
def validUntil = { attrs, body ->
Date reservedAt = attrs.reservedAt
String style = attrs.style ?: "SHORT"
Locale locale = GrailsWebRequest.lookup().getLocale()
if (locale == null) {
locale = Locale.getDefault()
}
def timeZone = grailsTagDateHelper.getTimeZone()
def dateFormat = grailsTagDateHelper.getDateFormat(style, timeZone, locale)
DateTime expirationDateTime = new DateTime(reservedAt)
.plusDays(expirationTime - 1)
.withHourOfDay(23)
.withMinuteOfHour(59)
.withSecondOfMinute(59)
.withMillisOfSecond(999)
return grailsTagDateHelper.format(dateFormat, expirationDateTime.toDate())
}
}
Although this might not be the answer that you are looking for, I hope this will help others with a similar problem!
Related
I'm using Grails 3.2.4 and am attempting to use the email property of my User class as the username for registration.
So far, I've managed to get Spring Security Core to use the email as the username using the configuration setting below:
grails.plugin.springsecurity.userLookup.usernamePropertyName='email'
However, the registration functionality doesn't seem to take this into account and won't let me register a new user using only an email and password.
I've made a few attempts at overriding the RegisterController but I continue to experience different errors regarding null usernames.
It seems like I must be missing something very simple. Any help / direction is greatly appreciated.
It appears that in version spring-security-ui-3.0.0.M2 the username property might not be override-able.
String paramNameToPropertyName(String paramName, String controllerName) {
// Properties in the ACL classes and RegistrationCode aren't currently
// configurable, and the PersistentLogin class is generated but its
// properties also aren't configurable. Since this method is only by
// the controllers to be able to hard-code param names and lookup the
// actual domain class property name we can short-circuit the logic here.
// Additionally there's no need to support nested properties (e.g.
// 'aclClass.className' for AclObjectIdentity search) since those are
// not used in GSP for classes with configurable properties.
if (!paramName) {
return paramName
}
Class<?> clazz = domainClassesByControllerName[controllerName]
String name
if (clazz) {
String key = paramName
if (paramName.endsWith('.id')) {
key = paramName[0..-4]
}
name = classMappings[clazz][key]
}
name ?: paramName
}
PS I'm getting around this now by doing something like this in my user domain class:
static transients = ["migrating"]]
String username = null
public void setEmail(String email) {
this.email = email
this.username = email
}
Let's say we have the following line of code:
<p> <g:message code="nav.usuario.show" /> </p>
If we are using an italian computer, Grails will look at messages_it.properties first. If grails desn't find nav.usuario.show=textLabel there, will try to find it in messages.properties. I want to change this behavior to look at message_es.properties instead the default messages.properties (but only if the label is not in the current locale language)
I tried the following code, but I didn't see any change. resources.groovy:
beans = {
localeResolver(org.springframework.web.servlet.i18n.SessionLocaleResolver) {
defaultLocale = new Locale("it","")
java.util.Locale.setDefault(defaultLocale)
}
}
Simply copy the code of the language you want by default (for example messages_es.properties) to the default messages.properties file.
If you want to keep the English language, you have to create a new file with a name like messages_en.properties. Move the code of messages.properties there.
If you want to fix the locale, put the following lines into the init closure of your BootStrap.groovy:
TimeZone.setDefault(TimeZone.getTimeZone("CET"))
Locale.setDefault(new Locale("it"));
or
Locale.setDefault(new Locale("es"));
beans = {
localeResolver(org.springframework.web.servlet.i18n.SessionLocaleResolver)
}
Then setup a filter to change the default locale based on the request.
//Filter
class LocaleFilters {
def localeResolver
def filters = {
localize(controller: '*') {
before = {
Locale.setDefault(localeResolver.resolveLocale(request))
return true
}
}
}
}
If the local context is Spanish then the default sets to _es, so on an so forth for other locales based on which locale the application is accessed.
I have a grails application that has a service that creates reports. The report is defined as:
class Report {
Date createDate
String reportType
List contents
static constraints = {
}
}
The service generates a report and populates contents as a list that is returned by createCriteria.
My problem is that my service claims to be saving the Report, no errors turn up, logging says that its all there, but when I go to call show from the controller on that report, it says contents is null.
Another relevant bit, my Service is called by an ActiveMQ message queue. The message originating from my report controller.
Controller:
class ReportController {
def scaffold = Report
def show = {
def rep = Report.get(params.id)
log.info("Report is " + (rep? "not null" : "null")) //says report is not null
log.info("Report content is " + (rep.contents? "not null" : "null")) //always says report.contents is null.
redirect(action: rep.reportType, model: [results: rep.contents, resultsTotal: rep.contents.size()])
}
}
My service that creates the report:
class ReportService {
static transactional = false
static expose = ['jms']
static destination = "Report"
void onMessage(msg)
{
this."$msg.reportType"(msg)
}
void totalQuery(msg)
{
def results = Result.createCriteria().list {
//This returns exactly what i need.
}
Report.withTransaction() {
def rep = new Report(createDate: new Date(), reportType: "totalQuery", contents: results)
log.info("Validation results: ${rep.validate()}")
if( !rep.save(flush: true) ) {
rep.errors.each {
log.error(it)
}
}
}
}
Is there something obvious that I'm missing here? My thought is that since all my unit tests work, that the hibernate context is not being passed through the message queue. But that would generate Exceptions wouldn't it? I've been beating my head on this problem for days, so a point in the right direction would be great.
Thanks,
You can't define an arbitrary List like that, so it's getting ignored and treated as transient. You'd get the same behavior if you had a def name field, since in both cases Hibernate doesn't know the data type, so it has no idea how to map it to the database.
If you want to refer to a collection of Results, then you need a hasMany:
class Report {
Date createDate
String reportType
static hasMany = [contents: Result]
}
If you need the ordered list, then also add in a List field with the same name, and instead of creating a Set (the default), it will be a List:
class Report {
Date createDate
String reportType
List contents
static hasMany = [contents: Result]
}
Your unit tests work because you're not accessing a database or using Hibernate. I think it's best to always integration test domain classes so you at least use the in-memory database, and mock the domain classes when testing controllers, services, etc.
I'm having an issue with grails. I have a domain that looks like:
class Book {
static belongsTo = Author
String toString() { title }
Author bookAuthor
String title
String currentPage
static constraints = {
bookAuthor()
title(unique:true)
currentPage()
}
}
The main thing to note is that I have title(unique:true) to avoid from adding the same book twice. However, this is causing issues. In the controller I have created:
def populate = {
def bookInstance = new Book()
def dir = 'C:/currentBooks.txt'
def bookList
bookList = readFile(dir) //read file and push values into bookList
int numOfBooks = bookList.size()
numOfBooks.times {
bookInstance.setBookAuthor(bookList.author[it])
bookInstance.setTitle(bookList.title[it])
bookInstance.setCurrentPage(bookList.title[it])
bookInstance.save()
}
}
I call populate to read a file and populate the database with new Books. The problem is that I want to update it with new values. For instance, lets say that the book already exists in the database but I have read farther into the book and want to change the currentPage so the data is changed in the file and populate is called but doesn't update the page because the title already exists.
Can someone explain how to update the results with the new values?
First of all, you need a key for your Book domain object. You have the title marked as unique, which suggests you want to use that to uniquely identify a Book. I'd recommend against that (what happens when two books have the same title?) and use the id grails provides by default. That means you'll have to store the id in your currentBooks.txt in addition to your other fields.
Once you've got an id, you can try loading an existing record from the database. If not, create a new one. For Example:
def dir = 'C:/currentBooks.txt'
def bookList
bookList = readFile(dir) //read file and push values into bookList
int numOfBooks = bookList.size()
numOfBooks.times {
def bookInstance.get(bookList.id[it])
if (!bookInstance) {
bookInstance = new Book()
}
bookInstance.setBookAuthor(bookList.author[it])
bookInstance.setTitle(bookList.title[it])
bookInstance.setCurrentPage(bookList.title[it])
bookInstance.save()
}
Alternatively, you could use the title as the id. This is a bad idea as indicated above, but it saves having to keep track of a separate id and change the format of currentBooks.txt. With Book defined as below, you could call Book.get(bookList.title[it]):
class Book {
static belongsTo = Author
String toString() { title }
Author bookAuthor
String title
String currentPage
static constraints = {
bookAuthor()
title(unique:true)
currentPage()
}
static mapping = {
id name: 'title', generator: 'assigned'
}
}
I thought this would be really simple but ..
We've create a user and a member type with various properties
When we try to access the properties via the member object we got nothing.
//Member m is current User
eg.
Property s = m.getProperty("PreferdUserName");
is null
m.getProperties has a count of Zero..
have we missed something obvious?
Could there be a spelling error?
"PreferdUserName" may want to be "PreferredUserName".
Other than that it looks correct.
In the end i resorted to storing member properties in a separate db table, which anyhow is closer to what i need.
I presume it had something to do with the way I created the memberType from outside umbraco using a custom msbuild task.
You could create your own class and extend ProfileBase. The code below will expose the properties that you have created within Umbraco. e.g. umbraco alias is 'first_name'.
[SettingsAllowAnonymous(false)]
public string FirstName
{
get
{
var o = base.GetPropertyValue("first_name");
if (o == DBNull.Value)
{
return string.Empty;
}
return (string)o;
}
set
{
base.SetPropertyValue("first_name", value);
}
}
Then you can access properties like so...
string firstName = ((MemberProfile)HttpContext.Current.Profile).FirstName;
More info about how to set this all up can be seen here:
http://www.aaron-powell.com/posts/2010-04-07-umbraco-members-profiles.html
This might help someone else, if you need to get member details for someone other than the current user in Umbraco and have their Username.
var TheirUsername = "s12345";
Member MemberFind = new Member(Convert.ToInt32(Membership.GetUser(***TheirUsername***).ProviderUserKey));
//now use this value
var NameOfUser = MemberFind.Text;
var EmailAddress = MemberFind.Email;
Try
Property s = m.getProperty("PreferdUserName").value;
If that still doesn't work, then check out this great post about member properties
http://legacy.aaron-powell.com/blog/july-2009/umbraco-member-profiles.aspx