Skip to content

Commit

Permalink
Merge branch 'master' into upgrade/grails4
Browse files Browse the repository at this point in the history
* master:
  If field validation error codes are unresolved and fallback to the defaultMessage on the error, this defaultMessage must be escaped due to the possibility of user input being in the error message. Fixes issue gpc#323.
  Update readme.md
  respect defaultNullable() of Validateable classes (issue gpc#218)
  fix warning "Accessing config through dot notation is deprecated"
  Added Action: Stale issues and pr's
  Back to snapshot
  Release 3.0.0.RC1
  Use release versions of dependencies
  Update dependencies
  Bump up serverlet api version to 4.0.1
  Bump up Groovy to 2.5.5

# Conflicts:
#	build.gradle
#	gradle.properties
  • Loading branch information
sbglasius committed Feb 10, 2023
2 parents 798f373 + 8b4a870 commit d07786c
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 13 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Mark stale issues and pull requests

on:
schedule:
- cron: "30 1 * * *"

jobs:
stale:

runs-on: ubuntu-latest

steps:
- uses: actions/stale@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Stale issue. Marked automatically'
stale-pr-message: 'Stale pull request. Marked automatically'
stale-issue-label: 'no-issue-activity'
stale-pr-label: 'no-pr-activity'
days-before-stale: 120
7 changes: 3 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,8 @@ dependencies {
provided "org.springframework.boot:spring-boot-starter-tomcat"

provided "org.grails:grails-web-boot"
compile "org.grails:grails-core"
compile "org.grails:grails-dependencies"
compile 'javax.servlet:javax.servlet-api:3.1.0'
provided "org.grails:grails-dependencies"
provided "javax.servlet:javax.servlet-api:$servletApiVersion"

compile "org.grails:scaffolding-core"

Expand Down Expand Up @@ -176,4 +175,4 @@ task snapshotVersion {
}
}
}
}
}
7 changes: 6 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
projectVersion=3.0.0.BUILD-SNAPSHOT
grailsVersion=4.0.13
#projectVersion=2.2.7
grailsVersion=4.1.2
scaffoldingVersion=2.0.0.RC1
groovyVersion=2.5.6
cglibNodepVersion=3.2.9
joddWotVersion=3.3.8
servletApiVersion=4.0.1
asciidoc=true
githubSlug=grails-fields-plugin/grails-fields
githubBranch=master
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ import org.grails.scaffolding.model.property.DomainPropertyFactory
import org.grails.web.servlet.mvc.GrailsWebRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.MessageSource
import org.springframework.context.MessageSourceResolvable
import org.springframework.context.NoSuchMessageException
import org.springframework.web.servlet.LocaleResolver

