Skip to content

Commit

Permalink
Merge pull request #344 from sbglasius/fix/340
Browse files Browse the repository at this point in the history
Fix/340
  • Loading branch information
sbglasius authored Apr 8, 2024
2 parents 062adff + acd5678 commit 753dfd9
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package grails.plugin.formfields

import grails.core.GrailsApplication
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import groovy.xml.MarkupBuilder
import org.apache.commons.lang.StringUtils
import org.grails.buffer.FastStringWriter
Expand All @@ -44,6 +45,7 @@ import java.text.NumberFormat

import static FormFieldsTemplateService.toPropertyNameFormat

@Slf4j
class FormFieldsTagLib {
static final namespace = 'f'

Expand Down Expand Up @@ -615,6 +617,9 @@ class FormFieldsTagLib {
def message = keysInPreferenceOrder.findResult { key ->
message(code: key, default: null) ?: null
}
if(log.traceEnabled && !message) {
log.trace("i18n missing translation for one of ${keysInPreferenceOrder}")
}
message ?: defaultMessage
}

Expand Down
3 changes: 2 additions & 1 deletion src/docs/asciidoc/customizingFieldRendering.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ NOTE: If the `bean` attribute was not supplied to `f:field` then `bean`, `type`,

If the `label` attribute is not supplied to the `f:field` tag then the label string passed to the field template is resolved by convention. The plugin uses the following order of preference for the label:

* An i18n message using the key '_beanClass_._path_`.label`'. For example when using `<f:field bean="personInstance" property="address.city"/>` the plugin will try the i18n key `person.address.city.label`. If the property path contains any index it is removed so `<f:field bean="authorInstance" property="books<<0>>.title"/>` would use the key `author.books.title.label`.
* An i18n message using the key '_beanClass_._path_.label'. For example when using `<f:field bean="authorInstance" property="book.title"/>` the plugin will try the i18n key `author.book.title.label`. If the property path contains any index it is removed so `<f:field bean="authorInstance" property="books<<0>>.title"/>` would use the key `author.books.title.label`.
* For classes using the same bean class as properties, it is possible to get a key without the class name prefixed. If the configuration value `grails.plugin.fields.i18n.addPathFromRoot` is set to `true` (default: `false`). _Example_: a class `Publisher` has two `Address` properties `authorAddress` and `printAddress`. With `addPathFromRoot=true` they will share the key `address.city.label`. The same goes if `Author` and `Publisher` had a `Book book`, the key would be `book.title.label`, and if they both had a `List<Book> books` the key would be `books.title.label`
* An i18n message using the key '_objectType_._propertyName_`.label`'. For example when using `<f:field bean="personInstance" property="address.city"/>` the plugin will try the i18n key `address.city.label`.
* The natural property name. For example when using `<f:field bean="personInstance" property="dateOfBirth"/>` the plugin will use the label `"Date Of Birth"`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,18 @@ class BeanPropertyAccessorFactory implements GrailsApplicationAware {
DomainProperty domainProperty = resolvePropertyFromPathComponents(beanWrapper, pathElements, params)

if (domainProperty != null) {
new DelegatingBeanPropertyAccessorImpl(bean, params.value, params.propertyType as Class, pathFromRoot, domainProperty)
new DelegatingBeanPropertyAccessorImpl(bean, params.value, params.propertyType as Class, pathFromRoot, domainProperty, addPathFromRoot)
} else {
new BeanPropertyAccessorImpl(params)
}

}

private DomainProperty resolvePropertyFromPathComponents(BeanWrapper beanWrapper, List<String> pathElements, Map params) {
private boolean getAddPathFromRoot() {
grailsApplication.config.getProperty('grails.plugin.fields.i18n.addPathFromRoot', Boolean)
}

private DomainProperty resolvePropertyFromPathComponents(BeanWrapper beanWrapper, List<String> pathElements, Map params) {
String propertyName = pathElements.remove(0)
PersistentEntity beanClass = resolveDomainClass(beanWrapper.wrappedClass)
Class propertyType = resolvePropertyType(beanWrapper, beanClass, propertyName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor {
grailsApplication.config.getProperty("grails.databinding.$paramName", Boolean, defaultParamValue)
}

private boolean getAddPathFromRoot() {
grailsApplication.config.getProperty('grails.plugin.fields.i18n.addPathFromRoot', Boolean, false)
}

List<Class> getBeanSuperclasses() {
getSuperclassesAndInterfaces(beanType)
}
Expand All @@ -78,10 +82,15 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor {
}

List<String> getLabelKeys() {
[
"${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, ''),
"${GrailsNameUtils.getPropertyName(beanType.simpleName)}.${propertyName}.label"
].unique() as List<String>
List<String> labelKeys = []

labelKeys << "${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, '')
if(addPathFromRoot) {
labelKeys << "${pathFromRoot}.label".replaceAll(/\[(.+)\]/, '')
}
labelKeys << "${GrailsNameUtils.getPropertyName(beanType.simpleName)}.${propertyName}.label".toString()

return labelKeys.unique() as List<String>
}

String getDefaultLabel() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ class DelegatingBeanPropertyAccessorImpl implements BeanPropertyAccessor {
final Class beanType
final String propertyName
final Class propertyType
final boolean addPathFromRoot

DelegatingBeanPropertyAccessorImpl(Object rootBean, Object value, Class propertyType, String pathFromRoot, DomainProperty domainProperty) {
DelegatingBeanPropertyAccessorImpl(Object rootBean, Object value, Class propertyType, String pathFromRoot, DomainProperty domainProperty, boolean addPathFromRoot) {
this.rootBean = rootBean
this.value = value
this.pathFromRoot = pathFromRoot
this.domainProperty = domainProperty
this.propertyType = propertyType
this.propertyName = domainProperty.name
this.beanType = domainProperty.beanType
this.addPathFromRoot = addPathFromRoot
}

@Override
Expand Down Expand Up @@ -97,6 +99,9 @@ class DelegatingBeanPropertyAccessorImpl implements BeanPropertyAccessor {
List labelKeys = []
if (rootBean) {
labelKeys.add("${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, ''))
if (addPathFromRoot) {
labelKeys.add("${pathFromRoot}.label".replaceAll(/\[(.+)\]/, ''))
}
}
labelKeys.addAll(domainProperty.labelKeys)
labelKeys.unique() as List<String>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ abstract class BuildsAccessorFactory extends Specification implements GrailsWebU
proxyHandler = new DefaultProxyHandler()
grailsDomainClassMappingContext = ref("grailsDomainClassMappingContext")
fieldsDomainPropertyFactory = dpf
grailsApplication = ref('grailsApplication')
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory {
propertyAccessor.domainProperty == null
}

@Issue('https://github.com/grails-fields-plugin/grails-fields/issues/37')
@Issue('https://github.com/gpc/fields/issues/37')
void "resolves constraints of the '#property' property when the intervening path is null"() {
given:
def book = new Book()
Expand Down Expand Up @@ -214,7 +214,7 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory {
propertyAccessor.constraints.inList == ["USA", "UK", "Canada"]
}

@Issue('https://github.com/grails-fields-plugin/grails-fields/issues/38')
@Issue('https://github.com/gpc/fields/issues/38')
void "label keys for '#property' are '#labels'"() {
given:
def bean = beanType.list().find { it.class == beanType }
Expand All @@ -232,6 +232,25 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory {
Author | 'books[0].title' | ['author.books.title.label', 'book.title.label']
}

@Issue('https://github.com/gpc/fields/issues/340')
void "label keys for '#property' are '#labels' when addPathFromRoot == true"() {
given:
config.setAt('grails.plugin.fields.i18n.addPathFromRoot', true)
def bean = beanType.list().find { it.class == beanType}
def propertyAccessor = factory.accessorFor(bean, property)

expect:
propertyAccessor.labelKeys == labels

where:
beanType | property | labels
Person | 'name' | ['person.name.label', 'name.label']
Person | 'dateOfBirth' | ['person.dateOfBirth.label', 'dateOfBirth.label']
Person | 'address' | ['person.address.label', 'address.label']
Person | 'address.city' | ['person.address.city.label', 'address.city.label']
Author | 'books[0].title' | ['author.books.title.label', 'books.title.label', 'book.title.label']
}

void "default label for '#property' is '#label'"() {
given:
def bean = beanType.list().first()
Expand Down Expand Up @@ -292,7 +311,7 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory {
propertyAccessor.invalid
}

@Issue('https://github.com/grails-fields-plugin/grails-fields/issues/160')
@Issue('https://github.com/gpc/fields/issues/160')
void "resolves transient property"() {
given:
def propertyAccessor = factory.accessorFor(person, "transientText")
Expand All @@ -309,7 +328,7 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory {
propertyAccessor.constraints.nullable
}

@Issue('https://github.com/grails-fields-plugin/grails-fields/issues/160')
@Issue('https://github.com/gpc/fields/issues/160')
void "resolves id property that has no constraints"() {
given:
def propertyAccessor = factory.accessorFor(person, "id")
Expand Down

0 comments on commit 753dfd9

Please sign in to comment.