Approval history (#488)

* rev: approval

* open some syscfg
This commit is contained in:
RB 2022-07-19 23:54:09 +08:00 committed by GitHub
parent 49211c8e69
commit d73df479b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 103 additions and 64 deletions

View file

@ -483,7 +483,7 @@ public class PrivilegesManager {
* @param entry
* @return
* @see ZeroPrivileges
* @see ZeroPermission
* @see InternalPermission
*/
public boolean allow(ID user, ZeroEntry entry) {
Boolean a = userAllow(user);
@ -497,7 +497,7 @@ public class PrivilegesManager {
}
if (role.hasPrivileges(entry.name())) {
return role.getPrivileges(entry.name()).allowed(ZeroPermission.ZERO);
return role.getPrivileges(entry.name()).allowed(InternalPermission.ZERO);
}
return entry.getDefaultVal();
}

View file

@ -15,7 +15,21 @@ import cn.devezhao.bizz.privileges.impl.BizzPermission;
* @see BizzPermission
* @since 10/12/2018
*/
public class ZeroPermission {
public class InternalPermission {
/**
* 扩展权限
*/
public static final Permission ZERO = new BizzPermission("ZERO", 0, true);
/**
* 删除前触发的动作
*/
public static final Permission DELETE_BEFORE = new BizzPermission("DELETE_BEFORE", 0, false);
/**
* 审批
*/
public static final Permission APPROVAL = new BizzPermission("APPROVAL", 0, false);
}

View file

@ -22,6 +22,7 @@ import com.rebuild.core.metadata.easymeta.DisplayType;
import com.rebuild.core.metadata.easymeta.EasyField;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.privileges.UserService;
import com.rebuild.core.privileges.bizz.InternalPermission;
import com.rebuild.core.privileges.bizz.User;
import com.rebuild.core.service.BaseService;
import com.rebuild.core.service.DataSpecificationException;
@ -723,5 +724,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
triggerManual.onApproved(
OperatingContext.create(opUser, BizzPermission.UPDATE, before, approvalRecord));
}
// 手动记录历史
new RevisionHistoryObserver().onApprovalManual(
OperatingContext.create(opUser, InternalPermission.APPROVAL, before, approvalRecord));
}
}

View file

