From 87e64ab0d089c013154c551d30895758a4be925d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?REBUILD=20=E4=BC=81=E4=B8=9A=E7=AE=A1=E7=90=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= <42044143+getrebuild@users.noreply.github.com> Date: Sat, 25 Nov 2023 22:32:45 +0800 Subject: [PATCH] Fix 3.5.0 beta2 (#683) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 表单引用支持任意引用 2. 分组关联触发器设置 (LAB),必填,格式验证 3. ANYREF 允许重复选项 4. 去重连接*N * export fields * refform 3 * be: validAdvFilter * fix: GroupAggregationRefresh fake源记录 * be: escapeSql --- @rbv | 2 +- .../service/dashboard/charts/ChartData.java | 19 ++--- .../service/dataimport/RecordCheckout.java | 4 +- .../core/service/query/AdvFilterParser.java | 12 ++++ .../service/query/FilterRecordChecker.java | 5 +- .../trigger/impl/FieldAggregation.java | 3 +- .../trigger/impl/FieldWritebackRefresh.java | 3 +- .../trigger/impl/GroupAggregation.java | 15 ++-- .../trigger/impl/GroupAggregationRefresh.java | 72 +++++++------------ .../trigger/impl/TargetWithMatchFields.java | 9 +++ .../com/rebuild/core/support/ShortUrls.java | 4 +- .../core/support/general/QueryParser.java | 17 +++-- .../java/com/rebuild/utils/CommonsUtils.java | 6 +- .../web/admin/ApisManagerController.java | 4 +- .../web/general/ListAndViewRedirection.java | 12 ++-- .../web/project/ProjectTaskController.java | 3 +- .../trigger/FieldAggregationController.java | 3 +- .../trigger/FieldWritebackController.java | 4 +- .../trigger/GroupAggregationController.java | 2 +- src/main/resources/web/assets/css/rb-page.css | 8 ++- .../web/assets/js/metadata/field-edit.js | 2 +- .../web/assets/js/metadata/fields.js | 5 +- .../web/assets/js/metadata/form-design.js | 3 +- src/main/resources/web/assets/js/rb-forms.js | 17 +++-- src/main/resources/web/assets/js/rb-page.js | 8 +-- src/main/resources/web/assets/js/rb-view.js | 2 +- .../js/trigger/trigger.FIELDAGGREGATION.js | 20 +++++- .../js/trigger/trigger.FIELDWRITEBACK.js | 18 ++++- .../js/trigger/trigger.GROUPAGGREGATION.js | 2 +- 29 files changed, 174 insertions(+), 110 deletions(-) diff --git a/@rbv b/@rbv index 15d7f439a..222f88ffd 160000 --- a/@rbv +++ b/@rbv @@ -1 +1 @@ -Subproject commit 15d7f439a84cef4be39dadada2d51e2e6ab4f375 +Subproject commit 222f88ffd578ce0a87622e472a855dc061ae97a7 diff --git a/src/main/java/com/rebuild/core/service/dashboard/charts/ChartData.java b/src/main/java/com/rebuild/core/service/dashboard/charts/ChartData.java index d3565eef3..262774914 100644 --- a/src/main/java/com/rebuild/core/service/dashboard/charts/ChartData.java +++ b/src/main/java/com/rebuild/core/service/dashboard/charts/ChartData.java @@ -27,6 +27,7 @@ import com.rebuild.core.metadata.impl.EasyFieldConfigProps; import com.rebuild.core.privileges.UserHelper; import com.rebuild.core.privileges.UserService; import com.rebuild.core.service.query.AdvFilterParser; +import com.rebuild.core.service.query.ParseHelper; import com.rebuild.core.support.SetUser; import com.rebuild.core.support.general.FieldValueHelper; import com.rebuild.core.support.i18n.Language; @@ -217,17 +218,17 @@ public abstract class ChartData extends SetUser implements ChartSpec { } } - JSONObject filterExp = config.getJSONObject("filter"); - if (filterExp == null || filterExp.isEmpty()) { - return previewFilter + "(1=1)"; + JSONObject filterExpr = config.getJSONObject("filter"); + if (ParseHelper.validAdvFilter(filterExpr)) { + AdvFilterParser filterParser = new AdvFilterParser(filterExpr); + String sqlWhere = filterParser.toSqlWhere(); + if (sqlWhere != null) { + sqlWhere = previewFilter + sqlWhere; + } + return StringUtils.defaultIfBlank(sqlWhere, "(1=1)"); } - AdvFilterParser filterParser = new AdvFilterParser(filterExp); - String sqlWhere = filterParser.toSqlWhere(); - if (sqlWhere != null) { - sqlWhere = previewFilter + sqlWhere; - } - return StringUtils.defaultIfBlank(sqlWhere, "(1=1)"); + return previewFilter + "(1=1)"; } /** diff --git a/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java b/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java index 9a8682d1f..042ce6844 100644 --- a/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java +++ b/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java @@ -231,7 +231,7 @@ public class RecordCheckout { if (refEntity.getEntityCode() == EntityHelper.User) { String sql = MessageFormat.format( "select userId from User where loginName = ''{0}'' or email = ''{0}'' or fullName = ''{0}''", - CommonsUtils.escapeSql(val2Text.toString())); + CommonsUtils.escapeSql(val2Text)); query = Application.createQueryNoFilter(sql); } else { // 查找引用实体的名称字段和自动编号字段 @@ -248,7 +248,7 @@ public class RecordCheckout { String.format("select %s from %s where ", refEntity.getPrimaryField().getName(), refEntity.getName())); for (String qf : queryFields) { sql.append( - String.format("%s = '%s' or ", qf, CommonsUtils.escapeSql(val2Text.toString()))); + String.format("%s = '%s' or ", qf, CommonsUtils.escapeSql(val2Text))); } sql = new StringBuilder(sql.substring(0, sql.length() - 4)); diff --git a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java index f5f742791..edeb6b274 100644 --- a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java +++ b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java @@ -105,6 +105,12 @@ public class AdvFilterParser extends SetUser { this.filterExpr = filterExpr; this.rootEntity = rootEntity; this.varRecord = null; + + String entityName = filterExpr.getString("entity"); + if (entityName != null) { + Assert.isTrue(entityName.equalsIgnoreCase(this.rootEntity.getName()), + "Filter#2 uses different entities : " + entityName + "/" + this.rootEntity.getName()); + } } /** @@ -115,6 +121,12 @@ public class AdvFilterParser extends SetUser { this.filterExpr = filterExpr; this.rootEntity = MetadataHelper.getEntity(varRecord.getEntityCode()); this.varRecord = License.isRbvAttached() ? varRecord : null; + + String entityName = filterExpr.getString("entity"); + if (entityName != null) { + Assert.isTrue(entityName.equalsIgnoreCase(this.rootEntity.getName()), + "Filter#3 uses different entities : " + entityName + "/" + this.rootEntity.getName()); + } } /** diff --git a/src/main/java/com/rebuild/core/service/query/FilterRecordChecker.java b/src/main/java/com/rebuild/core/service/query/FilterRecordChecker.java index 333f00a9e..e5f8471d5 100644 --- a/src/main/java/com/rebuild/core/service/query/FilterRecordChecker.java +++ b/src/main/java/com/rebuild/core/service/query/FilterRecordChecker.java @@ -32,10 +32,7 @@ public class FilterRecordChecker { * @return */ public boolean check(ID recordId) { - if (filterExpr == null || filterExpr.isEmpty() - || filterExpr.getJSONArray("items") == null || filterExpr.getJSONArray("items").isEmpty()) { - return true; - } + if (!ParseHelper.validAdvFilter(filterExpr)) return true; Entity entity = MetadataHelper.getEntity(recordId.getEntityCode()); diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java b/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java index 632a2baab..7f03fbc7b 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java @@ -29,6 +29,7 @@ import com.rebuild.core.service.general.GeneralEntityServiceContextHolder; import com.rebuild.core.service.general.OperatingContext; import com.rebuild.core.service.general.RecordDifference; import com.rebuild.core.service.query.AdvFilterParser; +import com.rebuild.core.service.query.ParseHelper; import com.rebuild.core.service.query.QueryHelper; import com.rebuild.core.service.trigger.ActionContext; import com.rebuild.core.service.trigger.ActionType; @@ -161,7 +162,7 @@ public class FieldAggregation extends TriggerAction { // 聚合数据过滤 JSONObject dataFilter = ((JSONObject) actionContext.getActionContent()).getJSONObject("dataFilter"); String dataFilterSql = null; - if (dataFilter != null && !dataFilter.isEmpty()) { + if (ParseHelper.validAdvFilter(dataFilter)) { dataFilterSql = new AdvFilterParser(dataFilter, operatingContext.getFixedRecordId()).toSqlWhere(); } diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/FieldWritebackRefresh.java b/src/main/java/com/rebuild/core/service/trigger/impl/FieldWritebackRefresh.java index afda2578d..ee5374fb7 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/FieldWritebackRefresh.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/FieldWritebackRefresh.java @@ -50,7 +50,8 @@ public class FieldWritebackRefresh { fa.targetEntity = parent.targetEntity; fa.targetRecordIds = Collections.singleton(beforeValue); - Record fakeSourceRecord = EntityHelper.forUpdate(EntityHelper.newUnsavedId(fa.sourceEntity.getEntityCode()), triggerUser, false); + ID fakeSourceId = EntityHelper.newUnsavedId(fa.sourceEntity.getEntityCode()); + Record fakeSourceRecord = EntityHelper.forUpdate(fakeSourceId, triggerUser, false); OperatingContext oCtx = OperatingContext.create(triggerUser, BizzPermission.NONE, fakeSourceRecord, fakeSourceRecord); fa.targetRecordData = fa.buildTargetRecordData(oCtx, true); diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java b/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java index e29d52a8a..43710600c 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java @@ -29,6 +29,7 @@ import com.rebuild.core.service.trigger.ActionContext; import com.rebuild.core.service.trigger.ActionType; import com.rebuild.core.service.trigger.TriggerException; import com.rebuild.core.support.i18n.Language; +import com.rebuild.utils.CommonsUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.util.Assert; @@ -223,8 +224,8 @@ public class GroupAggregation extends FieldAggregation { } } - qFields.add(String.format("%s = '%s'", targetField, val)); - qFieldsFollow.add(String.format("%s = '%s'", sourceField, val)); + qFields.add(String.format("%s = '%s'", targetField, CommonsUtils.escapeSql(val))); + qFieldsFollow.add(String.format("%s = '%s'", sourceField, CommonsUtils.escapeSql(val))); allNull = false; } @@ -232,11 +233,11 @@ public class GroupAggregation extends FieldAggregation { } if (allNull) { - // 如果分组字段值全空将会触发全量更新 - if (!valueChanged.isEmpty()) { - this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh); - } else { + if (valueChanged.isEmpty()) { log.warn("All values of group-fields are null, ignored"); + } else { + // 如果分组字段值全空将会触发全量更新 + this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh, operatingContext.getFixedRecordId()); } return; } @@ -244,7 +245,7 @@ public class GroupAggregation extends FieldAggregation { this.followSourceWhere = StringUtils.join(qFieldsFollow.iterator(), " and "); if (isGroupUpdate) { - this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh); + this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh, operatingContext.getFixedRecordId()); } sql = String.format("select %s from %s where ( %s )", diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregationRefresh.java b/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregationRefresh.java index 63b67ab5d..9a6dcbdf5 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregationRefresh.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregationRefresh.java @@ -16,10 +16,14 @@ import com.rebuild.core.metadata.EntityHelper; import com.rebuild.core.privileges.UserService; import com.rebuild.core.service.general.OperatingContext; import com.rebuild.core.service.trigger.ActionContext; +import com.rebuild.utils.CommonsUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * 分组聚合目标数据刷新 @@ -39,15 +43,17 @@ public class GroupAggregationRefresh { final private GroupAggregation parent; final private List fieldsRefresh; + final private ID originSourceId; // fieldsRefresh = [TargetField, SourceField, Value] - protected GroupAggregationRefresh(GroupAggregation parent, List fieldsRefresh) { + protected GroupAggregationRefresh(GroupAggregation parent, List fieldsRefresh, ID originSourceId) { this.parent = parent; this.fieldsRefresh = fieldsRefresh; + this.originSourceId = originSourceId; } /** - * TODO 存在性能问题(可能刷新过多) + * NOTE 存在性能问题,因为可能刷新过多 */ public void refresh() { List targetFields = new ArrayList<>(); @@ -55,79 +61,52 @@ public class GroupAggregationRefresh { for (String[] s : fieldsRefresh) { targetFields.add(s[0]); if (s[2] != null) { - targetWhere.add(String.format("%s = '%s'", s[0], s[2])); + targetWhere.add(String.format("%s = '%s'", s[0], CommonsUtils.escapeSql(s[2]))); } } - // 全量刷新,性能较低 + // 只有1个字段会全量刷新,性能较低 if (targetWhere.size() <= 1) { targetWhere.clear(); targetWhere.add("(1=1)"); log.warn("Force refresh all aggregation target(s)"); } - Entity entity = this.parent.targetEntity; + // 1.获取待刷新的目标 + Entity targetEntity = this.parent.targetEntity; String sql = String.format("select %s,%s from %s where ( %s )", StringUtils.join(targetFields, ","), - entity.getPrimaryField().getName(), - entity.getName(), + targetEntity.getPrimaryField().getName(), + targetEntity.getName(), StringUtils.join(targetWhere, " or ")); - Object[][] targetArray = Application.createQueryNoFilter(sql).array(); - log.info("Maybe refresh target record(s) : {}", targetArray.length); + Object[][] targetRecords4Refresh = Application.createQueryNoFilter(sql).array(); + log.info("Maybe refresh target record(s) : {}", targetRecords4Refresh.length); ID triggerUser = UserService.SYSTEM_USER; ActionContext parentAc = parent.getActionContext(); // 避免重复的无意义更新 - // NOTE 220905 不能忽略触发源本身 + // NOTE 220905 更新时不能忽略触发源本身的更新 Set refreshedIds = new HashSet<>(); - for (Object[] o : targetArray) { + // 2.逐一刷新目标 + for (Object[] o : targetRecords4Refresh) { final ID targetRecordId = (ID) o[o.length - 1]; + // 排重 + if (refreshedIds.contains(targetRecordId)) continue; + else refreshedIds.add(targetRecordId); List qFieldsFollow = new ArrayList<>(); - List qFieldsFollow2 = new ArrayList<>(); for (int i = 0; i < o.length - 1; i++) { String[] source = fieldsRefresh.get(i); if (o[i] == null) { qFieldsFollow.add(String.format("%s is null", source[1])); } else { - qFieldsFollow.add(String.format("%s = '%s'", source[1], o[i])); - qFieldsFollow2.add(String.format("%s = '%s'", source[1], o[i])); + qFieldsFollow.add(String.format("%s = '%s'", source[1], CommonsUtils.escapeSql(o[i]))); } } - ID fakeUpdateReferenceId = null; - - // 1.尝试获取触发源 - for (int i = 0; i < o.length - 1; i++) { - Object mayId = o[i]; - if (ID.isId(mayId) && ((ID) mayId).getEntityCode() > 100) { - fakeUpdateReferenceId = (ID) mayId; - break; - } - } - // 2.强制获取 - if (fakeUpdateReferenceId == null) { - sql = String.format("select %s from %s where %s", - parent.sourceEntity.getPrimaryField().getName(), - parent.sourceEntity.getName(), - StringUtils.join(qFieldsFollow2, " or ")); - Object[] found = Application.getQueryFactory().unique(sql); - fakeUpdateReferenceId = found == null ? null : (ID) found[0]; - } else { - - // 1.1.排重 - if (refreshedIds.contains(targetRecordId)) continue; - else refreshedIds.add(fakeUpdateReferenceId); - } - - if (fakeUpdateReferenceId == null) { - log.warn("No any source-id found, ignored : {}", Arrays.toString(o)); - continue; - } - ActionContext actionContext = new ActionContext(null, parentAc.getSourceEntity(), parentAc.getActionContent(), parentAc.getConfigId()); @@ -137,7 +116,8 @@ public class GroupAggregationRefresh { ga.targetRecordId = targetRecordId; ga.followSourceWhere = StringUtils.join(qFieldsFollow, " and "); - Record fakeSourceRecord = EntityHelper.forUpdate(fakeUpdateReferenceId, triggerUser, false); + // FIXME v35 可能导致数据聚合条件中的字段变量不准 + Record fakeSourceRecord = EntityHelper.forUpdate(originSourceId, triggerUser, false); OperatingContext oCtx = OperatingContext.create(triggerUser, BizzPermission.NONE, fakeSourceRecord, fakeSourceRecord); try { diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/TargetWithMatchFields.java b/src/main/java/com/rebuild/core/service/trigger/impl/TargetWithMatchFields.java index 6431489f4..00c22b89f 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/TargetWithMatchFields.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/TargetWithMatchFields.java @@ -20,8 +20,10 @@ import com.rebuild.core.metadata.easymeta.DisplayType; import com.rebuild.core.metadata.easymeta.EasyField; import com.rebuild.core.metadata.easymeta.EasyMetaFactory; import com.rebuild.core.metadata.impl.EasyFieldConfigProps; +import com.rebuild.core.service.general.OperatingContext; import com.rebuild.core.service.trigger.ActionContext; import com.rebuild.core.support.i18n.Language; +import com.rebuild.utils.CommonsUtils; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; @@ -70,6 +72,12 @@ public class TargetWithMatchFields { return o == null ? new ID[0] : (ID[]) o; } + /** + * @param actionContext + * @param m + * @return + * @see GroupAggregation#prepare(OperatingContext) + */ private Object match(ActionContext actionContext, boolean m) { if (sourceEntity != null) return targetRecordId; // 已做匹配 @@ -186,6 +194,7 @@ public class TargetWithMatchFields { } } + val = CommonsUtils.escapeSql(val); qFields.add(String.format("%s = '%s'", targetField, val)); qFieldsFollow.add(String.format("%s = '%s'", sourceField, val)); allNull = false; diff --git a/src/main/java/com/rebuild/core/support/ShortUrls.java b/src/main/java/com/rebuild/core/support/ShortUrls.java index 66d2e7df9..d2a1698d4 100644 --- a/src/main/java/com/rebuild/core/support/ShortUrls.java +++ b/src/main/java/com/rebuild/core/support/ShortUrls.java @@ -14,8 +14,8 @@ import cn.devezhao.persist4j.engine.ID; import com.rebuild.core.Application; import com.rebuild.core.metadata.EntityHelper; import com.rebuild.core.privileges.UserService; +import com.rebuild.utils.CommonsUtils; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang3.ObjectUtils; import java.util.Calendar; @@ -63,7 +63,7 @@ public class ShortUrls { */ public static boolean invalid(String shortKey) { String dsql = String.format( - "delete from `short_url` where SHORT_KEY = '%s'", StringEscapeUtils.escapeSql(shortKey)); + "delete from `short_url` where SHORT_KEY = '%s'", CommonsUtils.escapeSql(shortKey)); int a = Application.getSqlExecutor().execute(dsql); return a > 0; } diff --git a/src/main/java/com/rebuild/core/support/general/QueryParser.java b/src/main/java/com/rebuild/core/support/general/QueryParser.java index 346009f06..a92b7c0d8 100644 --- a/src/main/java/com/rebuild/core/support/general/QueryParser.java +++ b/src/main/java/com/rebuild/core/support/general/QueryParser.java @@ -18,11 +18,18 @@ import com.rebuild.core.metadata.EntityHelper; import com.rebuild.core.metadata.MetadataHelper; import com.rebuild.core.privileges.UserService; import com.rebuild.core.service.query.AdvFilterParser; +import com.rebuild.core.service.query.ParseHelper; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * 列表查询解析 @@ -200,13 +207,13 @@ public class QueryParser { // append: QuickQuery JSONObject quickFilter = queryExpr.getJSONObject("filter"); - if (quickFilter != null) { + if (ParseHelper.validAdvFilter(quickFilter)) { String where = new AdvFilterParser(quickFilter, entity).toSqlWhere(); if (StringUtils.isNotBlank(where)) wheres.add(where); } // v3.3 JSONObject quickFilterAnd = queryExpr.getJSONObject("filterAnd"); - if (quickFilterAnd != null) { + if (ParseHelper.validAdvFilter(quickFilterAnd)) { String where = new AdvFilterParser(quickFilterAnd, entity).toSqlWhere(); if (StringUtils.isNotBlank(where)) wheres.add(where); } @@ -270,7 +277,9 @@ public class QueryParser { ConfigBean advFilter = AdvFilterManager.instance.getAdvFilter(filterId); if (advFilter != null) { JSONObject filterExpr = (JSONObject) advFilter.getJSON("filter"); - return new AdvFilterParser(filterExpr, entity).toSqlWhere(); + if (ParseHelper.validAdvFilter(filterExpr)) { + return new AdvFilterParser(filterExpr, entity).toSqlWhere(); + } } return null; } diff --git a/src/main/java/com/rebuild/utils/CommonsUtils.java b/src/main/java/com/rebuild/utils/CommonsUtils.java index ca96a3496..643b8523a 100644 --- a/src/main/java/com/rebuild/utils/CommonsUtils.java +++ b/src/main/java/com/rebuild/utils/CommonsUtils.java @@ -110,10 +110,10 @@ public class CommonsUtils { * @return * @see StringEscapeUtils#escapeSql(String) */ - public static String escapeSql(String text) { + public static String escapeSql(Object text) { // https://github.com/getrebuild/rebuild/issues/594 - text = text.replace("\\'", "'"); - return StringEscapeUtils.escapeSql(text); + text = text.toString().replace("\\'", "'"); + return StringEscapeUtils.escapeSql((String) text); } /** diff --git a/src/main/java/com/rebuild/web/admin/ApisManagerController.java b/src/main/java/com/rebuild/web/admin/ApisManagerController.java index 8ef53aa3d..22df9cb7b 100644 --- a/src/main/java/com/rebuild/web/admin/ApisManagerController.java +++ b/src/main/java/com/rebuild/web/admin/ApisManagerController.java @@ -18,8 +18,8 @@ import com.rebuild.core.Application; import com.rebuild.core.configuration.RebuildApiService; import com.rebuild.core.metadata.EntityHelper; import com.rebuild.core.support.i18n.I18nUtils; +import com.rebuild.utils.CommonsUtils; import com.rebuild.web.BaseController; -import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -88,7 +88,7 @@ public class ApisManagerController extends BaseController { String sql = "select remoteIp,requestTime,responseTime,requestUrl,requestBody,responseBody,requestId from RebuildApiRequest" + " where appId = ? and requestTime > ? and (1=1) order by requestTime desc"; if (StringUtils.isNotBlank(q)) { - q = StringEscapeUtils.escapeSql(q); + q = CommonsUtils.escapeSql(q); sql = sql.replace("(1=1)", String.format("(requestBody like '%%%s%%' or responseBody like '%%%s%%')", q, q)); } diff --git a/src/main/java/com/rebuild/web/general/ListAndViewRedirection.java b/src/main/java/com/rebuild/web/general/ListAndViewRedirection.java index a342243ad..e5a8faf68 100644 --- a/src/main/java/com/rebuild/web/general/ListAndViewRedirection.java +++ b/src/main/java/com/rebuild/web/general/ListAndViewRedirection.java @@ -56,13 +56,17 @@ public class ListAndViewRedirection extends BaseController { || entity.getEntityCode() == EntityHelper.ProjectTaskComment) { Object[] found = findProjectAndTaskId(anyId); if (found != null) { - url = MessageFormat.format( - "../project/{0}/tasks#!/View/ProjectTask/{1}", found[1], found[0]); + url = MessageFormat.format("../project/{0}/tasks#!/View/ProjectTask/{1}", found[1], found[0]); } } else if (entity.getEntityCode() == EntityHelper.User) { - url = MessageFormat.format( - "../admin/bizuser/users#!/View/{0}/{1}", entity.getName(), anyId); + url = MessageFormat.format("../admin/bizuser/users#!/View/{0}/{1}", entity.getName(), anyId); + } else if (entity.getEntityCode() == EntityHelper.Department) { + url = MessageFormat.format("../admin/bizuser/departments#!/View/{0}/{1}", entity.getName(), anyId); + } else if (entity.getEntityCode() == EntityHelper.Team) { + url = MessageFormat.format("../admin/bizuser/teams#!/View/{0}/{1}", entity.getName(), anyId); + } else if (entity.getEntityCode() == EntityHelper.Role) { + url = MessageFormat.format("../admin/bizuser/role/{0}", anyId); } else if (MetadataHelper.isBusinessEntity(entity)) { if ("dock".equalsIgnoreCase(type)) { diff --git a/src/main/java/com/rebuild/web/project/ProjectTaskController.java b/src/main/java/com/rebuild/web/project/ProjectTaskController.java index 0bc42eb37..c21b9e599 100644 --- a/src/main/java/com/rebuild/web/project/ProjectTaskController.java +++ b/src/main/java/com/rebuild/web/project/ProjectTaskController.java @@ -24,6 +24,7 @@ import com.rebuild.core.service.NoRecordFoundException; import com.rebuild.core.service.project.ProjectHelper; import com.rebuild.core.service.project.ProjectManager; import com.rebuild.core.service.query.AdvFilterParser; +import com.rebuild.core.service.query.ParseHelper; import com.rebuild.core.support.general.FieldValueHelper; import com.rebuild.core.support.i18n.I18nUtils; import com.rebuild.core.support.i18n.Language; @@ -106,7 +107,7 @@ public class ProjectTaskController extends BaseController { // 高级查询 JSON advFilter = ServletUtils.getRequestJson(request); - if (advFilter != null) { + if (ParseHelper.validAdvFilter((JSONObject) advFilter)) { String filterSql = new AdvFilterParser((JSONObject) advFilter).toSqlWhere(); if (filterSql != null) { queryWhere += " and (" + filterSql + ")"; diff --git a/src/main/java/com/rebuild/web/robot/trigger/FieldAggregationController.java b/src/main/java/com/rebuild/web/robot/trigger/FieldAggregationController.java index 9de21592e..0c2bb5374 100644 --- a/src/main/java/com/rebuild/web/robot/trigger/FieldAggregationController.java +++ b/src/main/java/com/rebuild/web/robot/trigger/FieldAggregationController.java @@ -84,7 +84,7 @@ public class FieldAggregationController extends BaseController { // 源字段 - JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, field -> { + JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, true, field -> { if (field instanceof EasyField) { EasyField easyField = (EasyField) field; return !easyField.isQueryable() || easyField.getDisplayType() == DisplayType.BARCODE; @@ -104,6 +104,7 @@ public class FieldAggregationController extends BaseController { for (Field field : MetadataSorter.sortFields(targetEntity, DisplayType.NUMBER, DisplayType.DECIMAL, DisplayType.DATE, DisplayType.DATETIME, DisplayType.N2NREFERENCE, DisplayType.NTEXT, DisplayType.FILE)) { + EasyField easyField = EasyMetaFactory.valueOf(field); if (easyField.isBuiltin()) continue; diff --git a/src/main/java/com/rebuild/web/robot/trigger/FieldWritebackController.java b/src/main/java/com/rebuild/web/robot/trigger/FieldWritebackController.java index a40c02efb..4e6c00983 100644 --- a/src/main/java/com/rebuild/web/robot/trigger/FieldWritebackController.java +++ b/src/main/java/com/rebuild/web/robot/trigger/FieldWritebackController.java @@ -117,7 +117,7 @@ public class FieldWritebackController extends BaseController { // 源字段 - JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, field -> { + JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, true, field -> { if (field instanceof EasyField) { EasyField easyField = (EasyField) field; return easyField.getDisplayType() == DisplayType.BARCODE; @@ -134,7 +134,7 @@ public class FieldWritebackController extends BaseController { JSONArray targetFields = new JSONArray(); if (targetEntity != null) { - targetFields = MetaFormatter.buildFieldsWithRefs(targetEntity, 1, field -> { + targetFields = MetaFormatter.buildFieldsWithRefs(targetEntity, 1, true, field -> { EasyField easyField = (EasyField) field; return easyField.getDisplayType() == DisplayType.SERIES || easyField.getDisplayType() == DisplayType.BARCODE diff --git a/src/main/java/com/rebuild/web/robot/trigger/GroupAggregationController.java b/src/main/java/com/rebuild/web/robot/trigger/GroupAggregationController.java index c874d625d..f65a49000 100644 --- a/src/main/java/com/rebuild/web/robot/trigger/GroupAggregationController.java +++ b/src/main/java/com/rebuild/web/robot/trigger/GroupAggregationController.java @@ -54,7 +54,7 @@ public class GroupAggregationController extends BaseController { paddingType2(sourceGroupFields, sourceEntity); // 源-聚合字段 - JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, field -> { + JSONArray sourceFields = MetaFormatter.buildFieldsWithRefs(sourceEntity, 3, true, field -> { if (field instanceof EasyField) { EasyField easyField = (EasyField) field; return !easyField.isQueryable() || easyField.getDisplayType() == DisplayType.BARCODE; diff --git a/src/main/resources/web/assets/css/rb-page.css b/src/main/resources/web/assets/css/rb-page.css index b06f223ec..bf4a26f24 100644 --- a/src/main/resources/web/assets/css/rb-page.css +++ b/src/main/resources/web/assets/css/rb-page.css @@ -2177,6 +2177,10 @@ th.column-fixed { content: '\f2fb'; } +.form-line.hover fieldset legend > span { + margin-left: 3px; +} + .form-line.v33 { background-color: #ebebeb; border-radius: 2px; @@ -5014,7 +5018,7 @@ pre.unstyle { .tablesort thead th.sorted.ascending::after, .tablesort thead th.sorted.descending::after { - font: normal normal normal 18px/1 "Material Design Icons"; + font: normal normal normal 18px/1 'Material Design Icons'; content: '\F0140'; position: absolute; right: 10px; @@ -5377,4 +5381,4 @@ div.dataTables_wrapper.compact div.dataTables_oper .btn-space { .dataTables_oper.invisible2 > .btn.J_view { display: inline-block; -} \ No newline at end of file +} diff --git a/src/main/resources/web/assets/js/metadata/field-edit.js b/src/main/resources/web/assets/js/metadata/field-edit.js index 468c4887e..eea034390 100644 --- a/src/main/resources/web/assets/js/metadata/field-edit.js +++ b/src/main/resources/web/assets/js/metadata/field-edit.js @@ -9,7 +9,7 @@ See LICENSE and COMMERCIAL in the project root for license information. const wpc = window.__PageConfig const __gExtConfig = {} -const SHOW_REPEATABLE = ['TEXT', 'DATE', 'EMAIL', 'URL', 'PHONE', 'REFERENCE', 'CLASSIFICATION'] +const SHOW_REPEATABLE = ['TEXT', 'DATE', 'EMAIL', 'URL', 'PHONE', 'REFERENCE', 'CLASSIFICATION', 'ANYREFERENCE'] const SHOW_DEFAULTVALUE = ['TEXT', 'NTEXT', 'EMAIL', 'PHONE', 'URL', 'NUMBER', 'DECIMAL', 'DATETIME', 'DATE', 'TIME', 'BOOL', 'CLASSIFICATION', 'REFERENCE', 'N2NREFERENCE'] const SHOW_ADVDESENSITIZED = ['TEXT', 'PHONE', 'EMAIL', 'NUMBER', 'DECIMAL'] const SHOW_ADVPATTERN = ['TEXT'] diff --git a/src/main/resources/web/assets/js/metadata/fields.js b/src/main/resources/web/assets/js/metadata/fields.js index c7b0a1932..a0c04b862 100644 --- a/src/main/resources/web/assets/js/metadata/fields.js +++ b/src/main/resources/web/assets/js/metadata/fields.js @@ -67,11 +67,12 @@ const renderList = function () { } function exportFields() { - const rows = [[$L('内部标识'), $L('字段名称'), $L('类型'), $L('必填'), $L('备注')].join(', ')] + const rows = [[$L('内部标识'), $L('字段名称'), $L('类型'), $L('必填'), $L('只读'), $L('备注')].join(', ')] + rows.push([`${wpc.entityName}Id`, $L('主键'), 'ID', 'N', 'Y', ''].join(', ')) fields_data.forEach((item) => { let type = item.displayType if (item.displayTypeRef) type += ` (${item.displayTypeRef[1]})` - rows.push([item.fieldName, item.fieldLabel, type, item.nullable ? 'N' : 'Y', item.comments ? item.comments.replace(/[,;]/, ' ') : ''].join(', ')) + rows.push([item.fieldName, item.fieldLabel, type, item.nullable ? 'N' : 'Y', item.creatable ? 'N' : 'Y', item.comments ? item.comments.replace(/[,;]/, ' ') : ''].join(', ')) }) const encodedUri = encodeURI('data:text/csv;charset=utf-8,\ufeff' + rows.join('\n')) diff --git a/src/main/resources/web/assets/js/metadata/form-design.js b/src/main/resources/web/assets/js/metadata/form-design.js index 563c2b60c..bc8839b0a 100644 --- a/src/main/resources/web/assets/js/metadata/form-design.js +++ b/src/main/resources/web/assets/js/metadata/form-design.js @@ -514,12 +514,13 @@ class DlgEditRefform extends DlgEditField { {Object.keys(_ValidFields).map((k) => { const field = _ValidFields[k] - if (field.displayTypeName === 'REFERENCE' && !(field.fieldName === 'approvalId')) + if (['REFERENCE', 'ANYREFERENCE'].includes(field.displayTypeName) && field.fieldName !== 'approvalId') { return ( ) + } return null })} diff --git a/src/main/resources/web/assets/js/rb-forms.js b/src/main/resources/web/assets/js/rb-forms.js index 1476775a5..66d54bbea 100644 --- a/src/main/resources/web/assets/js/rb-forms.js +++ b/src/main/resources/web/assets/js/rb-forms.js @@ -2765,7 +2765,7 @@ class RbFormDivider extends React.Component {
(this._$formLine = c)}>
this._toggle()} title={$L('展开/收起')}> - {this.props.label || ''} + {this.props.label && {this.props.label}}
@@ -2805,8 +2805,11 @@ class RbFormRefform extends React.Component { const $$$parent = this.props.$$$parent if ($$$parent && $$$parent.__ViewData && $$$parent.__ViewData[this.props.reffield]) { - const s = $$$parent.__ViewData[this.props.reffield] - this._renderViewFrom({ ...s }) + // 避免循环嵌套死循环 + if (($$$parent.__nestDepth || 0) < 3) { + const s = $$$parent.__ViewData[this.props.reffield] + this._renderViewFrom({ ...s }) + } } } @@ -2819,6 +2822,10 @@ class RbFormRefform extends React.Component { return } + // 支持嵌套 + this.__ViewData = {} + this.__nestDepth = (this.props.$$$parent.__nestDepth || 0) + 1 + const VFORM = ( @@ -2826,6 +2833,7 @@ class RbFormRefform extends React.Component {
{res.data.elements.map((item) => { + if (![TYPE_DIVIDER, TYPE_REFFORM].includes(item.field)) this.__ViewData[item.field] = item.value item.$$$parent = this // eslint-disable-next-line no-undef return detectViewElement(item, props.entity) @@ -2840,7 +2848,7 @@ class RbFormRefform extends React.Component { // 确定元素类型 var detectElement = function (item, entity) { - if (!item.key) item.key = `field-${item.field === TYPE_DIVIDER ? $random() : item.field}` + if (!item.key) item.key = `field-${item.field === TYPE_DIVIDER || item.field === TYPE_REFFORM ? $random() : item.field}` if (entity && window._CustomizedForms) { const c = window._CustomizedForms.useFormElement(entity, item) @@ -2898,6 +2906,7 @@ var detectElement = function (item, entity) { } else if (item.field === TYPE_DIVIDER || item.field === '$LINE$') { return } else if (item.field === TYPE_REFFORM) { + console.log(item) return } else { return diff --git a/src/main/resources/web/assets/js/rb-page.js b/src/main/resources/web/assets/js/rb-page.js index 9bf9a9c8a..c87586b56 100644 --- a/src/main/resources/web/assets/js/rb-page.js +++ b/src/main/resources/web/assets/js/rb-page.js @@ -8,8 +8,8 @@ See LICENSE and COMMERCIAL in the project root for license information. /* !!! KEEP IT ES5 COMPATIBLE !!! */ // GA -;(function () { - const gaScript = document.createElement('script') +(function () { + var gaScript = document.createElement('script') gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-ZCZHJPMEG7' gaScript.async = true gaScript.onload = function () { @@ -20,7 +20,7 @@ See LICENSE and COMMERCIAL in the project root for license information. gtag('js', new Date()) gtag('config', 'G-ZCZHJPMEG7') } - const s = document.getElementsByTagName('script')[0] + var s = document.getElementsByTagName('script')[0] s.parentNode.insertBefore(gaScript, s) })() @@ -259,7 +259,7 @@ var _initNav = function () { $sidebar.toggleClass('rb-collapsible-sidebar-collapsed') $('.sidebar-elements>li>a').tooltip('toggleEnabled') - const collapsed = $sidebar.hasClass('rb-collapsible-sidebar-collapsed') + var collapsed = $sidebar.hasClass('rb-collapsible-sidebar-collapsed') $.cookie('rb.sidebarCollapsed', collapsed, { expires: 180 }) if (collapsed) { diff --git a/src/main/resources/web/assets/js/rb-view.js b/src/main/resources/web/assets/js/rb-view.js index 636103228..512444525 100644 --- a/src/main/resources/web/assets/js/rb-view.js +++ b/src/main/resources/web/assets/js/rb-view.js @@ -61,7 +61,7 @@ class RbViewForm extends React.Component { {hadApproval && }
{res.data.elements.map((item) => { - if (!(item.field === TYPE_DIVIDER || item.field === TYPE_REFFORM)) this.__ViewData[item.field] = item.value + if (![TYPE_DIVIDER, TYPE_REFFORM].includes(item.field)) this.__ViewData[item.field] = item.value if (item.field === TYPE_REFFORM) this.__hasRefform = true item.$$$parent = this return detectViewElement(item, this.props.entity) diff --git a/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js b/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js index 613206f18..49609eb95 100644 --- a/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js +++ b/src/main/resources/web/assets/js/trigger/trigger.FIELDAGGREGATION.js @@ -10,7 +10,7 @@ const CALC_MODES2 = { ...FormulaAggregation.CALC_MODES, RBJOIN: $L('连接'), RBJOIN2: $L('去重连接'), - // RBJOIN3: $L('去重连接*'), + // RBJOIN3: $L('去重连接*N'), } const __LAB_MATCHFIELDS = false @@ -70,7 +70,7 @@ class ContentFieldAggregation extends ActionContentSpec {
-
{$L('目标实体/记录匹配规则')}
+
{$L('目标实体/记录匹配规则')} (LAB)