mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
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:
parent
9562020dc0
commit
76bf69318a
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit e26090af686fd25f9071b1776b303b3e8ae9491a
|
||||
Subproject commit b653c87672ac09e4277c012211f1a11db339e92b
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -393,7 +393,7 @@ public class QiniuCloud {
|
|||
}
|
||||
|
||||
/**
|
||||
* 读取文件
|
||||
* 读取本地存储文件 `$DATA/rb/`
|
||||
*
|
||||
* @param filepath
|
||||
* @return
|
||||
|
|
|
@ -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("<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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
// 记住最后一次访问的文件类型
|
||||
|
|
|
@ -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(",")) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -496,7 +496,7 @@ body {
|
|||
|
||||
.type-AVATAR .edit-oper,
|
||||
.type-SIGN .edit-oper {
|
||||
margin-top: 7px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.type-IMAGE .edit-oper {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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) {
|
||||
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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
if (JSONUtils.wellFormat(resp)) {
|
||||
return (JSON) JSON.parse(resp);
|
||||
} else {
|
||||
return JSONUtils.toJSONObject("__NOTJSON", resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue