From 39f1d0e476467e1ac06ff65833de50a5fea7d63f Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 28 Nov 2025 13:48:45 -0800 Subject: [PATCH 1/4] display constraint expansion --- .../grails/gorm/validation/Constrained.groovy | 9 +- .../DefaultConstrainedProperty.groovy | 50 +++++- .../grails/gorm/validation/DisplayType.groovy | 80 ++++++++++ .../gorm/validation/DisplayTypeSpec.groovy | 151 ++++++++++++++++++ .../model/DomainModelServiceImpl.groovy | 44 +++-- .../model/property/Constrained.groovy | 20 +++ .../model/DomainModelServiceSpec.groovy | 6 +- .../validation/ConstrainedDelegate.groovy | 6 + 8 files changed, 351 insertions(+), 15 deletions(-) create mode 100644 grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy create mode 100644 grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy diff --git a/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/Constrained.groovy b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/Constrained.groovy index 50b7b48c7dc..9bf44c7a542 100644 --- a/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/Constrained.groovy +++ b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/Constrained.groovy @@ -88,9 +88,16 @@ interface Constrained { */ boolean isUrl() /** - * @return Whether the value should be displayed + * @return Whether the value should be displayed (for backwards compatibility) + * @deprecated Use {@link #getDisplayType()} instead for more granular control */ boolean isDisplay() + + /** + * @return The display type controlling where this property is shown in scaffolded views + * @since 7.1 + */ + DisplayType getDisplayType() /** * @return Whether the value is editable */ diff --git a/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DefaultConstrainedProperty.groovy b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DefaultConstrainedProperty.groovy index 56090084f68..8908205a9c6 100644 --- a/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DefaultConstrainedProperty.groovy +++ b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DefaultConstrainedProperty.groovy @@ -70,8 +70,8 @@ class DefaultConstrainedProperty implements ConstrainedProperty { protected final Map appliedConstraints = new LinkedHashMap() // simple constraints - /** whether the property should be displayed */ - boolean display = true + /** the display type controlling where the property is shown in scaffolded views */ + DisplayType displayType = null /** * whether the property is editable */ @@ -581,6 +581,52 @@ class DefaultConstrainedProperty implements ConstrainedProperty { } } + /** + * @return Whether the property should be displayed (for backwards compatibility). + * Returns true unless displayType is explicitly set to NONE. + * @deprecated Use {@link #getDisplayType()} instead for more granular control + */ + @Override + boolean isDisplay() { + displayType != DisplayType.NONE + } + + /** + * Returns the display value for property access compatibility with ClassPropertyFetcher. + * Returns the displayType if set, otherwise returns the boolean display value. + * This method exists to ensure ClassPropertyFetcher.getPropertyDescriptor("display") works. + * @return The display value (DisplayType or Boolean) + */ + Object getDisplay() { + displayType != null ? displayType : isDisplay() + } + + /** + * Sets the display constraint with backwards compatibility for boolean values. + * @param value Can be a Boolean (true/false) or a DisplayType enum value + */ + void setDisplay(Object value) { + if (value instanceof Boolean) { + this.displayType = value ? null : DisplayType.NONE + } else if (value instanceof DisplayType) { + this.displayType = value + } else if (value == null) { + this.displayType = null + } else { + throw new IllegalArgumentException("display constraint must be a Boolean or DisplayType, got: ${value?.class?.name}") + } + } + + /** + * @return The display type controlling where this property is shown in scaffolded views. + * Returns null if not explicitly set (default behavior applies). + * @since 7.1 + */ + @Override + DisplayType getDisplayType() { + this.displayType + } + @SuppressWarnings('rawtypes') Map getAttributes() { return attributes diff --git a/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy new file mode 100644 index 00000000000..6fb27531b96 --- /dev/null +++ b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package grails.gorm.validation + +/** + * Enum representing the display behavior for a constrained property in scaffolded views. + * + *

This enum controls where a property is displayed in generated scaffolding views:

+ *
    + *
  • {@link #ALL} - Display everywhere, including overriding default blacklists (e.g., dateCreated, lastUpdated)
  • + *
  • {@link #NONE} - Never display in any view
  • + *
  • {@link #INPUT} - Display only in input forms (create/edit views)
  • + *
  • {@link #OUTPUT} - Display only in output views (show/index views)
  • + *
+ * + *

Example usage in domain class constraints:

+ *
+ * import static grails.gorm.validation.DisplayType.*
+ *
+ * class Book {
+ *     String title
+ *     Date dateCreated
+ *     String internalNotes
+ *
+ *     static constraints = {
+ *         dateCreated display: ALL      // Override blacklist, show everywhere
+ *         internalNotes display: NONE   // Never show
+ *     }
+ * }
+ * 
+ * + *

For backwards compatibility, boolean values are also supported:

+ *
    + *
  • {@code display: true} is equivalent to the default behavior (not setting display)
  • + *
  • {@code display: false} is equivalent to {@link #NONE}
  • + *
+ * + * @since 7.1 + */ +enum DisplayType { + + /** + * Display the property in all views (input and output). + * This also overrides the default blacklist for properties like dateCreated and lastUpdated. + */ + ALL, + + /** + * Never display the property in any view. + */ + NONE, + + /** + * Display the property only in input views (create and edit forms). + */ + INPUT, + + /** + * Display the property only in output views (show and index/list views). + */ + OUTPUT + +} diff --git a/grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy b/grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy new file mode 100644 index 00000000000..9824a398fdd --- /dev/null +++ b/grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package grails.gorm.validation + +import org.grails.datastore.gorm.validation.constraints.registry.DefaultConstraintRegistry +import spock.lang.Specification + +class DisplayTypeSpec extends Specification { + + DefaultConstrainedProperty constrainedProperty + + void setup() { + constrainedProperty = new DefaultConstrainedProperty( + TestDomain, + "testProperty", + String, + new DefaultConstraintRegistry() + ) + } + + void "test default displayType is null"() { + expect: + constrainedProperty.displayType == null + } + + void "test default isDisplay returns true"() { + expect: + constrainedProperty.display == true + } + + void "test setDisplay with boolean false sets displayType to NONE"() { + when: + constrainedProperty.setDisplay(false) + + then: + constrainedProperty.displayType == DisplayType.NONE + constrainedProperty.display == false + } + + void "test setDisplay with boolean true sets displayType to null"() { + when: + constrainedProperty.setDisplay(true) + + then: + constrainedProperty.displayType == null + constrainedProperty.display == true + } + + void "test setDisplay with DisplayType.ALL"() { + when: + constrainedProperty.setDisplay(DisplayType.ALL) + + then: + constrainedProperty.displayType == DisplayType.ALL + constrainedProperty.display == true + } + + void "test setDisplay with DisplayType.NONE"() { + when: + constrainedProperty.setDisplay(DisplayType.NONE) + + then: + constrainedProperty.displayType == DisplayType.NONE + constrainedProperty.display == false + } + + void "test setDisplay with DisplayType.INPUT"() { + when: + constrainedProperty.setDisplay(DisplayType.INPUT) + + then: + constrainedProperty.displayType == DisplayType.INPUT + constrainedProperty.display == true // isDisplay still returns true for INPUT + } + + void "test setDisplay with DisplayType.OUTPUT"() { + when: + constrainedProperty.setDisplay(DisplayType.OUTPUT) + + then: + constrainedProperty.displayType == DisplayType.OUTPUT + constrainedProperty.display == true // isDisplay still returns true for OUTPUT + } + + void "test setDisplay with null resets to default"() { + given: + constrainedProperty.setDisplay(DisplayType.NONE) + + when: + constrainedProperty.setDisplay(null) + + then: + constrainedProperty.displayType == null + constrainedProperty.display == true + } + + void "test setDisplay with invalid type throws exception"() { + when: + constrainedProperty.setDisplay("invalid") + + then: + thrown(IllegalArgumentException) + } + + void "test applyConstraint with display false sets displayType to NONE"() { + when: + constrainedProperty.applyConstraint("display", false) + + then: + constrainedProperty.displayType == DisplayType.NONE + constrainedProperty.display == false + } + + void "test applyConstraint with display true sets displayType to null"() { + when: + constrainedProperty.applyConstraint("display", true) + + then: + constrainedProperty.displayType == null + constrainedProperty.display == true + } + + void "test applyConstraint with DisplayType enum"() { + when: + constrainedProperty.applyConstraint("display", DisplayType.INPUT) + + then: + constrainedProperty.displayType == DisplayType.INPUT + constrainedProperty.display == true + } + + static class TestDomain { + String testProperty + } +} diff --git a/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy b/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy index c558854c56d..27d1837a635 100644 --- a/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy +++ b/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy @@ -26,6 +26,7 @@ import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value +import grails.gorm.validation.DisplayType import grails.util.GrailsClassUtils import org.grails.datastore.mapping.config.Property import org.grails.datastore.mapping.config.Settings @@ -63,10 +64,13 @@ class DomainModelServiceImpl implements DomainModelService { /** *

Retrieves persistent properties and excludes:

    *
  • Any properties listed in the {@code static scaffold = [exclude: []]} property on the domain class - *
  • Any properties that have the constraint {@code [display: false]} + *
  • Any properties that have the constraint {@code [display: false]} or {@code [display: DisplayType.NONE]} + *
  • Any properties that have {@code [display: DisplayType.INPUT]} (output views only) *
  • Any properties whose name exist in the blackList *

* + *

Properties with {@code [display: DisplayType.ALL]} or {@code [display: DisplayType.OUTPUT]} will override the blacklist.

+ * * @see {@link DomainModelService#getInputProperties} * @param domainClass The persistent entity * @param blackList The list of domain class property names to exclude @@ -88,16 +92,25 @@ class DomainModelServiceImpl implements DomainModelService { } properties.removeAll { - if (it.name in blacklist) { - return true - } Constrained constrained = it.constrained - if (constrained && !constrained.display) { + DisplayType displayType = constrained?.displayType + + if (displayType == DisplayType.ALL || displayType == DisplayType.OUTPUT) { + // Explicit DisplayType overrides blacklist + } else if (displayType == DisplayType.NONE || displayType == DisplayType.INPUT) { return true + } else { + if (it.name in blacklist) { + return true + } + if (constrained && !constrained.display) { + return true + } } + if (derivedMethod != null) { - Property property = it.mapping.mappedForm - if (derivedMethod.invoke(property, (Object[]) null)) { + Property property = it.mapping?.mappedForm + if (property != null && derivedMethod.invoke(property, (Object[]) null)) { return true } } @@ -174,13 +187,22 @@ class DomainModelServiceImpl implements DomainModelService { } properties.removeAll { - if (it.name in blacklist) { - return true - } Constrained constrained = it.constrained - if (constrained && !constrained.display) { + DisplayType displayType = constrained?.displayType + + if (displayType == DisplayType.ALL || displayType == DisplayType.INPUT) { + // Explicit DisplayType overrides blacklist + } else if (displayType == DisplayType.NONE || displayType == DisplayType.OUTPUT) { return true + } else { + if (it.name in blacklist) { + return true + } + if (constrained && !constrained.display) { + return true + } } + if (derivedMethod != null) { Property property = it.mapping.mappedForm if (derivedMethod.invoke(property, (Object[]) null)) { diff --git a/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy b/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy index c726be7d394..0abfd228eed 100644 --- a/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy +++ b/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy @@ -19,6 +19,8 @@ package org.grails.scaffolding.model.property +import grails.gorm.validation.DisplayType + class Constrained { grails.gorm.validation.Constrained constrained1 @@ -75,6 +77,24 @@ class Constrained { } } + DisplayType getDisplayType() { + if (this.constrained1 != null) { + this.constrained1.getDisplayType() + } else { + null + } + } + + boolean isDisplayInput() { + DisplayType type = getDisplayType() + type == null || type == DisplayType.ALL || type == DisplayType.INPUT + } + + boolean isDisplayOutput() { + DisplayType type = getDisplayType() + type == null || type == DisplayType.ALL || type == DisplayType.OUTPUT + } + boolean isEditable() { if (this.constrained1 != null) { this.constrained1.editable diff --git a/grails-fields/src/test/groovy/org/grails/scaffolding/model/DomainModelServiceSpec.groovy b/grails-fields/src/test/groovy/org/grails/scaffolding/model/DomainModelServiceSpec.groovy index 4eb841bf769..e37d354641f 100644 --- a/grails-fields/src/test/groovy/org/grails/scaffolding/model/DomainModelServiceSpec.groovy +++ b/grails-fields/src/test/groovy/org/grails/scaffolding/model/DomainModelServiceSpec.groovy @@ -215,10 +215,14 @@ class DomainModelServiceSpec extends Specification implements MocksDomain { PersistentProperty persistentProperty2 = Mock(PersistentProperty) DomainProperty bar = Stub(DomainProperty) { getName() >> "bar" - getConstrained() >> Mock(Constrained) { 1 * isDisplay() >> true } + getConstrained() >> Stub(Constrained) { + getDisplayType() >> null + isDisplay() >> true + } } DomainProperty version = Stub(DomainProperty) { getName() >> "version" + getConstrained() >> null } domainModelService.domainPropertyFactory = Mock(DomainPropertyFactoryImpl) { 1 * build(persistentProperty1) >> bar diff --git a/grails-validation/src/main/groovy/grails/validation/ConstrainedDelegate.groovy b/grails-validation/src/main/groovy/grails/validation/ConstrainedDelegate.groovy index 34ec4e29b19..2302814ed02 100644 --- a/grails-validation/src/main/groovy/grails/validation/ConstrainedDelegate.groovy +++ b/grails-validation/src/main/groovy/grails/validation/ConstrainedDelegate.groovy @@ -25,6 +25,7 @@ import org.springframework.validation.Errors import grails.gorm.validation.ConstrainedProperty import grails.gorm.validation.Constraint +import grails.gorm.validation.DisplayType /** * Bridge from the old API to the new @@ -159,6 +160,11 @@ class ConstrainedDelegate implements Constrained, ConstrainedProperty { return property.isDisplay() } + @Override + DisplayType getDisplayType() { + return property.getDisplayType() + } + @Override boolean isEditable() { return property.isEditable() From 4d7a5dc538779e72c4e0ff6d4588101da3593a6f Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Fri, 28 Nov 2025 18:12:06 -0800 Subject: [PATCH 2/4] Change DisplayType.INPUT/OUTPUT to DisplayType.INPUT/OUTPUT_ONLY --- .../grails/gorm/validation/DisplayType.groovy | 9 +++++---- .../gorm/validation/DisplayTypeSpec.groovy | 20 +++++++++---------- .../model/DomainModelServiceImpl.groovy | 12 +++++------ .../model/property/Constrained.groovy | 4 ++-- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy index 6fb27531b96..e7c5d90bc9e 100644 --- a/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy +++ b/grails-datamapping-validation/src/main/groovy/grails/gorm/validation/DisplayType.groovy @@ -26,8 +26,8 @@ package grails.gorm.validation *
    *
  • {@link #ALL} - Display everywhere, including overriding default blacklists (e.g., dateCreated, lastUpdated)
  • *
  • {@link #NONE} - Never display in any view
  • - *
  • {@link #INPUT} - Display only in input forms (create/edit views)
  • - *
  • {@link #OUTPUT} - Display only in output views (show/index views)
  • + *
  • {@link #INPUT_ONLY} - Display only in input forms (create/edit views)
  • + *
  • {@link #OUTPUT_ONLY} - Display only in output views (show/index views)
  • *
* *

Example usage in domain class constraints:

@@ -52,6 +52,7 @@ package grails.gorm.validation *
  • {@code display: false} is equivalent to {@link #NONE}
  • * * + * @author Scott Murphy Heiberg * @since 7.1 */ enum DisplayType { @@ -70,11 +71,11 @@ enum DisplayType { /** * Display the property only in input views (create and edit forms). */ - INPUT, + INPUT_ONLY, /** * Display the property only in output views (show and index/list views). */ - OUTPUT + OUTPUT_ONLY } diff --git a/grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy b/grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy index 9824a398fdd..9f76eeb6819 100644 --- a/grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy +++ b/grails-datamapping-validation/src/test/groovy/grails/gorm/validation/DisplayTypeSpec.groovy @@ -80,22 +80,22 @@ class DisplayTypeSpec extends Specification { constrainedProperty.display == false } - void "test setDisplay with DisplayType.INPUT"() { + void "test setDisplay with DisplayType.INPUT_ONLY"() { when: - constrainedProperty.setDisplay(DisplayType.INPUT) + constrainedProperty.setDisplay(DisplayType.INPUT_ONLY) then: - constrainedProperty.displayType == DisplayType.INPUT - constrainedProperty.display == true // isDisplay still returns true for INPUT + constrainedProperty.displayType == DisplayType.INPUT_ONLY + constrainedProperty.display == true // isDisplay still returns true for INPUT_ONLY } - void "test setDisplay with DisplayType.OUTPUT"() { + void "test setDisplay with DisplayType.OUTPUT_ONLY"() { when: - constrainedProperty.setDisplay(DisplayType.OUTPUT) + constrainedProperty.setDisplay(DisplayType.OUTPUT_ONLY) then: - constrainedProperty.displayType == DisplayType.OUTPUT - constrainedProperty.display == true // isDisplay still returns true for OUTPUT + constrainedProperty.displayType == DisplayType.OUTPUT_ONLY + constrainedProperty.display == true // isDisplay still returns true for OUTPUT_ONLY } void "test setDisplay with null resets to default"() { @@ -138,10 +138,10 @@ class DisplayTypeSpec extends Specification { void "test applyConstraint with DisplayType enum"() { when: - constrainedProperty.applyConstraint("display", DisplayType.INPUT) + constrainedProperty.applyConstraint("display", DisplayType.INPUT_ONLY) then: - constrainedProperty.displayType == DisplayType.INPUT + constrainedProperty.displayType == DisplayType.INPUT_ONLY constrainedProperty.display == true } diff --git a/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy b/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy index 27d1837a635..553aa334226 100644 --- a/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy +++ b/grails-fields/src/main/groovy/org/grails/scaffolding/model/DomainModelServiceImpl.groovy @@ -65,11 +65,11 @@ class DomainModelServiceImpl implements DomainModelService { *

    Retrieves persistent properties and excludes:

      *
    • Any properties listed in the {@code static scaffold = [exclude: []]} property on the domain class *
    • Any properties that have the constraint {@code [display: false]} or {@code [display: DisplayType.NONE]} - *
    • Any properties that have {@code [display: DisplayType.INPUT]} (output views only) + *
    • Any properties that have {@code [display: DisplayType.INPUT_ONLY]} (output views only) *
    • Any properties whose name exist in the blackList *

    * - *

    Properties with {@code [display: DisplayType.ALL]} or {@code [display: DisplayType.OUTPUT]} will override the blacklist.

    + *

    Properties with {@code [display: DisplayType.ALL]} or {@code [display: DisplayType.OUTPUT_ONLY]} will override the blacklist.

    * * @see {@link DomainModelService#getInputProperties} * @param domainClass The persistent entity @@ -95,9 +95,9 @@ class DomainModelServiceImpl implements DomainModelService { Constrained constrained = it.constrained DisplayType displayType = constrained?.displayType - if (displayType == DisplayType.ALL || displayType == DisplayType.OUTPUT) { + if (displayType == DisplayType.ALL || displayType == DisplayType.OUTPUT_ONLY) { // Explicit DisplayType overrides blacklist - } else if (displayType == DisplayType.NONE || displayType == DisplayType.INPUT) { + } else if (displayType == DisplayType.NONE || displayType == DisplayType.INPUT_ONLY) { return true } else { if (it.name in blacklist) { @@ -190,9 +190,9 @@ class DomainModelServiceImpl implements DomainModelService { Constrained constrained = it.constrained DisplayType displayType = constrained?.displayType - if (displayType == DisplayType.ALL || displayType == DisplayType.INPUT) { + if (displayType == DisplayType.ALL || displayType == DisplayType.INPUT_ONLY) { // Explicit DisplayType overrides blacklist - } else if (displayType == DisplayType.NONE || displayType == DisplayType.OUTPUT) { + } else if (displayType == DisplayType.NONE || displayType == DisplayType.OUTPUT_ONLY) { return true } else { if (it.name in blacklist) { diff --git a/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy b/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy index 0abfd228eed..4498781daaa 100644 --- a/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy +++ b/grails-fields/src/main/groovy/org/grails/scaffolding/model/property/Constrained.groovy @@ -87,12 +87,12 @@ class Constrained { boolean isDisplayInput() { DisplayType type = getDisplayType() - type == null || type == DisplayType.ALL || type == DisplayType.INPUT + type == null || type == DisplayType.ALL || type == DisplayType.INPUT_ONLY } boolean isDisplayOutput() { DisplayType type = getDisplayType() - type == null || type == DisplayType.ALL || type == DisplayType.OUTPUT + type == null || type == DisplayType.ALL || type == DisplayType.OUTPUT_ONLY } boolean isEditable() { From 71f7c096ccf6e644f548f0b00d5c21c011fb4de9 Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Sat, 29 Nov 2025 09:51:59 -0800 Subject: [PATCH 3/4] update documentation for display constraint --- grails-doc/src/en/ref/Constraints.adoc | 51 +++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/grails-doc/src/en/ref/Constraints.adoc b/grails-doc/src/en/ref/Constraints.adoc index 405d1653c9a..26dc2d74e90 100644 --- a/grails-doc/src/en/ref/Constraints.adoc +++ b/grails-doc/src/en/ref/Constraints.adoc @@ -176,13 +176,62 @@ Some constraints have no impact on persistence but customize the scaffolding. It |=== |Constraint|Description -|display|Boolean that determines whether the property is displayed in the scaffolding views. If `true` (the default), the property _is_ displayed. +|display|Controls whether and where the property is displayed in scaffolding views. Accepts a boolean or a `DisplayType` enum value (see below). |editable|Boolean that determines whether the property can be edited from the scaffolding views. If `false`, the associated form fields are displayed in read-only mode. |format|Specify a display format for types that accept one, such as dates. For example, 'yyyy-MM-dd'. |password|Boolean indicating whether this is property should be displayed with a password field. Only works on fields that would normally be displayed with a text field. |widget|Controls what widget is used to display the property. For example, 'textarea' will force the scaffolding to use a