Fix 3.6 beta2 (#726)

* enh: sanitizeHtml

* be: checkSafeFilePath

* Update @rbv

* feat: draop upload

* enh: getDisksUsed

* Update media-capturer.js


---------

Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
REBUILD 企业管理系统 2024-03-11 00:32:55 +08:00 committed by GitHub
parent 9562020dc0
commit 76bf69318a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 324 additions and 196 deletions

2
@rbv

@ -1 +1 @@
Subproject commit e26090af686fd25f9071b1776b303b3e8ae9491a
Subproject commit b653c87672ac09e4277c012211f1a11db339e92b

View file

@ -52,8 +52,8 @@ public final class ServerStatus {
* @return
*/
public static List<Status> getLastStatus(boolean realtime) {
// 60 秒缓存
if (realtime || System.currentTimeMillis() - LastCheckTime > 60 * 1000) {
// 30 秒缓存
if (realtime || System.currentTimeMillis() - LastCheckTime > 30 * 1000) {
checkAll();
}

View file

@ -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) {

View file

@ -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<ID> nextApprovers = nextNodes.getApproveUsers(this.getUser(), this.record, selectNextUsers);
Set<ID> nextApprovers = nextNodes.getApproveUsers(this.getUser(), this.recordId, selectNextUsers);
if (nextApprovers.isEmpty()) {
log.warn("No any approvers special");
return false;
}
Set<ID> ccUsers = nextNodes.getCcUsers(this.getUser(), this.record, selectNextUsers);
Set<String> ccAccounts = nextNodes.getCcAccounts(this.record);
Set<ID> ccUsers = nextNodes.getCcUsers(this.getUser(), this.recordId, selectNextUsers);
Set<String> 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<ID> ccs4share = nextNodes.getCcUsers4Share(this.getUser(), this.record, selectNextUsers);
share2CcIfNeed(this.record, ccs4share);
Set<ID> 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<ID> ccUsers = nextNodes.getCcUsers(this.getUser(), this.record, selectNextUsers);
Set<String> ccAccounts = nextNodes.getCcAccounts(this.record);
Set<ID> ccUsers = nextNodes.getCcUsers(this.getUser(), this.recordId, selectNextUsers);
Set<String> 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<ID> ccs4share = nextNodes.getCcUsers4Share(this.getUser(), this.record, selectNextUsers);
share2CcIfNeed(this.record, ccs4share);
Set<ID> 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)

View file

@ -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<String, Set<ID>> recordsOfCascaded = getCascadedRecords(record, cascades, BizzPermission.DELETE);
Map<String, Set<ID>> recordsOfCascaded = getCascadedRecords(recordId, cascades, BizzPermission.DELETE);
for (Map.Entry<String, Set<ID>> 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<String, Set<ID>> getCascadedRecords(ID recordMain, String[] cascadeEntities, Permission action) {
protected Map<String, Set<ID>> 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<String, Set<ID>> 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<String> 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);

View file

@ -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()) {

View file

@ -393,7 +393,7 @@ public class QiniuCloud {
}
/**
* 读取文件
* 读取本地存储文件 `$DATA/rb/`
*
* @param filepath
* @return

View file

@ -99,13 +99,31 @@ public class CommonsUtils {
// https://www.php.net/htmlspecialchars
return text.toString()
// .replace("&", "&amp;")
.replace("\"", "&quot;")
.replace("'", "&apos;")
.replace(">", "&gt;")
.replace("<", "&lt;");
}
/**
* @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("<script", "")
.replace("</script>", "")
.replace("<style", "")
.replace("</style>", "")
.replace("<img", "");
}
/**
* @param text
* @return
@ -164,6 +182,19 @@ public class CommonsUtils {
return hex;
}
/**
* 指定范围的随机数
*
* @param s
* @param e
* @return
* @see RandomUtils#nextInt(int)
*/
public static int randomInt(int s, int e) {
int rnd = RandomUtils.nextInt(e);
return rnd < s ? rnd + s : rnd;
}
/**
* 转整数四舍五入
*
@ -287,27 +318,14 @@ public class CommonsUtils {
return StringUtils.equals(a.toString(), b.toString());
}
/**
* 指定范围的随机数
*
* @param s
* @param e
* @return
* @see RandomUtils#nextInt(int)
*/
public static int randomInt(int s, int e) {
int rnd = RandomUtils.nextInt(e);
return rnd < s ? rnd + s : rnd;
}
/**
* @param filepath
* @throws SecurityException
*/
public static void checkFilePathAttack(String filepath) throws SecurityException {
public static void checkSafeFilePath(String filepath) throws SecurityException {
if (filepath == null) return;
if (filepath.contains("../") || filepath.contains("<") || filepath.contains(">")) {
throw new SecurityException("Attack path detected : " + filepath);
throw new SecurityException("Attack path detected : " + escapeHtml(filepath));
}
}
}

View file

@ -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<Object[]> getDisksUsed() {
List<Object[]> 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;
}
}

View file

@ -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;

View file

@ -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<Object[]> 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());
}

View file

@ -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);
}

