Skip to content
This repository was archived by the owner on Mar 10, 2025. It is now read-only.

Commit 753dfd9

Browse files
authored
Merge pull request #344 from sbglasius/fix/340
Fix/340
2 parents 062adff + acd5678 commit 753dfd9

File tree

7 files changed

+56
-12
lines changed

7 files changed

+56
-12
lines changed

grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package grails.plugin.formfields
1818

1919
import grails.core.GrailsApplication
2020
import groovy.transform.CompileStatic
21+
import groovy.util.logging.Slf4j
2122
import groovy.xml.MarkupBuilder
2223
import org.apache.commons.lang.StringUtils
2324
import org.grails.buffer.FastStringWriter
@@ -44,6 +45,7 @@ import java.text.NumberFormat
4445

4546
import static FormFieldsTemplateService.toPropertyNameFormat
4647

48+
@Slf4j
4749
class FormFieldsTagLib {
4850
static final namespace = 'f'
4951

@@ -615,6 +617,9 @@ class FormFieldsTagLib {
615617
def message = keysInPreferenceOrder.findResult { key ->
616618
message(code: key, default: null) ?: null
617619
}
620+
if(log.traceEnabled && !message) {
621+
log.trace("i18n missing translation for one of ${keysInPreferenceOrder}")
622+
}
618623
message ?: defaultMessage
619624
}
620625

src/docs/asciidoc/customizingFieldRendering.adoc

+2-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ NOTE: If the `bean` attribute was not supplied to `f:field` then `bean`, `type`,
279279

280280
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:
281281

282-
* 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`.
282+
* 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`.
283+
* 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`
283284
* 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`.
284285
* The natural property name. For example when using `<f:field bean="personInstance" property="dateOfBirth"/>` the plugin will use the label `"Date Of Birth"`.
285286

src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorFactory.groovy

+6-2
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,18 @@ class BeanPropertyAccessorFactory implements GrailsApplicationAware {
7474
DomainProperty domainProperty = resolvePropertyFromPathComponents(beanWrapper, pathElements, params)
7575

7676
if (domainProperty != null) {
77-
new DelegatingBeanPropertyAccessorImpl(bean, params.value, params.propertyType as Class, pathFromRoot, domainProperty)
77+
new DelegatingBeanPropertyAccessorImpl(bean, params.value, params.propertyType as Class, pathFromRoot, domainProperty, addPathFromRoot)
7878
} else {
7979
new BeanPropertyAccessorImpl(params)
8080
}
8181

8282
}
8383

84-
private DomainProperty resolvePropertyFromPathComponents(BeanWrapper beanWrapper, List<String> pathElements, Map params) {
84+
private boolean getAddPathFromRoot() {
85+
grailsApplication.config.getProperty('grails.plugin.fields.i18n.addPathFromRoot', Boolean)
86+
}
87+
88+
private DomainProperty resolvePropertyFromPathComponents(BeanWrapper beanWrapper, List<String> pathElements, Map params) {
8589
String propertyName = pathElements.remove(0)
8690
PersistentEntity beanClass = resolveDomainClass(beanWrapper.wrappedClass)
8791
Class propertyType = resolvePropertyType(beanWrapper, beanClass, propertyName)

src/main/groovy/grails/plugin/formfields/BeanPropertyAccessorImpl.groovy

+13-4
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor {
6969
grailsApplication.config.getProperty("grails.databinding.$paramName", Boolean, defaultParamValue)
7070
}
7171

72+
private boolean getAddPathFromRoot() {
73+
grailsApplication.config.getProperty('grails.plugin.fields.i18n.addPathFromRoot', Boolean, false)
74+
}
75+
7276
List<Class> getBeanSuperclasses() {
7377
getSuperclassesAndInterfaces(beanType)
7478
}
@@ -78,10 +82,15 @@ class BeanPropertyAccessorImpl implements BeanPropertyAccessor {
7882
}
7983

8084
List<String> getLabelKeys() {
81-
[
82-
"${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, ''),
83-
"${GrailsNameUtils.getPropertyName(beanType.simpleName)}.${propertyName}.label"
84-
].unique() as List<String>
85+
List<String> labelKeys = []
86+
87+
labelKeys << "${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, '')
88+
if(addPathFromRoot) {
89+
labelKeys << "${pathFromRoot}.label".replaceAll(/\[(.+)\]/, '')
90+
}
91+
labelKeys << "${GrailsNameUtils.getPropertyName(beanType.simpleName)}.${propertyName}.label".toString()
92+
93+
return labelKeys.unique() as List<String>
8594
}
8695

8796
String getDefaultLabel() {

src/main/groovy/grails/plugin/formfields/DelegatingBeanPropertyAccessorImpl.groovy

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,17 @@ class DelegatingBeanPropertyAccessorImpl implements BeanPropertyAccessor {
3030
final Class beanType
3131
final String propertyName
3232
final Class propertyType
33+
final boolean addPathFromRoot
3334

34-
DelegatingBeanPropertyAccessorImpl(Object rootBean, Object value, Class propertyType, String pathFromRoot, DomainProperty domainProperty) {
35+
DelegatingBeanPropertyAccessorImpl(Object rootBean, Object value, Class propertyType, String pathFromRoot, DomainProperty domainProperty, boolean addPathFromRoot) {
3536
this.rootBean = rootBean
3637
this.value = value
3738
this.pathFromRoot = pathFromRoot
3839
this.domainProperty = domainProperty
3940
this.propertyType = propertyType
4041
this.propertyName = domainProperty.name
4142
this.beanType = domainProperty.beanType
43+
this.addPathFromRoot = addPathFromRoot
4244
}
4345

4446
@Override
@@ -97,6 +99,9 @@ class DelegatingBeanPropertyAccessorImpl implements BeanPropertyAccessor {
9799
List labelKeys = []
98100
if (rootBean) {
99101
labelKeys.add("${GrailsNameUtils.getPropertyName(rootBeanType.simpleName)}.${pathFromRoot}.label".replaceAll(/\[(.+)\]/, ''))
102+
if (addPathFromRoot) {
103+
labelKeys.add("${pathFromRoot}.label".replaceAll(/\[(.+)\]/, ''))
104+
}
100105
}
101106
labelKeys.addAll(domainProperty.labelKeys)
102107
labelKeys.unique() as List<String>

src/test/groovy/grails/plugin/formfields/BuildsAccessorFactory.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ abstract class BuildsAccessorFactory extends Specification implements GrailsWebU
2121
proxyHandler = new DefaultProxyHandler()
2222
grailsDomainClassMappingContext = ref("grailsDomainClassMappingContext")
2323
fieldsDomainPropertyFactory = dpf
24+
grailsApplication = ref('grailsApplication')
2425
}
2526
}
2627
}

src/test/groovy/grails/plugin/formfields/DomainClassPropertyAccessorSpec.groovy

+23-4
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory {
158158
propertyAccessor.domainProperty == null
159159
}
160160

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

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

235+
@Issue('https://github.com/gpc/fields/issues/340')
236+
void "label keys for '#property' are '#labels' when addPathFromRoot == true"() {
237+
given:
238+
config.setAt('grails.plugin.fields.i18n.addPathFromRoot', true)
239+
def bean = beanType.list().find { it.class == beanType}
240+
def propertyAccessor = factory.accessorFor(bean, property)
241+
242+
expect:
243+
propertyAccessor.labelKeys == labels
244+
245+
where:
246+
beanType | property | labels
247+
Person | 'name' | ['person.name.label', 'name.label']
248+
Person | 'dateOfBirth' | ['person.dateOfBirth.label', 'dateOfBirth.label']
249+
Person | 'address' | ['person.address.label', 'address.label']
250+
Person | 'address.city' | ['person.address.city.label', 'address.city.label']
251+
Author | 'books[0].title' | ['author.books.title.label', 'books.title.label', 'book.title.label']
252+
}
253+
235254
void "default label for '#property' is '#label'"() {
236255
given:
237256
def bean = beanType.list().first()
@@ -292,7 +311,7 @@ class DomainClassPropertyAccessorSpec extends BuildsAccessorFactory {
292311
propertyAccessor.invalid
293312
}
294313

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

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

0 commit comments

Comments
 (0)