@ -7,7 +7,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.core.service.general;
import cn.devezhao.bizz.privileges.Permission;
import cn.devezhao.bizz.privileges.impl.BizzPermission;
import cn.devezhao.persist4j.PersistManagerFactory;
import cn.devezhao.persist4j.Record;
@ -15,6 +14,7 @@ import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.UserContextHolder;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.privileges.bizz.InternalPermission;
import com.rebuild.core.service.BaseService;
import com.rebuild.core.service.NoRecordFoundException;
import com.rebuild.core.service.ServiceSpec;
@ -36,11 +36,6 @@ import java.util.*;
@Slf4j
public abstract class ObservableService extends Observable implements ServiceSpec {
/**
* 删除前触发的动作
*/
public static final Permission DELETE_BEFORE = new BizzPermission("DELETE_BEFORE", 0, false);
final protected ServiceSpec delegateService;
/**
@ -100,7 +95,7 @@ public abstract class ObservableService extends Observable implements ServiceSpe
// 删除前触发做一些状态保持
setChanged();
notifyObservers(OperatingContext.create(currentUser, DELETE_BEFORE, deleted, null));
notifyObservers(OperatingContext.create(currentUser, InternalPermission.DELETE_BEFORE, deleted, null));
}
int affected = delegateService.delete(recordId);

View file

@ -9,6 +9,7 @@ package com.rebuild.core.service.general;
import cn.devezhao.bizz.privileges.impl.BizzPermission;
import cn.devezhao.commons.ThreadPool;
import com.rebuild.core.privileges.bizz.InternalPermission;
import lombok.extern.slf4j.Slf4j;
import java.util.Observable;
@ -52,7 +53,7 @@ public abstract class OperatingObserver implements Observer {
onCreate(ctx);
} else if (ctx.getAction() == BizzPermission.UPDATE) {
onUpdate(ctx);
} else if (ctx.getAction() == ObservableService.DELETE_BEFORE) {
} else if (ctx.getAction() == InternalPermission.DELETE_BEFORE) {
onDeleteBefore(ctx);
} else if (ctx.getAction() == BizzPermission.DELETE) {
onDelete(ctx);

View file

@ -135,7 +135,7 @@ public class RecordDifference {
|| EntityHelper.CreatedOn.equalsIgnoreCase(fieldName)
|| EntityHelper.CreatedBy.equalsIgnoreCase(fieldName)
|| EntityHelper.QuickCode.equalsIgnoreCase((fieldName))
|| MetadataHelper.isApprovalField(fieldName)
|| (MetadataHelper.isApprovalField(fieldName) && !EntityHelper.ApprovalState.equalsIgnoreCase(fieldName))
|| field.getType() == FieldType.PRIMARY;
}
}

View file

@ -15,6 +15,8 @@ import com.rebuild.core.Application;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.privileges.UserService;
import com.rebuild.core.privileges.bizz.InternalPermission;
import com.rebuild.core.service.approval.ApprovalState;
import com.rebuild.core.service.general.recyclebin.RecycleBinCleanerJob;
import com.rebuild.core.service.trigger.RobotTriggerObserver;
import com.rebuild.core.service.trigger.TriggerSource;
@ -37,18 +39,12 @@ public class RevisionHistoryObserver extends OperatingObserver {
// 激活
if (RecycleBinCleanerJob.isEnableRevisionHistory()) {
super.updateByAction(ctx);
} else if (ctx.getAction() != ObservableService.DELETE_BEFORE) {
} else if (ctx.getAction() != InternalPermission.DELETE_BEFORE) {
log.warn("RevisionHistory inactivated! {} {} by {}",
ctx.getAction().getName(), ctx.getAnyRecord().getPrimary(), ctx.getOperator());
}
}
/**
* 忽略的
*
* @param ctx
* @return
*/
private boolean isIgnore(OperatingContext ctx) {
int entity = ctx.getAnyRecord().getEntity().getEntityCode();
return entity == EntityHelper.FeedsComment || entity == EntityHelper.ProjectTaskComment;
@ -139,7 +135,22 @@ public class RevisionHistoryObserver extends OperatingObserver {
return record;
}
// TODO 异步无法获知是否关联操作
/**
* 审批通过/撤销
*
* @param context
*/
public void onApprovalManual(OperatingContext context) {
Record revision = newRevision(context, true);
if (context.getAfterRecord().getInt(EntityHelper.ApprovalState) == ApprovalState.APPROVED.getState()) {
revision.setInt("revisionType", 991);
} else {
revision.setInt("revisionType", 992);
}
Application.getCommonsService().create(revision);
}
// FIXME 异步无法获知是否关联操作
@Override
protected boolean isAsync() {
return false;

View file

@ -327,9 +327,7 @@ public class QiniuCloud {
*/
public static long getStorageSize() {
Long size = (Long) Application.getCommonsCache().getx("_StorageSize");
if (size != null) {
return size;
}
if (size != null) return size;
if (QiniuCloud.instance().available()) {
size = QiniuCloud.instance().stats();

View file

@ -84,12 +84,18 @@ public class ConfigurationController extends BaseController {
return RespBody.errorl("无效主页地址/域名");
}
String dPortalOfficePreviewUrl = defaultIfBlank(data, ConfigurationItem.PortalOfficePreviewUrl);
if (StringUtils.isNotBlank(dPortalOfficePreviewUrl) && !RegexUtils.isUrl(dPortalOfficePreviewUrl)) {
return RespBody.errorl("无效文档预览服务地址");
}
// 验证数字参数
ConfigurationItem[] validNumbers = new ConfigurationItem[] {
ConfigurationItem.RecycleBinKeepingDays,
ConfigurationItem.RevisionHistoryKeepingDays,
ConfigurationItem.DBBackupsKeepingDays,
ConfigurationItem.PasswordExpiredDays
ConfigurationItem.PasswordExpiredDays,
ConfigurationItem.PortalUploadMaxSize,
};
for (ConfigurationItem item : validNumbers) {
String number = defaultIfBlank(data, item);

View file

@ -34,6 +34,9 @@
.syscfg.edit a.img-thumbnail:hover b {
display: inline-block;
}
.syscfg a[target='_blank']:hover {
text-decoration: underline;
}
.bgimg-img {
display: inline-block;
@ -93,22 +96,23 @@
</td>
</tr>
<tr>
<td>
[[${bundle.L('主页地址/域名')}]]
<p>[[${bundle.L('所有外部链接将以此作为前缀')}]]</p>
</td>
<td data-id="HomeURL" th:data-value="${HomeURL}">[[${HomeURL}]]</td>
<td>[[${bundle.L('主页地址/域名')}]]</td>
<td data-id="HomeURL" th:data-value="${HomeURL}" th:data-form-text="${bundle.L('所有外部链接将以此作为前缀')}">[[${HomeURL}]]</td>
</tr>
<tr>
<td>[[${bundle.L('默认语言')}]]</td>
<td data-id="DefaultLanguage" th:data-value="${DefaultLanguage}" id="_DefaultLanguage">[[${DefaultLanguage}]]</td>
</tr>
<tr>
<td>
[[${bundle.L('页脚')}]]
<p class="link">[(${bundle.L('仅适用于外部页面,支持 [MD 语法](https://getrebuild.com/docs/markdown-guide#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95)')})]</p>
<td>[[${bundle.L('页脚')}]]</td>
<td
data-id="PageFooter"
th:data-value="${PageFooter}"
data-optional="true"
th:data-form-text="${bundle.L('仅适用于外部页面,支持 [MD 语法](https://getrebuild.com/docs/markdown-guide#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95)')}"
>
<pre class="unstyle">[[${PageFooter ?:bundle.L('无')}]]</pre>
</td>
<td data-id="PageFooter" th:data-value="${PageFooter}" data-optional="true"><pre class="unstyle">[[${PageFooter ?:bundle.L('无')}]]</pre></td>
</tr>
<tr>
<td>[[${bundle.L('登录页每日一图')}]]</td>
@ -139,11 +143,14 @@
<td data-id="FileSharable" th:data-value="${FileSharable}">[[${FileSharable ? bundle.L('是') : bundle.L('否')}]]</td>
</tr>
<tr>
<td>
[[${bundle.L('公开注册')}]]
<p class="link">[(${bundle.L('允许用户 [自助注册](https://getrebuild.com/docs/admin/users#3.%20%E8%87%AA%E5%8A%A9%E6%B3%A8%E5%86%8C)')})]</p>
<td>[[${bundle.L('公开注册')}]]</td>
<td
data-id="OpenSignUp"
th:data-value="${OpenSignUp}"
th:data-form-text="${bundle.L('允许用户 [自助注册](https://getrebuild.com/docs/admin/users#3.%20%E8%87%AA%E5%8A%A9%E6%B3%A8%E5%86%8C)')}"
>
[[${OpenSignUp ? bundle.L('是') : bundle.L('否')}]]
</td>
<td data-id="OpenSignUp" th:data-value="${OpenSignUp}">[[${OpenSignUp ? bundle.L('是') : bundle.L('否')}]]</td>
</tr>
<tr>
<td>[[${bundle.L('登录验证码显示模式')}]]</td>
@ -169,7 +176,11 @@
</tr>
<tr>
<td>[[${bundle.L('启用两步验证')}]] <sup class="rbv"></sup></td>
<td th:data-id="${commercial > 1 ? 'Login2FAMode' : ''}" th:data-value="${Login2FAMode}" th:data-form-text="${bundle.L('请确保邮件/短信配置正确,否则无法发送/接收验证码,导致无法登录')}">
<td
th:data-id="${commercial > 1 ? 'Login2FAMode' : ''}"
th:data-value="${Login2FAMode}"
th:data-form-text="${bundle.L('请确保邮件/短信配置正确,否则无法发送/接收验证码,导致无法登录')}"
>
<th:block th:if="${Login2FAMode == '0'}">[[${bundle.L('不启用')}]]</th:block>
<th:block th:if="${Login2FAMode == '1'}">[[${bundle.L('手机或邮箱')}]]</th:block>
<th:block th:if="${Login2FAMode == '2'}">[[${bundle.L('仅手机')}]]</th:block>
@ -197,40 +208,33 @@
>
<pre class="unstyle">[[${AllowUsesIp ?:bundle.L('不限')}]]</pre>
</td>
</tr>
<tr class="bosskey-show">
<td>[[${bundle.L('同一用户允许多地登录')}]] <sup class="rbv"></sup></td>
<td th:data-id="${commercial > 0 ? 'MultipleSessions' : ''}" th:data-value="${MultipleSessions}">[[${MultipleSessions ? bundle.L('是') : bundle.L('否')}]]</td>
</tr>
</tr>
</tbody>
</table>
<h5>[[${bundle.L('数据安全')}]]</h5>
<table class="table">
<tbody>
<tr>
<td width="40%">
[[${bundle.L('数据自动备份')}]]
<p>[[${bundle.L('每日 0 点备份到数据目录,请预留足够磁盘空间')}]]</p>
<td width="40%">[[${bundle.L('数据自动备份')}]]</td>
<td data-id="DBBackupsEnable" th:data-value="${DBBackupsEnable}" th:data-form-text="${bundle.L('每日 0 点备份到数据目录,请预留足够磁盘空间')}">
[[${DBBackupsEnable ? bundle.L('是') : bundle.L('否')}]]
</td>
<td data-id="DBBackupsEnable" th:data-value="${DBBackupsEnable}">[[${DBBackupsEnable ? bundle.L('是') : bundle.L('否')}]]</td>
</tr>
<tr>
<td>[[${bundle.L('备份保留时间')}]]</td>
<td data-id="DBBackupsKeepingDays" th:data-value="${DBBackupsKeepingDays}">
[[${DBBackupsKeepingDays}]] [[${bundle.L('天')}]]
</td>
<td data-id="DBBackupsKeepingDays" th:data-value="${DBBackupsKeepingDays}">[[${DBBackupsKeepingDays}]] [[${bundle.L('天')}]]</td>
</tr>
<tr>
<td>[[${bundle.L('变更历史保留时间')}]]</td>
<td data-id="RevisionHistoryKeepingDays" th:data-value="${RevisionHistoryKeepingDays}">
[[${RevisionHistoryKeepingDays}]] [[${bundle.L('天')}]]
</td>
<td data-id="RevisionHistoryKeepingDays" th:data-value="${RevisionHistoryKeepingDays}">[[${RevisionHistoryKeepingDays}]] [[${bundle.L('天')}]]</td>
</tr>
<tr>
<td>[[${bundle.L('回收站保留时间')}]]</td>
<td data-id="RecycleBinKeepingDays" th:data-value="${RecycleBinKeepingDays}">
[[${RecycleBinKeepingDays}]] [[${bundle.L('天')}]]
</td>
<td data-id="RecycleBinKeepingDays" th:data-value="${RecycleBinKeepingDays}">[[${RecycleBinKeepingDays}]] [[${bundle.L('天')}]]</td>
</tr>
</tbody>
</table>
@ -241,6 +245,18 @@
<td width="40%">[[${bundle.L('在视图页显示修改历史')}]]</td>
<td data-id="ShowViewHistory" th:data-value="${ShowViewHistory}">[[${ShowViewHistory ? bundle.L('是') : bundle.L('否')}]]</td>
</tr>
<tr class="bosskey-show">
<td>[[${bundle.L('文件上传大小限制')}]]</td>
<td data-id="PortalUploadMaxSize" th:data-value="${PortalUploadMaxSize}" data-optional="true">[[${PortalUploadMaxSize}]] MB</td>
</tr>
<tr class="bosskey-show">
<td>[[${bundle.L('文档预览服务地址')}]]</td>
<td data-id="PortalOfficePreviewUrl" th:data-value="${PortalOfficePreviewUrl}" data-optional="true">[[${PortalOfficePreviewUrl ?:bundle.L('默认')}]]</td>
</tr>
<tr class="bosskey-show">
<td>[[${bundle.L('百度地图 AK')}]]</td>
<td data-id="PortalBaiduMapAk" th:data-value="${PortalBaiduMapAk}" data-optional="true">[[${PortalBaiduMapAk ?:bundle.L('默认')}]]</td>
</tr>
</tbody>
</table>
<div class="edit-footer">

View file

@ -46,6 +46,8 @@ const RevTypes = {
16: $L('分派'),
32: $L('共享'),
64: $L('取消共享'),
991: $L('审批通过'),
992: $L('审批撤销')
}
class DataList extends React.Component {

View file

@ -514,11 +514,11 @@ class ApprovalApproveForm extends ApprovalUsersForm {
)
})
} else {
this.post2(state, null, true)
this.post2(state, null)
}
}
post2(state, rejectNode, showAlert) {
post2(state, rejectNode) {
const aformData = {}
if (this.state.aform && state === 10) {
const fd = this._rbform.__FormData
@ -563,16 +563,7 @@ class ApprovalApproveForm extends ApprovalUsersForm {
})
}
if (showAlert) {
RbAlert.create(state === 11 ? $L('确认驳回此审批') : $L('确认同意此审批'), {
onConfirm: function () {
this.hide()
fn()
},
})
} else {
fn()
}
fn()
}
}