View file

@ -63,7 +63,7 @@ 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";
}
// 记住最后一次访问的文件类型

View file

@ -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<ID> 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<String> casList = new ArrayList<>();
for (String c : cascades.split(",")) {

View file

@ -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;

View file

@ -496,7 +496,7 @@ body {
.type-AVATAR .edit-oper,
.type-SIGN .edit-oper {
margin-top: 7px;
margin-top: 6px;
}
.type-IMAGE .edit-oper {

View file

@ -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() {

View file

@ -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() {

View file

@ -45,7 +45,7 @@ class BaseChart extends React.Component {
</a>
)}
<a className="dropdown-item" onClick={() => this.export()}>
{$L('导出')}
{$L('导出')} <sup className="rbv" />
</a>
</div>
</div>
@ -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',

View file

@ -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')

View file

@ -1507,7 +1507,7 @@ class RbFormImage extends RbFormElement {
}
return (
<div className="img-field">
<div className="img-field" ref={(c) => (this._$dropArea = c)}>
{value.map((item, idx) => {
return (
<span key={item}>
@ -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 (
<div className="file-field">
<div className="file-field" ref={(c) => (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)

View file

@ -57,8 +57,8 @@ class MediaCapturer extends RbModal {
<div className="dropdown-menu dropdown-menu-right">
{this.state.webcamList.map((c) => {
return (
<a className="dropdown-item" key={c[0]} onClick={() => this.initDevice(null, c[0])}>
{c[1]}
<a className="dropdown-item" key={c.deviceId} onClick={() => this.initDevice(null, c.deviceId)}>
{c.label || '0'}
</a>
)
})}
@ -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)

View file

@ -144,7 +144,7 @@ class DlgRuleEdit extends RbFormHandler {
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mb-0">
<input className="custom-control-input" type="checkbox" checked={this.state.fillinBackend === true} data-id="fillinBackend" onChange={this.handleChange} />
<span className="custom-control-label">
{$L('使用后端回填')} <sup className="rbv"></sup>
{$L('使用后端回填')} <sup className="rbv" />
</span>
</label>
</div>

View file

@ -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
}
})

View file

@ -57,11 +57,15 @@
<td>[[${MemoryUsageJvm[1]}]]% ([[${MemoryUsageJvm[0]}]]MB)</td>
</tr>
<tr>
<th>Memory Usage (OS)</th>
<th>Memory Usage</th>
<td>[[${MemoryUsage[1]}]]% ([[${MemoryUsage[0]}]]MB)</td>
</tr>
<tr>
<th>System Load (OS)</th>
<th>Disk Usage</th>
<td>[[${DisksDesc ?:'n/a'}]]</td>
</tr>
<tr>
<th>System Load</th>
<td>[[${SystemLoad}]]</td>
</tr>
</tbody>

View file

@ -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);
}
}
}

View file

@ -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));
}
}
}