From ce3945386d0d3ddfc329749c057478b68b01576e Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 16 Jun 2026 09:51:26 +0800 Subject: [PATCH] Update low-code form docs and clarify sorting guard wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync the form layout examples in docs/en/low-code/model-json.md to the flat fields[]/row/colSpan shape introduced in this PR. Switch the form descriptor schema description from "layout cell" to "layout placement" to match the new vocabulary. Soften the AbpDynamicSortingGuard XML doc and inline comment so they no longer claim the allowed constant-string indexer carries no side effects — the guard cannot enforce that on user-defined indexers, so the doc now states it assumes the matching getter behaves like a property getter. --- docs/en/low-code/model-json.md | 17 ++++++++++------- .../Services/AbpDynamicSortingGuard.cs | 15 +++++++++------ .../definitions/form-descriptor.schema.json | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/en/low-code/model-json.md b/docs/en/low-code/model-json.md index ce59b0b3ec..2c11503553 100644 --- a/docs/en/low-code/model-json.md +++ b/docs/en/low-code/model-json.md @@ -362,9 +362,10 @@ Forms are named definitions referenced by pages through `formName`, `createFormN "id": "details", "title": "Details", "isDefault": true, - "rows": [ - { "cells": [{ "fieldId": "name", "colSpan": 4 }] }, - { "cells": [{ "fieldId": "status", "colSpan": 2 }, { "fieldId": "ownerId", "colSpan": 2 }] } + "fields": [ + { "fieldId": "name", "row": 0, "colSpan": 4 }, + { "fieldId": "status", "row": 1, "colSpan": 2 }, + { "fieldId": "ownerId", "row": 1, "colSpan": 2 } ] } ] @@ -526,10 +527,12 @@ The complete example below shows the logical aggregate shape. Split projects sto "id": "details", "title": "Details", "isDefault": true, - "rows": [ - { "cells": [{ "fieldId": "name", "colSpan": 4 }] }, - { "cells": [{ "fieldId": "status", "colSpan": 2 }, { "fieldId": "budget", "colSpan": 2 }] }, - { "cells": [{ "fieldId": "startDate", "colSpan": 2 }, { "fieldId": "coverImage", "colSpan": 2 }] } + "fields": [ + { "fieldId": "name", "row": 0, "colSpan": 4 }, + { "fieldId": "status", "row": 1, "colSpan": 2 }, + { "fieldId": "budget", "row": 1, "colSpan": 2 }, + { "fieldId": "startDate", "row": 2, "colSpan": 2 }, + { "fieldId": "coverImage", "row": 2, "colSpan": 2 } ] } ] diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs index 25f7f72439..2f67547b0b 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs @@ -16,9 +16,11 @@ namespace Volo.Abp.Application.Services; /// constrained to plain property or field access. Methods, comparisons, ternaries /// and constants in the sort key are rejected with . /// Property-bag / shadow-property access through a constant string indexer -/// (e.g. it["Prop"], Data["Prop"]) is treated as plain property access -/// and allowed, since it carries no side effects and is the canonical way to sort -/// dynamically-mapped entities. +/// (e.g. it["Prop"], Data["Prop"]) is treated like plain property +/// access and allowed, because it is the canonical way to sort dynamically-mapped +/// entities. The guard assumes the matching indexer getter behaves like a property +/// getter; defining an indexer that performs IO or mutates state will expose those +/// effects through sorting. /// internal static class AbpDynamicSortingGuard { @@ -88,9 +90,10 @@ internal static class AbpDynamicSortingGuard protected override Expression VisitMethodCall(MethodCallExpression node) { // Allow property-bag / shadow-property access through a constant string - // indexer (it["Prop"], Data["Prop"], mainEntity["Prop"], ...). This is a - // pure read of a named member, not an arbitrary method invocation, so it - // does not open the injection surface the guard protects against. + // indexer (it["Prop"], Data["Prop"], mainEntity["Prop"], ...). The + // constant key cannot smuggle in an arbitrary method invocation, so this + // does not widen the injection surface the guard protects against; it + // treats the indexer access like ordinary property access. if (IsConstantStringIndexer(node)) { if (node.Object != null) diff --git a/schemas/low-code/definitions/form-descriptor.schema.json b/schemas/low-code/definitions/form-descriptor.schema.json index 3508ff58a9..102193ee10 100644 --- a/schemas/low-code/definitions/form-descriptor.schema.json +++ b/schemas/low-code/definitions/form-descriptor.schema.json @@ -34,7 +34,7 @@ }, "layout": { "$ref": "form-layout-descriptor.schema.json", - "description": "Visual layout for the fields. Every layout cell fieldId must refer to a field in fields." + "description": "Visual layout for the fields. Every layout placement fieldId must refer to a field in fields." }, "rules": { "type": "array",