import javax.servlet.http.HttpServletRequest
Expand Down Expand Up @@ -68,6 +71,7 @@ class FormFieldsTagLib {
DomainModelService domainModelService
LocaleResolver localeResolver
CodecLookup codecLookup
MessageSource messageSource

static defaultEncodeAs = [taglib: 'raw']

Expand Down Expand Up @@ -458,7 +462,18 @@ class FormFieldsTagLib {
value : (value instanceof Number || value instanceof Boolean || value) ? value : valueDefault,
constraints : propertyAccessor.constraints,
persistentProperty: propertyAccessor.domainProperty,
errors : propertyAccessor.errors.collect { message(error: it) },
errors : propertyAccessor.errors.collect { error ->
String errorMsg = null
try {
errorMsg = error instanceof MessageSourceResolvable ? messageSource.getMessage(error, locale) : messageSource.getMessage(error.toString(), null, locale)
}
catch (NoSuchMessageException ignored) {
// no-op
}
// unresolved message codes fallback to the defaultMessage and this should
// be escaped as it could be an error message with user input
errorMsg && errorMsg == error.defaultMessage ? message(error: error, encodeAs: "HTML") : message(error: error)
},
required : attrs.containsKey("required") ? Boolean.valueOf(attrs.remove('required')) : propertyAccessor.required,
invalid : attrs.containsKey("invalid") ? Boolean.valueOf(attrs.remove('invalid')) : propertyAccessor.invalid,
prefix : resolvePrefix(attrs.remove('prefix')),
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- `2.2.x` y `2.1.x` for Grails 3.
- `grails2.x` for Grails 2.

A spiritual successor to the [bean-fields plugin](http://grails.org/plugin/bean-fields) that attempts to provide a configurable way to render forms with appropriate inputs for different properties without having to copy and paste lots of boilerplate code. It should be possible to change the rendering for a field with the minimum of impact on any other code. This plugin attempts to achieve that by using GSP templates looked up by convention. Developers can then create templates for rendering particular properties or types of properties with the former overriding the latter.
A spiritual successor to the bean-fields plugin that attempts to provide a configurable way to render forms with appropriate inputs for different properties without having to copy and paste lots of boilerplate code. It should be possible to change the rendering for a field with the minimum of impact on any other code. This plugin attempts to achieve that by using GSP templates looked up by convention. Developers can then create templates for rendering particular properties or types of properties with the former overriding the latter.

For further information please see the full documentation.

Expand Down
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 grails.gorm.validation.DefaultConstrainedProperty
import grails.validation.Validateable
import groovy.transform.PackageScope
import grails.core.support.GrailsApplicationAware
import grails.core.support.proxy.ProxyHandler
Expand Down Expand Up @@ -110,7 +111,9 @@ class BeanPropertyAccessorFactory implements GrailsApplicationAware {
}

private Constrained resolveConstraints(BeanWrapper beanWrapper, String propertyName) {
grails.gorm.validation.Constrained constraint = constraintsEvaluator.evaluate(beanWrapper.wrappedClass)[propertyName]
Class<?> type = beanWrapper.wrappedClass
boolean defaultNullable = Validateable.class.isAssignableFrom(type) ? type.metaClass.invokeStaticMethod(type, 'defaultNullable') : false
grails.gorm.validation.Constrained constraint = constraintsEvaluator.evaluate(type, defaultNullable)[propertyName]
if (!constraint) {
constraint = createDefaultConstraint(beanWrapper, propertyName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ class FormFieldsTemplateService {

private boolean shouldCache() {
// If not explicitly specified, there is no template caching
Boolean cacheDisabled = grailsApplication?.config?.grails?.plugin?.fields?.disableLookupCache
Boolean cacheDisabled = grailsApplication?.config?.getProperty('grails.plugin.fields.disableLookupCache', Boolean)
return !cacheDisabled
}

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package grails.plugin.formfields

import grails.plugin.formfields.mock.*
import spock.lang.*

import grails.plugin.formfields.mock.Gender
import grails.plugin.formfields.mock.Person
import grails.validation.Validateable
import spock.lang.Issue
import spock.lang.Unroll

@Unroll
class CommandPropertyAccessorSpec extends BuildsAccessorFactory {
Expand Down Expand Up @@ -256,6 +260,20 @@ class CommandPropertyAccessorSpec extends BuildsAccessorFactory {
propertyAccessor.constraints.blank
}

@Issue('https://github.com/grails-fields-plugin/grails-fields/issues/218')
void 'respect defaultNullable() when evaluating constraints of a Validateable'() {
given:
ValidateableCommand command = new ValidateableCommand()
DefaultNullableValidateableCommand command2 = new DefaultNullableValidateableCommand()
and:
def propertyAccessor = factory.accessorFor(command, 'myNullableProperty')
def propertyAccessor2 = factory.accessorFor(command2, 'myNullableProperty')
expect:
!propertyAccessor.constraints.nullable
propertyAccessor2.constraints.nullable
}
}
class TestCommand {
Expand All @@ -278,4 +296,16 @@ class TestCommand {
class UnconstrainedCommand {
String stringProperty
}
}
class ValidateableCommand implements Validateable {
String myNullableProperty
}
class DefaultNullableValidateableCommand implements Validateable {
String myNullableProperty
static boolean defaultNullable() {
return true
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package grails.plugin.formfields.taglib

import grails.plugin.formfields.mock.Gender
import grails.plugin.formfields.mock.Person
import grails.testing.web.taglib.TagLibUnitTest
import grails.validation.ValidationErrors
import org.grails.plugins.web.taglib.FormatTagLib
import org.springframework.context.support.StaticMessageSource
import spock.lang.Issue
import grails.plugin.formfields.*

Expand Down Expand Up @@ -56,4 +60,44 @@ class FieldTagWithBodySpec extends AbstractFormFieldsTagLibSpec implements TagLi
expect:
applyTemplate('<f:field bean="personInstance" property="name" input-foo="bar">${attrs.foo}</f:field>', [personInstance: personInstance]) == 'bar'
}

@Issue("https://github.com/grails-fields-plugin/grails-fields/issues/323")
void 'validation defaultMessage strings are escaped'() {
given:
views['/_fields/default/_wrapper.gsp'] = '${widget}'

and:
def person = new Person(name: 'Not Allowed', gender: Gender.Male, password: 'XYZ').with {
errors = new ValidationErrors(it)
errors.rejectValue('name', 'unresolved.code', 'custom error with special chars & < > \' "')
it
}

when:
def result = applyTemplate('<f:field bean="personInstance" property="name" encodeAs="raw">${errors[0]}</f:field>', [personInstance: person])

then:
result == 'custom error with special chars &amp; &lt; &gt; &#39; &quot;'
}

void 'resolved error codes are not escaped'() {
given:
views['/_fields/default/_wrapper.gsp'] = '${widget}'

and:
((StaticMessageSource) messageSource).addMessage('name.invalid', FormatTagLib.resolveLocale(null), '<div>Name is invalid</div>')

and:
def person = new Person(name: 'Not Allowed', gender: Gender.Male, password: 'XYZ').with {
errors = new ValidationErrors(it)
errors.rejectValue('name', 'name.invalid', 'default error message')
it
}

when:
def result = applyTemplate('<f:field bean="personInstance" property="name" encodeAs="raw">${errors[0]}</f:field>', [personInstance: person])

then:
result == '<div>Name is invalid</div>'
}
}

0 comments on commit d07786c

Please sign in to comment.