From 76bf69318a24c0cd5f8b3b826559d362ae1648cc 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: Mon, 11 Mar 2024 00:32:55 +0800 Subject: [PATCH] Fix 3.6 beta2 (#726) * enh: sanitizeHtml * be: checkSafeFilePath * Update @rbv * feat: draop upload * enh: getDisksUsed * Update media-capturer.js --------- Co-authored-by: devezhao --- @rbv | 2 +- .../java/com/rebuild/core/ServerStatus.java | 4 +- .../core/service/NoRecordFoundException.java | 8 +- .../service/approval/ApprovalProcessor.java | 80 ++++++++--------- .../service/general/GeneralEntityService.java | 90 +++++++++---------- .../core/support/RebuildConfiguration.java | 4 +- .../core/support/integration/QiniuCloud.java | 2 +- .../java/com/rebuild/utils/CommonsUtils.java | 50 +++++++---- .../java/com/rebuild/utils/OshiUtils.java | 31 +++++-- .../com/rebuild/web/RebuildWebConfigurer.java | 4 +- .../rebuild/web/commons/ErrorPageView.java | 21 ++++- .../rebuild/web/commons/FileDownloader.java | 68 +++++++------- .../rebuild/web/files/FileListController.java | 4 +- .../general/GeneralOperatingController.java | 24 ++--- src/main/resources/web/assets/css/rb-page.css | 8 ++ .../resources/web/assets/css/view-page.css | 2 +- .../web/assets/js/admin/recycle-bin.js | 7 ++ .../web/assets/js/admin/revision-history.js | 7 ++ .../resources/web/assets/js/charts/charts.js | 7 +- .../web/assets/js/files/files-docs.js | 3 +- .../web/assets/js/general/rb-forms.js | 45 ++++++++-- .../resources/web/assets/js/media-capturer.js | 12 +-- .../web/assets/js/metadata/auto-fillin.js | 2 +- src/main/resources/web/assets/js/rb-page.js | 13 ++- .../resources/web/error/server-status.html | 8 +- .../java/com/rebuild/api/sdk/OpenApiSDK.java | 7 +- .../java/com/rebuild/utils/OshiUtilsTest.java | 7 ++ 27 files changed, 324 insertions(+), 196 deletions(-) diff --git a/@rbv b/@rbv index e26090af6..b653c8767 160000 --- a/@rbv +++ b/@rbv @@ -1 +1 @@ -Subproject commit e26090af686fd25f9071b1776b303b3e8ae9491a +Subproject commit b653c87672ac09e4277c012211f1a11db339e92b diff --git a/src/main/java/com/rebuild/core/ServerStatus.java b/src/main/java/com/rebuild/core/ServerStatus.java index 1c5023152..466511075 100644 --- a/src/main/java/com/rebuild/core/ServerStatus.java +++ b/src/main/java/com/rebuild/core/ServerStatus.java @@ -52,8 +52,8 @@ public final class ServerStatus { * @return */ public static List getLastStatus(boolean realtime) { - // 60 秒缓存 - if (realtime || System.currentTimeMillis() - LastCheckTime > 60 * 1000) { + // 30 秒缓存 + if (realtime || System.currentTimeMillis() - LastCheckTime > 30 * 1000) { checkAll(); } diff --git a/src/main/java/com/rebuild/core/service/NoRecordFoundException.java b/src/main/java/com/rebuild/core/service/NoRecordFoundException.java index f319c3d32..912b5678c 100644 --- a/src/main/java/com/rebuild/core/service/NoRecordFoundException.java +++ b/src/main/java/com/rebuild/core/service/NoRecordFoundException.java @@ -24,14 +24,14 @@ public class NoRecordFoundException extends RebuildException { super(); } - public NoRecordFoundException(ID record) { - this(record, Boolean.FALSE); + public NoRecordFoundException(ID recordId) { + this(recordId, Boolean.FALSE); } - public NoRecordFoundException(ID record, boolean i18n) { + public NoRecordFoundException(ID recordId, boolean i18n) { this(i18n ? Language.L("无权读取此记录或记录已被删除") - : ("No Record found : " + record.toLiteral())); + : ("No Record found : " + recordId.toLiteral())); } public NoRecordFoundException(String msg, Throwable cause) { diff --git a/src/main/java/com/rebuild/core/service/approval/ApprovalProcessor.java b/src/main/java/com/rebuild/core/service/approval/ApprovalProcessor.java index 4a4debdcc..df03b5980 100644 --- a/src/main/java/com/rebuild/core/service/approval/ApprovalProcessor.java +++ b/src/main/java/com/rebuild/core/service/approval/ApprovalProcessor.java @@ -55,7 +55,7 @@ public class ApprovalProcessor extends SetUser { // 最大撤销次数 private static final int MAX_REVOKED = 100; - final private ID record; + final private ID recordId; // 如未传递,会在需要时根据 record 确定 private ID approval; @@ -63,18 +63,18 @@ public class ApprovalProcessor extends SetUser { private FlowParser flowParser; /** - * @param record + * @param recordId */ - public ApprovalProcessor(ID record) { - this(record, null); + public ApprovalProcessor(ID recordId) { + this(recordId, null); } /** - * @param record + * @param recordId * @param approval */ - public ApprovalProcessor(ID record, ID approval) { - this.record = record; + public ApprovalProcessor(ID recordId, ID approval) { + this.recordId = recordId; this.approval = approval; } @@ -86,7 +86,7 @@ public class ApprovalProcessor extends SetUser { * @throws ApprovalException */ public boolean submit(JSONObject selectNextUsers) throws ApprovalException { - final ApprovalState currentState = ApprovalHelper.getApprovalState(this.record); + final ApprovalState currentState = ApprovalHelper.getApprovalState(this.recordId); if (currentState == ApprovalState.PROCESSING || currentState == ApprovalState.APPROVED) { throw new ApprovalException(Language.L("无效审批状态 (%s) ,请刷新后重试", currentState)); } @@ -97,16 +97,16 @@ public class ApprovalProcessor extends SetUser { return false; } - Set nextApprovers = nextNodes.getApproveUsers(this.getUser(), this.record, selectNextUsers); + Set nextApprovers = nextNodes.getApproveUsers(this.getUser(), this.recordId, selectNextUsers); if (nextApprovers.isEmpty()) { log.warn("No any approvers special"); return false; } - Set ccUsers = nextNodes.getCcUsers(this.getUser(), this.record, selectNextUsers); - Set ccAccounts = nextNodes.getCcAccounts(this.record); + Set ccUsers = nextNodes.getCcUsers(this.getUser(), this.recordId, selectNextUsers); + Set ccAccounts = nextNodes.getCcAccounts(this.recordId); - Record recordOfMain = EntityHelper.forUpdate(this.record, this.getUser(), false); + Record recordOfMain = EntityHelper.forUpdate(this.recordId, this.getUser(), false); recordOfMain.setID(EntityHelper.ApprovalId, this.approval); recordOfMain.setInt(EntityHelper.ApprovalState, ApprovalState.PROCESSING.getState()); recordOfMain.setString(EntityHelper.ApprovalStepNode, nextNodes.getApprovalNode().getNodeId()); @@ -116,8 +116,8 @@ public class ApprovalProcessor extends SetUser { Application.getBean(ApprovalStepService.class).txSubmit(recordOfMain, ccUsers, ccAccounts, nextApprovers); // 审批时共享 - Set ccs4share = nextNodes.getCcUsers4Share(this.getUser(), this.record, selectNextUsers); - share2CcIfNeed(this.record, ccs4share); + Set ccs4share = nextNodes.getCcUsers4Share(this.getUser(), this.recordId, selectNextUsers); + share2CcIfNeed(this.recordId, ccs4share); return true; } @@ -153,7 +153,7 @@ public class ApprovalProcessor extends SetUser { final Object[] stepApprover = Application.createQueryNoFilter( "select stepId,state,node,approvalId,attrMore from RobotApprovalStep where recordId = ? and approver = ? and node = ? and isCanceled = 'F' order by createdOn desc") - .setParameter(1, this.record) + .setParameter(1, this.recordId) .setParameter(2, approver) .setParameter(3, getCurrentNodeId(status)) .unique(); @@ -188,7 +188,7 @@ public class ApprovalProcessor extends SetUser { nextNode = rejectNode; approvedStep.setInt("state", ApprovalState.BACKED.getState()); } else if (state == ApprovalState.APPROVED && !nextNodes.isLastStep()) { - nextApprovers = nextNodes.getApproveUsers(this.getUser(), this.record, selectNextUsers); + nextApprovers = nextNodes.getApproveUsers(this.getUser(), this.recordId, selectNextUsers); // 自选审批人 nextApprovers.addAll(getSelfSelectedApprovers(nextNodes)); @@ -200,8 +200,8 @@ public class ApprovalProcessor extends SetUser { nextNode = nextApprovalNode != null ? nextApprovalNode.getNodeId() : null; } - Set ccUsers = nextNodes.getCcUsers(this.getUser(), this.record, selectNextUsers); - Set ccAccounts = nextNodes.getCcAccounts(this.record); + Set ccUsers = nextNodes.getCcUsers(this.getUser(), this.recordId, selectNextUsers); + Set ccAccounts = nextNodes.getCcAccounts(this.recordId); FlowNode currentNode = getFlowParser().getNode((String) stepApprover[2]); Application.getBean(ApprovalStepService.class) @@ -209,8 +209,8 @@ public class ApprovalProcessor extends SetUser { // 同意时共享 if (state == ApprovalState.APPROVED) { - Set ccs4share = nextNodes.getCcUsers4Share(this.getUser(), this.record, selectNextUsers); - share2CcIfNeed(this.record, ccs4share); + Set ccs4share = nextNodes.getCcUsers4Share(this.getUser(), this.recordId, selectNextUsers); + share2CcIfNeed(this.recordId, ccs4share); } } @@ -223,7 +223,7 @@ public class ApprovalProcessor extends SetUser { final ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING); Application.getBean(ApprovalStepService.class).txCancel( - this.record, status.getApprovalId(), getCurrentNodeId(status), false); + this.recordId, status.getApprovalId(), getCurrentNodeId(status), false); } /** @@ -235,13 +235,13 @@ public class ApprovalProcessor extends SetUser { final ApprovalStatus status = checkApprovalState(ApprovalState.PROCESSING); this.approval = status.getApprovalId(); - final String sentKey = String.format("URGE:%s-%s", this.approval, this.record); + final String sentKey = String.format("URGE:%s-%s", this.approval, this.recordId); if (Application.getCommonsCache().getx(sentKey) != null) { return -1; } int sent = 0; - String entityLabel = EasyMetaFactory.getLabel(MetadataHelper.getEntity(this.record.getEntityCode())); + String entityLabel = EasyMetaFactory.getLabel(MetadataHelper.getEntity(this.recordId.getEntityCode())); JSONArray step = getCurrentStep(status); for (Object o : step) { @@ -250,7 +250,7 @@ public class ApprovalProcessor extends SetUser { ID approver = ID.valueOf(s.getString("approver")); String urgeMsg = Language.L("有一条 %s 记录正在等待你审批,请尽快审批", entityLabel); - Application.getNotifications().send(MessageBuilder.createApproval(approver, urgeMsg, this.record)); + Application.getNotifications().send(MessageBuilder.createApproval(approver, urgeMsg, this.recordId)); sent++; } @@ -273,7 +273,7 @@ public class ApprovalProcessor extends SetUser { Object[] instepApprover = Application.createQueryNoFilter( "select state from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and approver = ? and isCanceled = 'F'") - .setParameter(1, this.record) + .setParameter(1, this.recordId) .setParameter(2, this.approval) .setParameter(3, getCurrentNodeId(null)) .setParameter(4, toUser) @@ -306,7 +306,7 @@ public class ApprovalProcessor extends SetUser { Object[] count = Application.createQueryNoFilter( "select count(stepId) from RobotApprovalStep where recordId = ? and state = ?") - .setParameter(1, this.record) + .setParameter(1, this.recordId) .setParameter(2, ApprovalState.REVOKED.getState()) .unique(); if (ObjectUtils.toInt(count[0]) >= MAX_REVOKED) { @@ -314,7 +314,7 @@ public class ApprovalProcessor extends SetUser { } Application.getBean(ApprovalStepService.class).txCancel( - this.record, status.getApprovalId(), getCurrentNodeId(status), true); + this.recordId, status.getApprovalId(), getCurrentNodeId(status), true); } /** @@ -357,7 +357,7 @@ public class ApprovalProcessor extends SetUser { } FlowBranch branch = (FlowBranch) node; - if (branch.matches(record)) { + if (branch.matches(recordId)) { return getNextNode(branch.getNodeId()); } } @@ -404,7 +404,7 @@ public class ApprovalProcessor extends SetUser { * @return */ private String getCurrentNodeId(ApprovalStatus useStatus) { - if (useStatus == null) useStatus = ApprovalHelper.getApprovalStatus(this.record); + if (useStatus == null) useStatus = ApprovalHelper.getApprovalStatus(this.recordId); String currentNode = useStatus.getCurrentStepNode(); if (StringUtils.isBlank(currentNode) @@ -424,7 +424,7 @@ public class ApprovalProcessor extends SetUser { } FlowDefinition flowDefinition = RobotApprovalManager.instance.getFlowDefinition( - MetadataHelper.getEntity(this.record.getEntityCode()), this.approval); + MetadataHelper.getEntity(this.recordId.getEntityCode()), this.approval); flowParser = flowDefinition.createFlowParser(); return flowParser; } @@ -436,7 +436,7 @@ public class ApprovalProcessor extends SetUser { * @return returns [S, S] */ public JSONArray getCurrentStep(ApprovalStatus useStatus) { - if (useStatus == null) useStatus = ApprovalHelper.getApprovalStatus(this.record); + if (useStatus == null) useStatus = ApprovalHelper.getApprovalStatus(this.recordId); final String currentNode = useStatus.getCurrentStepNode(); @@ -444,7 +444,7 @@ public class ApprovalProcessor extends SetUser { String sql = "select nodeBatch from RobotApprovalStep" + " where recordId = ? and approvalId = ? and node = ? and isCanceled = 'F' and isBacked = 'F' order by createdOn desc"; Object[] lastNode = Application.createQueryNoFilter(sql) - .setParameter(1, this.record) + .setParameter(1, this.recordId) .setParameter(2, this.approval) .setParameter(3, currentNode) .unique(); @@ -456,7 +456,7 @@ public class ApprovalProcessor extends SetUser { if (StringUtils.isNotBlank(nodeBatch)) sql += " and nodeBatch = '" + nodeBatch + "'"; Object[][] array = Application.createQueryNoFilter(sql) - .setParameter(1, this.record) + .setParameter(1, this.recordId) .setParameter(2, this.approval) .setParameter(3, currentNode) .array(); @@ -474,13 +474,13 @@ public class ApprovalProcessor extends SetUser { * @return returns [ [S,S], [S], [SSS], [S] ] */ public JSONArray getWorkedSteps() { - final ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.record); + final ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId); this.approval = status.getApprovalId(); Object[][] array = Application.createQueryNoFilter( "select approver,state,remark,approvedTime,createdOn,createdBy,node,prevNode,nodeBatch,ccUsers,ccAccounts,attrMore from RobotApprovalStep" + " where recordId = ? and isWaiting = 'F' and isCanceled = 'F' order by createdOn") - .setParameter(1, this.record) + .setParameter(1, this.recordId) .array(); if (array.length == 0) return JSONUtils.EMPTY_ARRAY; @@ -497,7 +497,7 @@ public class ApprovalProcessor extends SetUser { stepGroup.add(o); } if (firstStep == null) { - throw new ConfigurationException(Language.L("无效审批记录 (%s)", this.record)); + throw new ConfigurationException(Language.L("无效审批记录 (%s)", this.recordId)); } JSONArray steps = new JSONArray(); @@ -625,7 +625,7 @@ public class ApprovalProcessor extends SetUser { * @return */ public JSONArray getBackSteps() { - ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.record); + ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId); this.approval = status.getApprovalId(); String currentNode = getCurrentNodeId(status); @@ -672,7 +672,7 @@ public class ApprovalProcessor extends SetUser { Object[][] array = Application.createQueryNoFilter( "select approver from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and isWaiting = 'T' and isCanceled = 'F'") - .setParameter(1, this.record) + .setParameter(1, this.recordId) .setParameter(2, this.approval) .setParameter(3, node) .array(); @@ -708,7 +708,7 @@ public class ApprovalProcessor extends SetUser { } private ApprovalStatus checkApprovalState(ApprovalState mustbe) { - final ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.record); + final ApprovalStatus status = ApprovalHelper.getApprovalStatus(this.recordId); if (status.getCurrentState() != mustbe) { throw new ApprovalException(Language.L("无效审批状态 (%s) ,请刷新后重试", status.getCurrentState())); } @@ -722,7 +722,7 @@ public class ApprovalProcessor extends SetUser { String currentNodeId = getCurrentNodeId(status); Object[] stepApprover = Application.createQueryNoFilter( "select stepId,state,approver from RobotApprovalStep where recordId = ? and approvalId = ? and node = ? and approver = ? and isCanceled = 'F'") - .setParameter(1, this.record) + .setParameter(1, this.recordId) .setParameter(2, this.approval) .setParameter(3, currentNodeId) .setParameter(4, approver) diff --git a/src/main/java/com/rebuild/core/service/general/GeneralEntityService.java b/src/main/java/com/rebuild/core/service/general/GeneralEntityService.java index 69559f1e9..9a43ec840 100644 --- a/src/main/java/com/rebuild/core/service/general/GeneralEntityService.java +++ b/src/main/java/com/rebuild/core/service/general/GeneralEntityService.java @@ -267,26 +267,26 @@ public class GeneralEntityService extends ObservableService implements EntitySer } @Override - public int delete(ID record) { - return delete(record, null); + public int delete(ID recordId) { + return delete(recordId, null); } @Override - public int delete(ID record, String[] cascades) { + public int delete(ID recordId, String[] cascades) { final ID currentUser = UserContextHolder.getUser(); - final RecycleStore recycleBin = useRecycleStore(record); + final RecycleStore recycleBin = useRecycleStore(recordId); - int affected = this.deleteInternal(record); + int affected = this.deleteInternal(recordId); if (affected == 0) return 0; affected = 1; - Map> recordsOfCascaded = getCascadedRecords(record, cascades, BizzPermission.DELETE); + Map> recordsOfCascaded = getCascadedRecords(recordId, cascades, BizzPermission.DELETE); for (Map.Entry> e : recordsOfCascaded.entrySet()) { log.info("Cascading delete - {} > {} ", e.getKey(), e.getValue()); for (ID id : e.getValue()) { if (Application.getPrivilegesManager().allowDelete(currentUser, id)) { - if (recycleBin != null) recycleBin.add(id, record); + if (recycleBin != null) recycleBin.add(id, recordId); int deleted = 0; try { @@ -312,39 +312,39 @@ public class GeneralEntityService extends ObservableService implements EntitySer } /** - * @param record + * @param recordId * @return * @throws DataSpecificationException */ - protected int deleteInternal(ID record) throws DataSpecificationException { - Record delete = EntityHelper.forUpdate(record, UserContextHolder.getUser()); + protected int deleteInternal(ID recordId) throws DataSpecificationException { + Record delete = EntityHelper.forUpdate(recordId, UserContextHolder.getUser()); if (!checkModifications(delete, BizzPermission.DELETE)) { return 0; } // 手动删除明细记录,以执行系统规则(如触发器、附件删除等) - Entity de = MetadataHelper.getEntity(record.getEntityCode()).getDetailEntity(); + Entity de = MetadataHelper.getEntity(recordId.getEntityCode()).getDetailEntity(); if (de != null) { - for (ID did : QueryHelper.detailIdsNoFilter(record)) { + for (ID did : QueryHelper.detailIdsNoFilter(recordId)) { // 明细无约束检查 checkModifications // 不使用明细实体 Service super.delete(did); } } - return super.delete(record); + return super.delete(recordId); } @Override - public int assign(ID record, ID to, String[] cascades) { - final User toUser = Application.getUserStore().getUser(to); - final ID recordOrigin = record; + public int assign(ID recordId, ID toUserId, String[] cascades) { + final User toUser = Application.getUserStore().getUser(toUserId); + final ID recordOrigin = recordId; // v3.2.2 若为明细则转为主记录 - if (MetadataHelper.getEntity(record.getEntityCode()).getMainEntity() != null) { - record = QueryHelper.getMainIdByDetail(record); + if (MetadataHelper.getEntity(recordId.getEntityCode()).getMainEntity() != null) { + recordId = QueryHelper.getMainIdByDetail(recordId); } - final Record assignAfter = EntityHelper.forUpdate(record, (ID) toUser.getIdentity(), Boolean.FALSE); + final Record assignAfter = EntityHelper.forUpdate(recordId, (ID) toUser.getIdentity(), Boolean.FALSE); assignAfter.setID(EntityHelper.OwningUser, (ID) toUser.getIdentity()); assignAfter.setID(EntityHelper.OwningDept, (ID) toUser.getOwningDept().getIdentity()); @@ -352,15 +352,15 @@ public class GeneralEntityService extends ObservableService implements EntitySer Record assignBefore = null; int affected; - if (to.equals(Application.getRecordOwningCache().getOwningUser(record))) { + if (toUserId.equals(Application.getRecordOwningCache().getOwningUser(recordId))) { // No need to change - log.debug("The record owner has not changed, ignore : {}", record); + log.debug("The record owner has not changed, ignore : {}", recordId); affected = 1; } else { assignBefore = countObservers() > 0 ? recordSnap(assignAfter, false) : null; delegateService.update(assignAfter); - Application.getRecordOwningCache().cleanOwningUser(record); + Application.getRecordOwningCache().cleanOwningUser(recordId); affected = 1; } @@ -369,7 +369,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer log.info("Cascading assign - {} > {}", e.getKey(), e.getValue()); for (ID casid : e.getValue()) { - affected += assign(casid, to, null); + affected += assign(casid, toUserId, null); } } @@ -380,38 +380,38 @@ public class GeneralEntityService extends ObservableService implements EntitySer } @Override - public int share(ID record, ID to, String[] cascades, int rights) { + public int share(ID recordId, ID toUserId, String[] cascades, int rights) { final ID currentUser = UserContextHolder.getUser(); - final ID recordOrigin = record; + final ID recordOrigin = recordId; // v3.2.2 若为明细则转为主记录 - if (MetadataHelper.getEntity(record.getEntityCode()).getMainEntity() != null) { - record = QueryHelper.getMainIdByDetail(record); + if (MetadataHelper.getEntity(recordId.getEntityCode()).getMainEntity() != null) { + recordId = QueryHelper.getMainIdByDetail(recordId); } boolean fromTriggerNoDowngrade = GeneralEntityServiceContextHolder.isFromTrigger(false); if (!fromTriggerNoDowngrade) { // 如用户无更新权限,则降级为只读共享 if ((rights & BizzPermission.UPDATE.getMask()) != 0) { - if (!Application.getPrivilegesManager().allowUpdate(to, record.getEntityCode()) /* 目标用户无基础更新权限 */ - || !Application.getPrivilegesManager().allow(currentUser, record, BizzPermission.UPDATE, true) /* 操作用户无记录更新权限 */) { + if (!Application.getPrivilegesManager().allowUpdate(toUserId, recordId.getEntityCode()) /* 目标用户无基础更新权限 */ + || !Application.getPrivilegesManager().allow(currentUser, recordId, BizzPermission.UPDATE, true) /* 操作用户无记录更新权限 */) { rights = BizzPermission.READ.getMask(); - log.warn("Downgrade share rights to READ({}) : {}", BizzPermission.READ.getMask(), record); + log.warn("Downgrade share rights to READ({}) : {}", BizzPermission.READ.getMask(), recordId); } } } - final String entityName = MetadataHelper.getEntityName(record); + final String entityName = MetadataHelper.getEntityName(recordId); final Record sharedAfter = EntityHelper.forNew(EntityHelper.ShareAccess, currentUser); - sharedAfter.setID("recordId", record); - sharedAfter.setID("shareTo", to); + sharedAfter.setID("recordId", recordId); + sharedAfter.setID("shareTo", toUserId); sharedAfter.setString("belongEntity", entityName); sharedAfter.setInt("rights", rights); Object[] hasShared = ((BaseService) delegateService).getPersistManagerFactory().createQuery( "select accessId,rights from ShareAccess where belongEntity = ? and recordId = ? and shareTo = ?") .setParameter(1, entityName) - .setParameter(2, record) - .setParameter(3, to) + .setParameter(2, recordId) + .setParameter(3, toUserId) .unique(); int affected; @@ -426,15 +426,15 @@ public class GeneralEntityService extends ObservableService implements EntitySer sharedAfter.setID("accessId", (ID) hasShared[0]); } else { - log.debug("The record has been shared and has the same rights, ignore : {}", record); + log.debug("The record has been shared and has the same rights, ignore : {}", recordId); affected = 1; } } else { // 可以共享给自己 if (log.isDebugEnabled() - && to.equals(Application.getRecordOwningCache().getOwningUser(record))) { - log.debug("Share to the same user as the record, ignore : {}", record); + && toUserId.equals(Application.getRecordOwningCache().getOwningUser(recordId))) { + log.debug("Share to the same user as the record, ignore : {}", recordId); } delegateService.create(sharedAfter); @@ -447,7 +447,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer log.info("Cascading share - {} > {}", e.getKey(), e.getValue()); for (ID casid : e.getValue()) { - affected += share(casid, to, null, rights); + affected += share(casid, toUserId, null, rights); } } @@ -458,7 +458,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer } @Override - public int unshare(ID record, ID accessId) { + public int unshare(ID recordId, ID accessId) { final ID currentUser = UserContextHolder.getUser(); Record unsharedBefore = null; @@ -502,12 +502,12 @@ public class GeneralEntityService extends ObservableService implements EntitySer /** * 获取级联操作记录 * - * @param recordMain 主记录 + * @param mainRecordId 主记录 * @param cascadeEntities 级联实体 * @param action 动作 * @return */ - protected Map> getCascadedRecords(ID recordMain, String[] cascadeEntities, Permission action) { + protected Map> getCascadedRecords(ID mainRecordId, String[] cascadeEntities, Permission action) { if (cascadeEntities == null || cascadeEntities.length == 0) { return Collections.emptyMap(); } @@ -515,7 +515,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer final boolean fromTriggerNoFilter = GeneralEntityServiceContextHolder.isFromTrigger(false); Map> entityRecordsMap = new HashMap<>(); - Entity mainEntity = MetadataHelper.getEntity(recordMain.getEntityCode()); + Entity mainEntity = MetadataHelper.getEntity(mainRecordId.getEntityCode()); for (String cas : cascadeEntities) { if (!MetadataHelper.containsEntity(cas)) { @@ -532,7 +532,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer List or = new ArrayList<>(); for (Field field : reftoFields) { - or.add(String.format("%s = '%s'", field.getName(), recordMain)); + or.add(String.format("%s = '%s'", field.getName(), mainRecordId)); } String sql = String.format("select %s from %s where ( %s )", @@ -793,7 +793,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer if (approvalUser == null) { approvalUser = UserService.SYSTEM_USER; - log.warn("Use system user for approve"); + log.warn("Use '{}' do approve : {}", approvalUser, recordId); } Record approvalRecord = EntityHelper.forUpdate(recordId, approvalUser, false); diff --git a/src/main/java/com/rebuild/core/support/RebuildConfiguration.java b/src/main/java/com/rebuild/core/support/RebuildConfiguration.java index 977dc7540..922dec14a 100644 --- a/src/main/java/com/rebuild/core/support/RebuildConfiguration.java +++ b/src/main/java/com/rebuild/core/support/RebuildConfiguration.java @@ -41,7 +41,7 @@ public class RebuildConfiguration extends KVStorage { * @return */ public static File getFileOfData(String filepath) { - CommonsUtils.checkFilePathAttack(filepath); + CommonsUtils.checkSafeFilePath(filepath); String d = get(ConfigurationItem.DataDirectory); File datadir = null; @@ -76,7 +76,7 @@ public class RebuildConfiguration extends KVStorage { * @see PerHourJob#doCleanTempFiles() */ public static File getFileOfTemp(String filepath) { - CommonsUtils.checkFilePathAttack(filepath); + CommonsUtils.checkSafeFilePath(filepath); File temp = getFileOfData("temp"); if (!temp.exists()) { diff --git a/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java b/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java index 255269e3b..ebe3331f8 100644 --- a/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java +++ b/src/main/java/com/rebuild/core/support/integration/QiniuCloud.java @@ -393,7 +393,7 @@ public class QiniuCloud { } /** - * 读取文件 + * 读取本地存储文件 `$DATA/rb/` * * @param filepath * @return diff --git a/src/main/java/com/rebuild/utils/CommonsUtils.java b/src/main/java/com/rebuild/utils/CommonsUtils.java index 0541076ff..caaed790d 100644 --- a/src/main/java/com/rebuild/utils/CommonsUtils.java +++ b/src/main/java/com/rebuild/utils/CommonsUtils.java @@ -99,13 +99,31 @@ public class CommonsUtils { // https://www.php.net/htmlspecialchars return text.toString() -// .replace("&", "&") .replace("\"", """) .replace("'", "'") .replace(">", ">") .replace("<", "<"); } + /** + * @param text + * @return + * @see #escapeHtml(Object) + */ + public static String sanitizeHtml(Object text) { + if (text == null || StringUtils.isBlank(text.toString())) { + return StringUtils.EMPTY; + } + + // TODO 更好的 sanitizeHtml + return text.toString() + .replace("", "") + .replace("", "") + .replace("")) { - throw new SecurityException("Attack path detected : " + filepath); + throw new SecurityException("Attack path detected : " + escapeHtml(filepath)); } } } diff --git a/src/main/java/com/rebuild/utils/OshiUtils.java b/src/main/java/com/rebuild/utils/OshiUtils.java index 7c8fa1d9c..dde63177e 100644 --- a/src/main/java/com/rebuild/utils/OshiUtils.java +++ b/src/main/java/com/rebuild/utils/OshiUtils.java @@ -10,8 +10,8 @@ package com.rebuild.utils; import cn.devezhao.commons.CalendarUtils; import cn.devezhao.commons.ObjectUtils; import cn.devezhao.commons.runtime.MemoryInformationBean; -import com.esotericsoftware.minlog.Log; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import oshi.SystemInfo; import oshi.hardware.GlobalMemory; @@ -22,6 +22,7 @@ import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.stream.Stream; @@ -55,7 +56,7 @@ public class OshiUtils { double memoryUsage = (memoryTotal - memoryFree) * 1.0 / memoryTotal; return new double[]{ (int) (memoryTotal / MemoryInformationBean.MEGABYTES), - ObjectUtils.round(memoryUsage * 100, 2) + ObjectUtils.round(memoryUsage * 100, 1) }; } @@ -65,13 +66,12 @@ public class OshiUtils { * @return */ public static double[] getJvmMemoryUsed() { -// double maxMemory = Runtime.getRuntime().maxMemory(); double memoryTotal = Runtime.getRuntime().totalMemory(); double memoryFree = Runtime.getRuntime().freeMemory(); double memoryUsage = (memoryTotal - memoryFree) / memoryTotal; return new double[]{ (int) (memoryTotal / MemoryInformationBean.MEGABYTES), - ObjectUtils.round(memoryUsage * 100, 2) + ObjectUtils.round(memoryUsage * 100, 1) }; } @@ -82,7 +82,7 @@ public class OshiUtils { */ public static double getSystemLoad() { double[] loadAverages = getSI().getHardware().getProcessor().getSystemLoadAverage(2); - return ObjectUtils.round(loadAverages[1], 2); + return ObjectUtils.round(loadAverages[1], 1); } /** @@ -156,4 +156,25 @@ public class OshiUtils { } return false; } + + /** + * 磁盘用量获取 + * + * @return [统计, 占用%, 磁盘] + */ + public static List getDisksUsed() { + List disks = new ArrayList<>(); + try { + for (File root : File.listRoots()) { + String name = org.apache.commons.lang.StringUtils.defaultIfBlank(root.getName(), root.getAbsolutePath()); + double total = root.getTotalSpace() * 1d / FileUtils.ONE_GB; + double used = total - (root.getFreeSpace() * 1d / FileUtils.ONE_GB); + double usedPercentage = used * 100d / total; + disks.add(new Object[] { total, usedPercentage, name }); + } + } catch (Exception ex) { + log.warn("Cannot stats disks", ex); + } + return disks; + } } diff --git a/src/main/java/com/rebuild/web/RebuildWebConfigurer.java b/src/main/java/com/rebuild/web/RebuildWebConfigurer.java index 329a2ebbe..28301e781 100644 --- a/src/main/java/com/rebuild/web/RebuildWebConfigurer.java +++ b/src/main/java/com/rebuild/web/RebuildWebConfigurer.java @@ -204,10 +204,10 @@ public class RebuildWebConfigurer implements WebMvcConfigurer, ErrorViewResolver if (StringUtils.isBlank(errorMsg)) errorMsg = Language.L("系统繁忙,请稍后重试"); error.getModel().put("error_code", errorCode); - error.getModel().put("error_msg", CommonsUtils.escapeHtml(errorMsg)); + error.getModel().put("error_msg", CommonsUtils.sanitizeHtml(errorMsg)); if (ex != null && Application.devMode()) { - error.getModel().put("error_stack", CommonsUtils.escapeHtml(ThrowableUtils.extractStackTrace(ex))); + error.getModel().put("error_stack", ThrowableUtils.extractStackTrace(ex)); } return error; diff --git a/src/main/java/com/rebuild/web/commons/ErrorPageView.java b/src/main/java/com/rebuild/web/commons/ErrorPageView.java index f3f5e6a4b..2c4fba441 100644 --- a/src/main/java/com/rebuild/web/commons/ErrorPageView.java +++ b/src/main/java/com/rebuild/web/commons/ErrorPageView.java @@ -8,6 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information. package com.rebuild.web.commons; import cn.devezhao.commons.CodecUtils; +import cn.devezhao.commons.ObjectUtils; import cn.devezhao.commons.web.ServletUtils; import com.alibaba.fastjson.JSONObject; import com.rebuild.core.Application; @@ -18,7 +19,6 @@ import com.rebuild.core.support.i18n.Language; import com.rebuild.utils.AppUtils; import com.rebuild.utils.OshiUtils; import com.rebuild.web.BaseController; -import com.rebuild.web.RebuildWebInterceptor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Controller; @@ -29,6 +29,7 @@ import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.List; /** * @author Zixin (RB) @@ -70,6 +71,14 @@ public class ErrorPageView extends BaseController { mv.getModel().put("SystemLoad", OshiUtils.getSystemLoad()); mv.getModelMap().put("isAdminVerified", AppUtils.isAdminVerified(request)); mv.getModelMap().put("SN", License.SN() + "/" + OshiUtils.getLocalIp() + "/" + ServerStatus.STARTUP_ONCE); + + StringBuilder disksDesc = new StringBuilder(); + for (Object[] d : OshiUtils.getDisksUsed()) { + //noinspection MalformedFormatString + disksDesc.append(String.format(" $%s:%.1f%%:%.1fGB", d[2], d[1], d[0])); + } + mv.getModelMap().put("DisksDesc", disksDesc.toString().trim()); + return mv; } @@ -90,6 +99,16 @@ public class ErrorPageView extends BaseController { status.put("MemoryUsage", OshiUtils.getOsMemoryUsed()[1]); status.put("SystemLoad", OshiUtils.getSystemLoad()); + List disksUsed = OshiUtils.getDisksUsed(); + double diskWarning = 0; + for (Object[] d : disksUsed) { + d[0] = ObjectUtils.round((double) d[0], 1); + d[1] = ObjectUtils.round((double) d[1], 1); + if ((double) d[1] >= 80) diskWarning = (double) d[1]; + } + status.put("DisksUsage", disksUsed); + if (diskWarning >= 80) s.put("warning", true); + ServletUtils.writeJson(response, s.toJSONString()); } diff --git a/src/main/java/com/rebuild/web/commons/FileDownloader.java b/src/main/java/com/rebuild/web/commons/FileDownloader.java index 1ca7f37e5..142120f44 100644 --- a/src/main/java/com/rebuild/web/commons/FileDownloader.java +++ b/src/main/java/com/rebuild/web/commons/FileDownloader.java @@ -60,12 +60,12 @@ public class FileDownloader extends BaseController { public void viewImg(HttpServletRequest request, HttpServletResponse response) throws IOException { RbAssert.isAllow(checkUser(request), "Unauthorized access"); - String filePath = request.getRequestURI(); - filePath = filePath.split("/filex/img/")[1]; - filePath = CodecUtils.urlDecode(filePath); + String filepath = request.getRequestURI(); + filepath = filepath.split("/filex/img/")[1]; + filepath = CodecUtils.urlDecode(filepath); - if (filePath.startsWith("http://") || filePath.startsWith("https://")) { - response.sendRedirect(filePath); + if (filepath.startsWith("http://") || filepath.startsWith("https://")) { + response.sendRedirect(filepath); return; } @@ -77,7 +77,7 @@ public class FileDownloader extends BaseController { imageView2 = "imageView2/" + imageView2.split("imageView2/")[1].split("&")[0]; // svg/webp does not support - if (filePath.toLowerCase().endsWith(".svg") || filePath.toLowerCase().endsWith(".webp")) { + if (filepath.toLowerCase().endsWith(".svg") || filepath.toLowerCase().endsWith(".webp")) { imageView2 = null; } } else { @@ -89,7 +89,7 @@ public class FileDownloader extends BaseController { // Local storage || temp || local if (!QiniuCloud.instance().available() || local) { - String fileName = QiniuCloud.parseFileName(filePath); + String fileName = QiniuCloud.parseFileName(filepath); String mimeType = request.getServletContext().getMimeType(fileName); if (mimeType != null) { response.setContentType(mimeType); @@ -99,12 +99,12 @@ public class FileDownloader extends BaseController { // 使用原图 if (iv2 == null || iv2.getWidth() <= 0 || iv2.getWidth() >= ImageView2.ORIGIN_WIDTH) { - writeLocalFile(filePath, temp, response); + writeLocalFile(filepath, temp, response); } // 粗略图 else { - filePath = checkFilePath(filePath); - File img = temp ? RebuildConfiguration.getFileOfTemp(filePath) : RebuildConfiguration.getFileOfData(filePath); + filepath = checkSafeFilePath(filepath); + File img = temp ? RebuildConfiguration.getFileOfTemp(filepath) : RebuildConfiguration.getFileOfData(filepath); if (!img.exists()) { response.setHeader("Content-Disposition", StringUtils.EMPTY); // Clean download response.sendError(HttpStatus.NOT_FOUND.value()); @@ -116,23 +116,23 @@ public class FileDownloader extends BaseController { } else { // 特殊字符文件名 - String[] path = filePath.split("/"); + String[] path = filepath.split("/"); path[path.length - 1] = CodecUtils.urlEncode(path[path.length - 1]); path[path.length - 1] = path[path.length - 1].replace("+", "%20"); - filePath = StringUtils.join(path, "/"); + filepath = StringUtils.join(path, "/"); if (imageView2 != null) { - filePath += "?" + imageView2; + filepath += "?" + imageView2; } - String privateUrl = QiniuCloud.instance().makeUrl(filePath, (int) (cacheTime * 60 * 1.2)); + String privateUrl = QiniuCloud.instance().makeUrl(filepath, (int) (cacheTime * 60 * 1.2)); response.sendRedirect(privateUrl); } } @GetMapping(value = {"download/**", "access/**"}) public void download(HttpServletRequest request, HttpServletResponse response) throws IOException { - String filePath = request.getRequestURI(); + String filepath = request.getRequestURI(); // 共享查看 if (request.getRequestURI().contains("/filex/access/")) { @@ -142,15 +142,15 @@ public class FileDownloader extends BaseController { return; } - filePath = filePath.split("/filex/access/")[1]; + filepath = filepath.split("/filex/access/")[1]; } else { RbAssert.isAllow(checkUser(request), "Unauthorized access"); - filePath = filePath.split("/filex/download/")[1]; + filepath = filepath.split("/filex/download/")[1]; } String attname = getParameter(request, "attname"); if (StringUtils.isBlank(attname)) { - attname = QiniuCloud.parseFileName(filePath); + attname = QiniuCloud.parseFileName(filepath); attname = CodecUtils.urlDecode(attname); } @@ -159,28 +159,28 @@ public class FileDownloader extends BaseController { ServletUtils.setNoCacheHeaders(response); if (QiniuCloud.instance().available() && !temp) { - String privateUrl = QiniuCloud.instance().makeUrl(filePath); + String privateUrl = QiniuCloud.instance().makeUrl(filepath); if (!INLINE_FORCE.equals(attname)) privateUrl += "&attname=" + CodecUtils.urlEncode(attname); response.sendRedirect(privateUrl); } else { // V34 PDF/HTML 可直接预览 - boolean inline = (filePath.toLowerCase().endsWith(".pdf") || filePath.toLowerCase().endsWith(".html")) + boolean inline = (filepath.toLowerCase().endsWith(".pdf") || filepath.toLowerCase().endsWith(".html")) && (request.getRequestURI().contains("/filex/access/") || INLINE_FORCE.equals(attname)); setDownloadHeaders(request, response, attname, inline); - writeLocalFile(filePath, temp, response); + writeLocalFile(filepath, temp, response); } } @GetMapping(value = "read-raw") public void readRawText(HttpServletRequest request, HttpServletResponse response) throws IOException { - String filePath = getParameterNotNull(request, "url"); + String filepath = getParameterNotNull(request, "url"); final String charset = getParameter(request, "charset", AppUtils.UTF8); final int cut = getIntParameter(request, "cut"); // MB - if (CommonsUtils.isExternalUrl(filePath)) { - String text = OkHttpUtils.get(filePath, null, charset); + if (CommonsUtils.isExternalUrl(filepath)) { + String text = OkHttpUtils.get(filepath, null, charset); ServletUtils.write(response, text); return; } @@ -189,19 +189,19 @@ public class FileDownloader extends BaseController { String text; if (QiniuCloud.instance().available()) { - FileInfo fi = QiniuCloud.instance().stat(filePath); + FileInfo fi = QiniuCloud.instance().stat(filepath); if (fi == null) { text = "ERROR:FILE_NOT_EXISTS"; } else if (cut > 0 && fi.fsize / 1024 / 1024 > cut) { text = "ERROR:FILE_TOO_LARGE"; } else { - text = OkHttpUtils.get(QiniuCloud.instance().makeUrl(filePath), null, charset); + text = OkHttpUtils.get(QiniuCloud.instance().makeUrl(filepath), null, charset); } } else { // Local storage - filePath = checkFilePath(filePath); - File file = RebuildConfiguration.getFileOfData(filePath); + filepath = checkSafeFilePath(filepath); + File file = RebuildConfiguration.getFileOfData(filepath); if (!file.exists()) { text = "ERROR:FILE_NOT_EXISTS"; @@ -264,21 +264,21 @@ public class FileDownloader extends BaseController { return "rb".equalsIgnoreCase(check); } - private static String checkFilePath(String filepath) { + private static String checkSafeFilePath(String filepath) { filepath = CodecUtils.urlDecode(filepath); filepath = filepath.replace("\\", "/"); - CommonsUtils.checkFilePathAttack(filepath); + CommonsUtils.checkSafeFilePath(filepath); if (filepath.startsWith("_log/") || filepath.contains("/_log/") || filepath.startsWith("_backups/") || filepath.contains("/_backups/")) { - throw new SecurityException("Attack path detected : " + filepath); + throw new SecurityException("Attack path detected : " + CommonsUtils.escapeHtml(filepath)); } return filepath; } - private static boolean writeLocalFile(String filePath, boolean temp, HttpServletResponse response) throws IOException { - filePath = checkFilePath(filePath); - File file = temp ? RebuildConfiguration.getFileOfTemp(filePath) : RebuildConfiguration.getFileOfData(filePath); + private static boolean writeLocalFile(String filepath, boolean temp, HttpServletResponse response) throws IOException { + filepath = checkSafeFilePath(filepath); + File file = temp ? RebuildConfiguration.getFileOfTemp(filepath) : RebuildConfiguration.getFileOfData(filepath); return writeLocalFile(file, response); } diff --git a/src/main/java/com/rebuild/web/files/FileListController.java b/src/main/java/com/rebuild/web/files/FileListController.java index 750f1e60f..5ffb39f07 100644 --- a/src/main/java/com/rebuild/web/files/FileListController.java +++ b/src/main/java/com/rebuild/web/files/FileListController.java @@ -63,9 +63,9 @@ public class FileListController extends BaseController { path = "attachment"; } else { path = ServletUtils.readCookie(request, CK_LASTPATH); - if (path == null) path = "docs"; + path = "attachment".equals(path) ? path : "docs"; } - + // 记住最后一次访问的文件类型 ServletUtils.addCookie(response, CK_LASTPATH, path); diff --git a/src/main/java/com/rebuild/web/general/GeneralOperatingController.java b/src/main/java/com/rebuild/web/general/GeneralOperatingController.java index 42e20bcab..a7c4834e5 100644 --- a/src/main/java/com/rebuild/web/general/GeneralOperatingController.java +++ b/src/main/java/com/rebuild/web/general/GeneralOperatingController.java @@ -451,24 +451,16 @@ public class GeneralOperatingController extends BaseController { // 操作 ID 列表 private ID[] parseIdList(HttpServletRequest request) { - String ids = getParameterNotNull(request, "id"); + ID[] idList = getIdArrayParameter(request, "id"); + if (idList.length == 0) return idList; - Set idList = new HashSet<>(); - int sameEntityCode = 0; - for (String id : ids.split(",")) { - if (!ID.isId(id)) { - continue; - } - ID id0 = ID.valueOf(id); - if (sameEntityCode == 0) { - sameEntityCode = id0.getEntityCode(); - } - if (sameEntityCode != id0.getEntityCode()) { + int mustSameEntityCode = idList[0].getEntityCode(); + for (ID id : idList) { + if (mustSameEntityCode != id.getEntityCode()) { throw new InvalidParameterException(Language.L("只能批量处理同一实体的记录")); } - idList.add(ID.valueOf(id)); } - return idList.toArray(new ID[0]); + return idList; } // 用户列表 @@ -482,9 +474,7 @@ public class GeneralOperatingController extends BaseController { // 级联操作实体 private String[] parseCascades(HttpServletRequest request) { String cascades = getParameter(request, "cascades"); - if (StringUtils.isBlank(cascades)) { - return ArrayUtils.EMPTY_STRING_ARRAY; - } + if (StringUtils.isBlank(cascades)) return ArrayUtils.EMPTY_STRING_ARRAY; List casList = new ArrayList<>(); for (String c : cascades.split(",")) { diff --git a/src/main/resources/web/assets/css/rb-page.css b/src/main/resources/web/assets/css/rb-page.css index 800dd62bb..4d6c9f13a 100644 --- a/src/main/resources/web/assets/css/rb-page.css +++ b/src/main/resources/web/assets/css/rb-page.css @@ -1184,6 +1184,14 @@ a.link.link-icon::after { line-height: 19px; } +.img-field.drop-area-active, +.file-field.drop-area-active { + background-color: #fff7c5; + background-color: #fff; + opacity: 0.6; + cursor: copy; +} + .img-field.avatar label.img-upload b { width: 100%; top: auto; diff --git a/src/main/resources/web/assets/css/view-page.css b/src/main/resources/web/assets/css/view-page.css index fd8e24ee5..ef9cc9977 100644 --- a/src/main/resources/web/assets/css/view-page.css +++ b/src/main/resources/web/assets/css/view-page.css @@ -496,7 +496,7 @@ body { .type-AVATAR .edit-oper, .type-SIGN .edit-oper { - margin-top: 7px; + margin-top: 6px; } .type-IMAGE .edit-oper { diff --git a/src/main/resources/web/assets/js/admin/recycle-bin.js b/src/main/resources/web/assets/js/admin/recycle-bin.js index 87eaabe68..86876ed61 100644 --- a/src/main/resources/web/assets/js/admin/recycle-bin.js +++ b/src/main/resources/web/assets/js/admin/recycle-bin.js @@ -65,6 +65,13 @@ class DataList extends React.Component { $('.J_restore').on('click', () => this.restore()) $('.J_details').on('click', () => this.showDetails()) + if (rb.commercial < 1) { + $('.J_restore, .J_details') + .off('click') + .on('click', () => { + RbHighbar.error(WrapHtml($L('免费版不支持此功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)'))) + }) + } } queryList() { diff --git a/src/main/resources/web/assets/js/admin/revision-history.js b/src/main/resources/web/assets/js/admin/revision-history.js index 22b638868..c9fb35b82 100644 --- a/src/main/resources/web/assets/js/admin/revision-history.js +++ b/src/main/resources/web/assets/js/admin/revision-history.js @@ -76,6 +76,13 @@ class DataList extends React.Component { this._$recordName = $input $('.J_details').on('click', () => this.showDetails()) + if (rb.commercial < 1) { + $('.J_details') + .off('click') + .on('click', () => { + RbHighbar.error(WrapHtml($L('免费版不支持此功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)'))) + }) + } } queryList() { diff --git a/src/main/resources/web/assets/js/charts/charts.js b/src/main/resources/web/assets/js/charts/charts.js index 33fcdbef4..5032781f6 100644 --- a/src/main/resources/web/assets/js/charts/charts.js +++ b/src/main/resources/web/assets/js/charts/charts.js @@ -45,7 +45,7 @@ class BaseChart extends React.Component { )} this.export()}> - {$L('导出')} + {$L('导出')} @@ -129,6 +129,11 @@ class BaseChart extends React.Component { } export() { + if (rb.commercial < 1) { + RbHighbar.error(WrapHtml($L('免费版不支持此功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)'))) + return + } + if (this._echarts) { const base64 = this._echarts.getDataURL({ type: 'png', diff --git a/src/main/resources/web/assets/js/files/files-docs.js b/src/main/resources/web/assets/js/files/files-docs.js index 872b335e7..dfb4411a3 100644 --- a/src/main/resources/web/assets/js/files/files-docs.js +++ b/src/main/resources/web/assets/js/files/files-docs.js @@ -314,6 +314,7 @@ class FileUploadDlg extends RbFormHandler { }) .on('dragover', (e) => { e.preventDefault() + if (e.originalEvent.dataTransfer) e.originalEvent.dataTransfer.dropEffect = 'copy' $da.find('.upload-box').addClass('active') }) .on('dragleave', (e) => { @@ -322,7 +323,7 @@ class FileUploadDlg extends RbFormHandler { }) .on('drop', function (e) { e.preventDefault() - const files = e.originalEvent.dataTransfer.files + const files = e.originalEvent.dataTransfer ? e.originalEvent.dataTransfer.files : null if (!files || files.length === 0) return false that._$upload.files = files $(that._$upload).trigger('change') diff --git a/src/main/resources/web/assets/js/general/rb-forms.js b/src/main/resources/web/assets/js/general/rb-forms.js index 17b1e48df..48636505b 100644 --- a/src/main/resources/web/assets/js/general/rb-forms.js +++ b/src/main/resources/web/assets/js/general/rb-forms.js @@ -1507,7 +1507,7 @@ class RbFormImage extends RbFormElement { } return ( -
+
(this._$dropArea = c)}> {value.map((item, idx) => { return ( @@ -1595,7 +1595,10 @@ class RbFormImage extends RbFormElement { } let mp + let mpCount = 0 const mp_end = function () { + if (--mpCount > 0) return + mpCount = 0 setTimeout(() => { if (mp) mp.end() mp = null @@ -1605,6 +1608,7 @@ class RbFormImage extends RbFormElement { $createUploader( this._fieldValue__input, (res) => { + mpCount++ if (!mp) mp = new Mprogress({ template: 2, start: true }) mp.set(res.percent / 100) // 0.x }, @@ -1612,14 +1616,44 @@ class RbFormImage extends RbFormElement { mp_end() const paths = this.state.value || [] // 最多上传,多余忽略 - // FIXME 多选时进度条有点不好看 if (paths.length < this.__maxUpload) { - paths.push(res.key) - this.handleChange({ target: { value: paths } }, true) + let hasByName = $fileCutName(res.key) + hasByName = paths.find((x) => $fileCutName(x) === hasByName) + console.log(hasByName) + if (!hasByName) { + paths.push(res.key) + this.handleChange({ target: { value: paths } }, true) + } } }, () => mp_end() ) + + // 拖拽上传 + if (this._$dropArea && !this.props.imageCapture) { + const that = this + const $da = $(this._$dropArea) + .on('dragenter', (e) => { + e.preventDefault() + }) + .on('dragover', (e) => { + e.preventDefault() + if (e.originalEvent.dataTransfer) e.originalEvent.dataTransfer.dropEffect = 'copy' + $da.addClass('drop-area-active') + }) + .on('dragleave', (e) => { + e.preventDefault() + $da.removeClass('drop-area-active') + }) + .on('drop dragdrop', function (e) { + e.preventDefault() + const files = e.originalEvent.dataTransfer ? e.originalEvent.dataTransfer.files : null + if (!files || files.length === 0) return false + that._fieldValue__input.files = files + $(that._fieldValue__input).trigger('change') + $da.removeClass('drop-area-active') + }) + } } } @@ -1666,7 +1700,7 @@ class RbFormFile extends RbFormImage { } return ( -
+
(this._$dropArea = c)}> {value.map((item) => { const fileName = $fileCutName(item) return ( @@ -1724,6 +1758,7 @@ class RbFormFile extends RbFormImage { ) } } + class RbFormPickList extends RbFormElement { constructor(props) { super(props) diff --git a/src/main/resources/web/assets/js/media-capturer.js b/src/main/resources/web/assets/js/media-capturer.js index bd8067c81..d54e7d2cf 100644 --- a/src/main/resources/web/assets/js/media-capturer.js +++ b/src/main/resources/web/assets/js/media-capturer.js @@ -57,8 +57,8 @@ class MediaCapturer extends RbModal {
{this.state.webcamList.map((c) => { return ( - this.initDevice(null, c[0])}> - {c[1]} + this.initDevice(null, c.deviceId)}> + {c.label || '0'} ) })} @@ -83,12 +83,8 @@ class MediaCapturer extends RbModal { navigator.mediaDevices .enumerateDevices() .then((devices) => { - const devices2 = devices.filter((device) => device.kind === 'videoinput') - const devices3 = [] - devices2.forEach((device, idx) => { - devices3.push([device.deviceId, device.label || idx]) - }) - this.setState({ webcamList: devices3 }) + const vidDevices = devices.filter((device) => device.kind === 'videoinput') + this.setState({ webcamList: vidDevices }) }) .catch((err) => { console.log(err) diff --git a/src/main/resources/web/assets/js/metadata/auto-fillin.js b/src/main/resources/web/assets/js/metadata/auto-fillin.js index cea72c2f0..b3dfcbb11 100644 --- a/src/main/resources/web/assets/js/metadata/auto-fillin.js +++ b/src/main/resources/web/assets/js/metadata/auto-fillin.js @@ -144,7 +144,7 @@ class DlgRuleEdit extends RbFormHandler {
diff --git a/src/main/resources/web/assets/js/rb-page.js b/src/main/resources/web/assets/js/rb-page.js index eac0b34dd..6251135c5 100644 --- a/src/main/resources/web/assets/js/rb-page.js +++ b/src/main/resources/web/assets/js/rb-page.js @@ -132,16 +132,21 @@ $(function () { } var bosskey = 0 - $(document.body).on('keydown', function (e) { + var bosskey_timer + $(document).on('keydown', function (e) { if (e.shiftKey) { if (++bosskey === 6) { $('.bosskey-show').removeClass('bosskey-show') typeof window.bosskeyTrigger === 'function' && window.bosskeyTrigger() window.__BOSSKEY = true } - setTimeout(function () { - --bosskey - }, 50000) // clean + if (bosskey_timer) { + clearTimeout(bosskey_timer) + bosskey_timer = null + } + bosskey_timer = setTimeout(function () { + bosskey = 0 + }, 1000) // clean } }) diff --git a/src/main/resources/web/error/server-status.html b/src/main/resources/web/error/server-status.html index 075b8faf2..55008aec9 100644 --- a/src/main/resources/web/error/server-status.html +++ b/src/main/resources/web/error/server-status.html @@ -57,11 +57,15 @@ [[${MemoryUsageJvm[1]}]]% ([[${MemoryUsageJvm[0]}]]MB) - Memory Usage (OS) + Memory Usage [[${MemoryUsage[1]}]]% ([[${MemoryUsage[0]}]]MB) - System Load (OS) + Disk Usage + [[${DisksDesc ?:'n/a'}]] + + + System Load [[${SystemLoad}]] diff --git a/src/test/java/com/rebuild/api/sdk/OpenApiSDK.java b/src/test/java/com/rebuild/api/sdk/OpenApiSDK.java index 6017bc7eb..dda8378fe 100644 --- a/src/test/java/com/rebuild/api/sdk/OpenApiSDK.java +++ b/src/test/java/com/rebuild/api/sdk/OpenApiSDK.java @@ -10,6 +10,7 @@ package com.rebuild.api.sdk; import cn.devezhao.commons.EncryptUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.rebuild.utils.JSONUtils; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; @@ -159,7 +160,11 @@ public class OpenApiSDK { try (Response response = okHttpClient.newCall(request).execute()) { String resp = Objects.requireNonNull(response.body()).string(); - return (JSON) JSON.parse(resp); + if (JSONUtils.wellFormat(resp)) { + return (JSON) JSON.parse(resp); + } else { + return JSONUtils.toJSONObject("__NOTJSON", resp); + } } } diff --git a/src/test/java/com/rebuild/utils/OshiUtilsTest.java b/src/test/java/com/rebuild/utils/OshiUtilsTest.java index b6d357506..158b18d26 100644 --- a/src/test/java/com/rebuild/utils/OshiUtilsTest.java +++ b/src/test/java/com/rebuild/utils/OshiUtilsTest.java @@ -39,4 +39,11 @@ class OshiUtilsTest { void getNetworkDate() { System.out.println(OshiUtils.getNetworkDate()); } + + @Test + void getDiskUsed() { + for (Object[] d : OshiUtils.getDisksUsed()) { + System.out.println(Arrays.toString(d)); + } + } } \ No newline at end of file