Better Language. Fixs cache of User
This commit is contained in:
devezhao-mbp 2019-12-05 01:41:53 +08:00
parent 0a531a5e4d
commit 2bcd2920d2
25 changed files with 261 additions and 179 deletions

View file

@ -24,7 +24,8 @@
"html"
],
"prettier.eslintIntegration": true,
"workbench.editor.enablePreview": false
"workbench.editor.enablePreview": false,
"java.configuration.updateBuildConfiguration": "disabled"
}
// node and eslint(-g)
// Plugins: Beautify and ESLint

View file

@ -195,6 +195,8 @@ public class SMSender {
}
/**
* 短信服务可用
*
* @return
*/
public static boolean availableSMS() {
@ -202,6 +204,8 @@ public class SMSender {
}
/**
* 邮件服务可用
*
* @return
*/
public static boolean availableMail() {

View file

@ -24,7 +24,7 @@ import com.rebuild.server.helper.cache.CommonCache;
import org.apache.commons.lang.math.RandomUtils;
/**
* 验证码
* 验证码助手
*
* @author devezhao
* @since 11/05/2018
@ -40,12 +40,14 @@ public class VCode {
}
/**
* 生成验证码
*
* @param key
* @param level complexity 1<2<3
* @return
*/
public static String generate(String key, int level) {
String vcode = null;
String vcode;
if (level == 3) {
vcode = CodecUtils.randomCode(20);
} else if (level == 2) {
@ -54,7 +56,8 @@ public class VCode {
vcode = RandomUtils.nextInt(999999999) + "888888";
vcode = vcode.substring(0, 6);
}
// 缓存 10 分钟
Application.getCommonCache().put("VCode-" + key, vcode, CommonCache.TS_HOUR / 6);
return vcode;
}
@ -63,12 +66,15 @@ public class VCode {
* @param key
* @param vcode
* @return
* @see #verfiy(String, String, boolean)
*/
public static boolean verfiy(String key, String vcode) {
return verfiy(key, vcode, false);
}
/**
* 验证是否有效
*
* @param key
* @param vcode
* @param keepAlive
@ -76,7 +82,11 @@ public class VCode {
* @see #clean(String)
*/
public static boolean verfiy(String key, String vcode, boolean keepAlive) {
String ckey = "VCode-" + key;
if (Application.devMode() && "rebuild".equalsIgnoreCase(vcode)) {
return true;
}
final String ckey = "VCode-" + key;
String exists = Application.getCommonCache().get(ckey);
if (exists == null) {
return false;
@ -92,6 +102,8 @@ public class VCode {
}
/**
* 清除验证码
*
* @param key
* @return
*/

View file

@ -111,6 +111,17 @@ public class LanguageBundle implements JSONable {
return lang;
}
/**
* @param key
* @param args
* @return
* @see String#format(String, Object...)
*/
public String formatLang(String key, Object... args) {
String lang = lang(key);
return String.format(lang, args);
}
/**
* @param key
* @return

View file

@ -138,8 +138,27 @@ public class Languages {
* @param key
* @param insideKeys
* @return
* @see #currentBundle()
*/
public static String lang(String key, String...insideKeys) {
return instance.getCurrentBundle().lang(key, insideKeys);
return currentBundle().lang(key, insideKeys);
}
/**
* 当前用户语言
*
* @return
*/
public static LanguageBundle currentBundle() {
return instance.getCurrentBundle();
}
/**
* 默认语言
*
* @return
*/
public static LanguageBundle defaultBundle() {
return instance.getDefaultBundle();
}
}

View file

@ -28,6 +28,7 @@ import com.rebuild.server.helper.BlackList;
import com.rebuild.server.helper.ConfigurableItem;
import com.rebuild.server.helper.SMSender;
import com.rebuild.server.helper.SysConfiguration;
import com.rebuild.server.helper.language.Languages;
import com.rebuild.server.helper.task.TaskExecutors;
import com.rebuild.server.metadata.EntityHelper;
import com.rebuild.server.service.DataSpecificationException;
@ -61,13 +62,25 @@ public class UserService extends SystemEntityService {
@Override
public Record create(Record record) {
final String passwd = record.getString("password");
saveBefore(record);
Record r = super.create(record);
Application.getUserStore().refreshUser(record.getPrimary());
notifyNewUser(r, passwd);
return r;
return create(record, true);
}
/**
* @param record
* @param notifyUser
* @return
*/
private Record create(Record record, boolean notifyUser) {
final String passwd = record.getString("password");
saveBefore(record);
record = super.create(record);
Application.getUserStore().refreshUser(record.getPrimary());
if (notifyUser) {
notifyNewUser(record, passwd);
}
return record;
}
@Override
public Record update(Record record) {
@ -99,7 +112,7 @@ public class UserService extends SystemEntityService {
}
if (record.hasValue("email") && Application.getUserStore().exists(record.getString("email"))) {
throw new DataSpecificationException("邮箱重复");
throw new DataSpecificationException(Languages.lang("Repeated", "Email"));
}
if (record.getPrimary() == null && !record.hasValue("fullName")) {
@ -136,7 +149,7 @@ public class UserService extends SystemEntityService {
*/
public void checkPassword(String password) throws DataSpecificationException {
if (password.length() < 6) {
throw new DataSpecificationException("密码不能小于6位");
throw new DataSpecificationException(Languages.lang("PasswordLevel1Tip"));
}
int policy = SysConfiguration.getInt(ConfigurableItem.PasswordPolicy);
@ -161,14 +174,13 @@ public class UserService extends SystemEntityService {
}
if (policy >= 2 && (countUpper == 0 || countLower == 0 || countDigit == 0)) {
throw new DataSpecificationException("密码必须包含数字和大小写字母");
throw new DataSpecificationException(Languages.lang("PasswordLevel2Tip"));
}
if (policy >= 3 && (countSpecial == 0 || password.length() < 8)) {
throw new DataSpecificationException("密码不能小于8位且必须包含特殊字符");
throw new DataSpecificationException(Languages.lang("PasswordLevel3Tip"));
}
}
private static final String MSG_NEWUSER = "<p>系统管理员已经为你开通了 %s 账号!以下为你的登录信息,请妥善保管。</p><div style='margin:10px 0'>登录账号 <b>%s</b><br>登录密码 <b>%s</b><br>登录地址 <a href='%s'>%s</a></div><p>首次登陆,建议你立即修改登陆密码。修改方式:登陆后点击右上角头像 - 个人设置 - 安全设置 - 更改密码</p>";
/**
* @param newUser
* @param passwd
@ -179,39 +191,18 @@ public class UserService extends SystemEntityService {
return false;
}
String subject = "你的 " + SysConfiguration.get(ConfigurableItem.AppName) + " 账号已就绪";
String content = String.format(MSG_NEWUSER,
SysConfiguration.get(ConfigurableItem.AppName),
newUser.getString("loginName"), passwd,
SysConfiguration.getHomeUrl(),
SysConfiguration.getHomeUrl());
String appName = SysConfiguration.get(ConfigurableItem.AppName);
String homeUrl = SysConfiguration.getHomeUrl();
String subject = Languages.defaultBundle().formatLang("YourAccountReady",
appName);
String content = Languages.defaultBundle().formatLang("NewUserAddedNotify",
appName, newUser.getString("loginName"), passwd, homeUrl, homeUrl);
SMSender.sendMail(newUser.getString("email"), subject, content);
return true;
}
/**
* 改变部门
*
* @param user
* @param deptNew
* @see #updateEnableUser(ID, ID, ID, Boolean)
*/
public void updateDepartment(ID user, ID deptNew) {
updateEnableUser(user, deptNew, null, null);
}
/**
* 改变角色
*
* @param user
* @param roleNew
* @see #updateEnableUser(ID, ID, ID, Boolean)
*/
public void updateRole(ID user, ID roleNew) {
updateEnableUser(user, null, roleNew, null);
}
/**
* 入参值为 null 表示不做修改
*
@ -252,8 +243,7 @@ public class UserService extends SystemEntityService {
// 改变记录的所属部门
if (deptOld != null) {
TaskExecutors.submit(
new ChangeOwningDeptTask(user, deptNew), Application.getCurrentUser());
TaskExecutors.submit(new ChangeOwningDeptTask(user, deptNew), Application.getCurrentUser());
}
}
@ -262,20 +252,15 @@ public class UserService extends SystemEntityService {
*
* @param record
*/
public void txSignUp(Record record) {
if (!SysConfiguration.getBool(ConfigurableItem.OpenSignUp)) {
throw new DataSpecificationException("管理员未开放公开注册");
}
record = this.create(record);
public ID txSignUp(Record record) {
record = this.create(record, false);
ID newUserId = record.getPrimary();
String content = String.format(
"用户 @%s 提交了注册申请。请验证用户有效性后为其启用并指定部门和角色,以便用户登录使用。如果这是一个无效的注册申请请忽略。"
+ "[点击此处激活](%s/admin/bizuser/users#!/View/User/%s)",
newUserId, AppUtils.getContextPath(), newUserId);
Message message = MessageBuilder.createMessage(ADMIN_USER, content);
String viewUrl = AppUtils.getContextPath() + "/app/list-and-view?id=" + newUserId;
String content = Languages.defaultBundle().formatLang("NewUserSignupNotify", newUserId, viewUrl);
Message message = MessageBuilder.createMessage(ADMIN_USER, content, newUserId);
Application.getNotifications().send(message);
return newUserId;
}
}

View file

@ -194,7 +194,7 @@ public class UserStore {
}
return top.toArray(new Department[0]);
}
/**
* @param roleId
* @return
@ -246,19 +246,21 @@ public class UserStore {
.unique();
final User newUser = new User(
userId, (String) o[1], (String) o[2], (String) o[3], (String) o[4], (Boolean) o[5]);
final ID deptId = (ID) o[6];
final ID roleId = (ID) o[7];
final User oldUser = exists(userId) ? getUser(userId) : null;
if (oldUser != null) {
Role role = oldUser.getOwningRole();
if (role != null) {
role.removeMember(oldUser);
role.addMember(newUser);
}
}
Department dept = oldUser.getOwningDept();
if (dept != null) {
dept.removeMember(oldUser);
dept.addMember(newUser);
}
for (Team team : oldUser.getOwningTeams().toArray(new Team[0])) {
team.removeMember(oldUser);
team.addMember(newUser);
@ -268,13 +270,13 @@ public class UserStore {
if (oldUser.getEmail() != null) {
USERs_MAIL2ID.remove(normalIdentifier(oldUser.getEmail()));
}
} else {
if (o[6] != null) {
getDepartment((ID) o[6]).addMember(newUser);
}
if (o[7] != null) {
getRole((ID) o[7]).addMember(newUser);
}
}
if (deptId != null) {
getDepartment(deptId).addMember(newUser);
}
if (roleId != null) {
getRole(roleId).addMember(newUser);
}
store(newUser);
@ -530,7 +532,7 @@ public class UserStore {
}
}
// 组织关系
// 组织部门关系
for (Map.Entry<ID, Set<ID>> e : parentTemp.entrySet()) {
BusinessUnit parent = getDepartment(e.getKey());
for (ID child : e.getValue()) {

View file

@ -116,7 +116,7 @@ public class MessageBuilder {
Matcher atMatcher = AT_PATTERN.matcher(message);
while (atMatcher.find()) {
String at = atMatcher.group();
String atLabel = parseAtId(at.substring(1));
String atLabel = parseAtsId(at.substring(1));
if (atLabel != null && !atLabel.equals(at)) {
message = message.replace(at, atLabel);
}
@ -132,7 +132,7 @@ public class MessageBuilder {
* @param atid
* @return
*/
static String parseAtId(String atid) {
protected static String parseAtsId(String atid) {
if (!ID.isId(atid)) {
return atid;
}

View file

@ -68,6 +68,11 @@ public class UserControll extends BaseEntityControll {
@RequestMapping("check-user-status")
public void checkUserStatus(HttpServletRequest request, HttpServletResponse response) throws IOException {
ID id = getIdParameterNotNull(request, "id");
if (!Application.getUserStore().exists(id)) {
writeFailure(response);
return;
}
User checkedUser = Application.getUserStore().getUser(id);
Map<String, Object> ret = new HashMap<>();

View file

@ -118,7 +118,9 @@ public class GeneralDataListControll extends BaseEntityControll {
url = MessageFormat.format("{0}/list#!/View/{0}/{1}", entity.getName(), id);
} else if (entity.getEntityCode() == EntityHelper.Feeds) {
url = "../feeds/home#s=" + id;
}
} else if (entity.getEntityCode() == EntityHelper.User) {
url = MessageFormat.format("../admin/bizuser/users#!/View/{0}/{1}", entity.getName(), id);
}
}
if (url != null) {

View file

@ -29,6 +29,7 @@ import com.rebuild.server.helper.ConfigurableItem;
import com.rebuild.server.helper.SMSender;
import com.rebuild.server.helper.SysConfiguration;
import com.rebuild.server.helper.VCode;
import com.rebuild.server.helper.language.Languages;
import com.rebuild.server.metadata.EntityHelper;
import com.rebuild.server.service.DataSpecificationException;
import com.rebuild.server.service.bizz.UserService;
@ -47,7 +48,7 @@ import java.io.IOException;
import static com.rebuild.server.helper.language.Languages.lang;
/**
* 注册
* 用户自助注册
*
* @author devezhao
* @since 11/01/2018
@ -57,7 +58,7 @@ import static com.rebuild.server.helper.language.Languages.lang;
public class SignUpControll extends BasePageControll {
@RequestMapping("signup")
public ModelAndView pageSignup(HttpServletRequest request, HttpServletResponse response) throws IOException {
public ModelAndView pageSignup(HttpServletResponse response) throws IOException {
if (!SysConfiguration.getBool(ConfigurableItem.OpenSignUp)) {
response.sendError(400, lang("SignupNotOpenTip"));
return null;
@ -119,7 +120,8 @@ public class SignUpControll extends BasePageControll {
Application.getBean(UserService.class).txSignUp(userNew);
String homeUrl = SysConfiguration.getHomeUrl();
String content = String.format(lang("SignupPending"), fullName, loginName, passwd, homeUrl, homeUrl);
String content = Languages.currentBundle().formatLang("SignupPending",
fullName, loginName, passwd, homeUrl, homeUrl);
SMSender.sendMail(email, lang("AdminReviewSignup"), content);
writeSuccess(response);
} catch (DataSpecificationException ex) {

View file

@ -26,7 +26,7 @@
"ReturnHome": "Go Home",
"ReportIssue": "Report an issue",
"SignupNotOpenTip": "Administrator has not opened public signup",
"UnsetEmailTip": "If you forgot or did not configure your email, please contact the administrator to reset your password",
"UnsetEmailTip": "If you forgot or unfilled email, please contact your administrator to reset your password",
"InputEmailPls": "Please enter email",
"InputNewPasswordPls": "Enter new password",
"PasswordNotMatch": "The new password not match.",

View file

@ -26,7 +26,7 @@
"ReturnHome": "返回首页",
"ReportIssue": "报告此问题",
"SignupNotOpenTip": "管理员未开放公开注册",
"UnsetEmailTip": "如果你忘记或未配置邮箱,请联系管理员重置密码",
"UnsetEmailTip": "如果你忘记或未填写邮箱,请联系管理员重置密码",
"InputEmailPls": "请输入邮箱",
"InputNewPasswordPls": "请输入新密码",
"PasswordNotMatch": "两次输入的新密码不一致",
@ -62,5 +62,12 @@
"Retry": "重试",
"ErrorUnknow": "未知错误,请稍后重试",
"Error404": "访问的地址/资源不存在",
"Error403": "权限不足,访问被阻止"
"Error403": "权限不足,访问被阻止",
"NewUserAddedNotify": "<p>系统管理员已经为你开通了 %s 账号!以下为你的登录信息,请妥善保管。</p><div style='margin:10px 0'>登录账号 <b>%s</b><br>登录密码 <b>%s</b><br>登录地址 <a href='%s'>%s</a></div><p>首次登陆,建议你立即修改登陆密码。修改方式:登陆后点击右上角头像 - 个人设置 - 安全设置 - 更改密码</p>",
"YourAccountReady": "你的 %s 账号已就绪",
"NewUserSignupNotify": "用户 @%s 提交了注册申请。请验证用户有效性后为其启用并指定部门和角色,以便用户登录使用。如果这是一个无效申请请忽略。[点击此处激活](%s)",
"PasswordLevel1Tip": "密码不能小于6位",
"PasswordLevel2Tip": "密码必须包含数字和大小写字母",
"PasswordLevel3Tip": "密码不能小于8位且必须包含特殊字符",
"Repeated": "{0}重复"
}

View file

@ -82,7 +82,11 @@ window.__PageConfig = {
<script src="${baseUrl}/assets/js/rb-forms.exts.jsx" type="text/babel"></script>
<script src="${baseUrl}/assets/js/bizuser/dept-tree.js"></script>
<script type="text/babel">
RbForm.postAfter = loadDeptTree
let RbForm_postAfter = RbForm.postAfter
RbForm.postAfter = function () {
RbForm_postAfter()
loadDeptTree()
}
$(document).ready(loadDeptTree)
clickDept = function(depts) {
if (depts[0] == '$ALL$') depts = []

View file

@ -53,36 +53,6 @@ window.__PageConfig = {
<script src="${baseUrl}/assets/js/rb-forms.jsx" type="text/babel"></script>
<script src="${baseUrl}/assets/js/rb-forms.exts.jsx" type="text/babel"></script>
<script src="${baseUrl}/assets/js/rb-view.jsx" type="text/babel"></script>
<script type="text/babel">
let RbForm_postAfter = RbForm.postAfter
RbForm.postAfter = function() {
RbForm_postAfter()
if (parent && parent.loadDeptTree) parent.loadDeptTree()
}
$(document).ready(function() {
$('.J_delete').off('click').click(function() {
$.get(rb.baseUrl + '/admin/bizuser/delete-checks?id=${id}', function(res) {
if (res.data.hasMember == 0 && res.data.hasChild == 0){
RbAlert.create('此部门可以被安全的删除', '删除部门', { type: 'danger', confirmText: '删除', confirm: function(){ deleteDept(this) } })
} else {
let msg = '此部门下有 '
if (res.data.hasMember > 0) msg += '<b>' + res.data.hasMember + '</b> 个用户' + (res.data.hasMember > 0 ? '和 ' : ' ')
if (res.data.hasMember > 0) msg += '<b>' + res.data.hasMember + '</b> 个子部门'
msg += '<br>需要先将他们转移至其他部门,然后才能安全删除'
RbAlert.create(msg, '无法删除', { type: 'warning', html: true })
}
})
})
})
let deleteDept = function(dlg){
dlg.disabled(true)
$.post(rb.baseUrl + '/admin/bizuser/dept-delete?transfer=&id=${id}', function(res){
if (res.error_code == 0) {
parent.location.hash = '!/View/'
parent.location.reload()
} else RbHighbar.error(res.error_msg)
})
}
</script>
<script src="${baseUrl}/assets/js/bizuser/dept-view.jsx" type="text/babel"></script>
</body>
</html>

View file

@ -89,15 +89,15 @@ window.__PageConfig = {
<script src="${baseUrl}/assets/js/bizuser/dept-tree.js"></script>
<script type="text/babel">
let formPostType = 1
RbForm.postAfter = function(){
if (formPostType == 1) RbListPage._RbList.reload()
let RbForm_postAfter = RbForm.postAfter
RbForm.postAfter = function() {
if (formPostType == 1) RbForm_postAfter()
else loadDeptTree()
}
$(document).ready(function(){
loadDeptTree()
$('.J_new').click(function(){ formPostType = 1 })
$('.J_new-dept').click(function(){
$('.J_new').click(function() { formPostType = 1 })
$('.J_new-dept').click(function() {
formPostType = 2
RbFormModal.create({ title: '新建部门', entity: 'Department', icon: 'accounts' })
})

View file

@ -11587,7 +11587,7 @@ canvas {
}
.rb-top-header .navbar-nav>li.dropdown .dropdown-menu {
border-radius: 3px;
border-radius: 2px;
margin-top: 12px;
line-height: 20px
}
@ -12511,13 +12511,13 @@ a.avatar.x48 img {
border-bottom: 1px solid #ebebeb
}
.rb-notifications .notification>a {
.rb-notifications .notification>.a {
padding: 10px 15px;
display: block;
overflow: hidden
}
.rb-notifications .notification>a .image {
.rb-notifications .notification>.a .image {
height: 38px;
width: 38px;
border-radius: 50%;
@ -12529,7 +12529,7 @@ a.avatar.x48 img {
width: 32px;
}
.rb-notifications .notification>a .image img {
.rb-notifications .notification>.a .image img {
height: 38px;
width: 38px;
font-size: 1.923rem;
@ -12540,24 +12540,24 @@ a.avatar.x48 img {
line-height: 32px;
}
.rb-notifications .notification>a .notification-info {
.rb-notifications .notification>.a .notification-info {
padding-left: 50px;
line-height: 14px;
padding-left: 44px;
}
.rb-notifications .notification>a .notification-info .text {
.rb-notifications .notification>.a .notification-info .text {
color: #8a8a8a;
font-size: .9231rem;
line-height: 16px
}
.rb-notifications .notification>a .notification-info .user-name {
.rb-notifications .notification>.a .notification-info .user-name {
color: #4285f4;
font-size: 1rem
}
.rb-notifications .notification>a .notification-info .date {
.rb-notifications .notification>.a .notification-info .date {
color: #8a8a8a;
display: block;
font-size: .6923rem;
@ -12572,11 +12572,11 @@ a.avatar.x48 img {
border-bottom: 1px solid #dce9fd
}
.rb-notifications .notification.notification-unread>a {
.rb-notifications .notification.notification-unread>.a {
position: relative
}
.rb-notifications .notification.notification-unread>a:after {
.rb-notifications .notification.notification-unread>.a:after {
content: '';
display: block;
position: absolute;
@ -12597,25 +12597,25 @@ a.avatar.x48 img {
color: #fff
}
.rb-notifications .notification:hover>a .logo {
.rb-notifications .notification:hover>.a .logo {
background-color: #206ff2
}
.rb-notifications .notification:hover>a .logo .icon {
.rb-notifications .notification:hover>.a .logo .icon {
color: #fff
}
.rb-notifications .notification:hover>a .notification-info .circle {
.rb-notifications .notification:hover>.a .notification-info .circle {
background: #fff
}
.rb-notifications .notification:hover>a .notification-info .date,
.rb-notifications .notification:hover>a .notification-info .text,
.rb-notifications .notification:hover>a .notification-info .user-name {
.rb-notifications .notification:hover>.a .notification-info .date,
.rb-notifications .notification:hover>.a .notification-info .text,
.rb-notifications .notification:hover>.a .notification-info .user-name {
color: #fff
}
.rb-notifications .notification:hover>a:after {
.rb-notifications .notification:hover>.a:after {
background-color: #fff
}

View file

@ -1974,8 +1974,7 @@ form {
z-index: 1090;
}
.rb-notifications .notification>a .notification-info .text>p,
.card-body.rb-notifications .notification>a .text>p {
.rb-notifications .notification .text>p {
margin: 0;
}
@ -2456,7 +2455,7 @@ form {
padding-top: 45px
}
.notification-list .notification>a {
.notification-list .notification>.a {
cursor: default;
padding-left: 10px;
}
@ -2535,6 +2534,11 @@ form {
right: 0
}
@media (max-width:768px) {
.float-right .rb-notifications {
right: 0;
}
}
.select2-sm .select2-container--default .select2-selection--single,
.select2-sm .select2-container--default .select2-selection--multiple {

View file

@ -0,0 +1,41 @@
/* global RbForm */
let RbForm_postAfter = RbForm.postAfter
RbForm.postAfter = function () {
RbForm_postAfter()
if (parent && parent.loadDeptTree) parent.loadDeptTree()
}
const deleteDept = function (alert) {
alert && alert.disabled(true)
$.post(`${rb.baseUrl}/admin/bizuser/dept-delete?transfer=&id=${dept_id}`, (res) => {
if (res.error_code === 0) {
parent.location.hash = '!/View/'
parent.location.reload()
} else RbHighbar.error(res.error_msg)
})
}
const dept_id = window.__PageConfig.recordId
$(document).ready(function () {
$('.J_delete').off('click').click(() => {
$.get(`${rb.baseUrl}/admin/bizuser/delete-checks?id=${dept_id}`, (res) => {
if (res.data.hasMember === 0 && res.data.hasChild === 0) {
RbAlert.create('此部门可以被安全的删除', '删除部门', {
icon: 'alert-circle-o',
type: 'danger',
confirmText: '删除',
confirm: function () { deleteDept(this) }
})
} else {
let msg = '此部门下有 '
if (res.data.hasMember > 0) msg += '<b>' + res.data.hasMember + '</b> 个用户' + (res.data.hasChild > 0 ? '和 ' : ' ')
if (res.data.hasChild > 0) msg += '<b>' + res.data.hasChild + '</b> 个子部门'
msg += '<br>需要先将他们转移至其他部门,然后才能安全删除'
RbAlert.create(msg, '无法删除', {
type: 'danger',
html: true
})
}
})
})
})

View file

@ -81,18 +81,16 @@ const loadRoles = function () {
action.find('a.J_del').click(function () {
let alertExt = {
type: 'danger', confirmText: '删除', confirm: function () {
deleteRole(_id, this)
}
type: 'danger',
confirmText: '删除',
confirm: function () { deleteRole(_id, this) }
}
$.get(rb.baseUrl + '/admin/bizuser/delete-checks?id=' + _id, function (res) {
if (res.data.hasMember === 0) {
RbAlert.create('此角色可以被安全的删除', '删除角色', alertExt)
RbAlert.create('此角色可以被安全的删除', '删除角色', { ...alertExt, icon: 'alert-circle-o' })
} else {
let url = rb.baseUrl + '/admin/bizuser/users#!/Filter/roleId=' + _id
let msg = '有 <a href="' + url + '" target="_blank"><b>' + res.data.hasMember + '</b></a> 个用户使用了此角色<br>删除将导致这些用户被禁用,直到你为他们指定了新的角色'
alertExt.html = true
RbAlert.create(msg, '删除角色', alertExt)
let msg = '有 <b>' + res.data.hasMember + '</b> 个用户使用了此角色<br>删除将导致这些用户被禁用,直到你为他们指定了新的角色'
RbAlert.create(msg, '删除角色', { ...alertExt, html: true })
}
})
})

View file

@ -4,30 +4,36 @@ $(document).ready(function () {
$.get(rb.baseUrl + '/admin/bizuser/delete-checks?id=' + user_id, function (res) {
if (res.data.hasMember === 0) {
RbAlert.create('此用户可以被安全的删除', '删除用户', {
icon: 'alert-circle-o',
type: 'danger',
confirmText: '删除',
confirm: function () { deleteUser(user_id, this) }
})
} else {
RbAlert.create('此用户已被使用过,因此不能删除。建议你可以将其停用', '无法删除', {
type: 'warning',
icon: 'alert-circle-o',
type: 'danger',
confirmText: '停用',
confirm: () => { toggleDisabled(true) }
confirm: function () { toggleDisabled(true, this) }
})
}
})
})
$('.J_disable').click(() => {
RbAlert.create('确定要停用此用户吗?', { type: 'warning', confirm: () => { toggleDisabled(true) } })
RbAlert.create('确定要停用此用户吗?', {
confirmText: '停用',
confirm: function () { toggleDisabled(true, this) }
})
})
$('.J_enable').click(() => { toggleDisabled(false) })
$('.J_enable').click(() => toggleDisabled(false))
$('.J_changeRole').click(() => { renderRbcomp(<DlgEnableUser user={user_id} role={true} />) })
$('.J_changeDept').click(() => { renderRbcomp(<DlgEnableUser user={user_id} dept={true} />) })
if (rb.isAdminVerified === true) {
$.get(rb.baseUrl + '/admin/bizuser/check-user-status?id=' + user_id, (res) => {
if (res.error_code > 0) return
if (res.data.system === true && rb.isAdminVerified === true) {
$('.J_tips').removeClass('hide').find('.message p').text('系统内建超级管理员,不允许修改。此用户拥有最高级系统权限,请谨慎使用')
$('.view-action').remove()
@ -57,19 +63,20 @@ $(document).ready(function () {
})
// /
const toggleDisabled = function (disabled) {
const toggleDisabled = function (disabled, alert) {
alert && alert.disabled(true)
let _data = { user: user_id, enable: !disabled }
$.post(rb.baseUrl + '/admin/bizuser/enable-user', JSON.stringify(_data), (res) => {
if (res.error_code === 0) {
RbHighbar.create('用户已' + (disabled ? '停用' : '启用'), 'success')
setTimeout(() => { location.reload() }, 500)
}
RbHighbar.success('用户已' + (disabled ? '停用' : '启用'))
setTimeout(() => location.reload(), 500)
} else RbHighbar.error(res.error_msg)
})
}
//
const deleteUser = function (id, dlg) {
dlg.disabled(true)
const deleteUser = function (id, alert) {
alert && alert.disabled(true)
$.post(rb.baseUrl + '/admin/bizuser/user-delete?id=' + id, (res) => {
if (res.error_code === 0) {
parent.location.hash = '!/View/'

View file

@ -43,20 +43,25 @@ class MessageList extends React.Component {
renderItem(item) {
// const append = item[6] === 30
const append = item[5] && ~~item[5].substr(0, 3) !== 29 // ID
return <li className={`notification ${item[3] ? 'notification-unread' : ''} ${append ? 'append' : ''}`} key={item[4]} onClick={item[3] ? () => this._makeRead(item[4]) : null}><a>
<div className="image"><img src={`${rb.baseUrl}/account/user-avatar/${item[0][0]}`} title={item[0][1]} alt="Avatar" /></div>
<div className="notification-info">
<div className="text" dangerouslySetInnerHTML={{ __html: item[1] }}></div>
<div className="date">{item[2]}</div>
{append && <a title="查看相关记录" className="badge link" href={`${rb.baseUrl}/app/list-and-view?id=${item[5]}`}>查看</a>}
</div>
</a></li>
let clazz = 'notification'
if (item[3]) clazz += ' notification-unread'
if (append) clazz += ' append'
return <li className={clazz} key={item[4]} onClick={item[3] ? () => this.makeRead(item(4)) : null}>
<span className="a">
<div className="image"><img src={`${rb.baseUrl}/account/user-avatar/${item[0][0]}`} title={item[0][1]} alt="Avatar" /></div>
<div className="notification-info">
<div className="text" dangerouslySetInnerHTML={{ __html: item[1] }}></div>
<div className="date">{item[2]}</div>
{append && <a title="查看相关记录" className="badge link" href={`${rb.baseUrl}/app/list-and-view?id=${item[5]}`}>查看</a>}
</div>
</span>
</li>
}
componentDidMount() {
// eslint-disable-next-line react/prop-types
if (this.props.lazy !== true) this.fetchList()
$('.read-all').click(() => this._makeRead('ALL'))
$('.read-all').click(() => this.makeRead('ALL'))
}
fetchList(page, type) {
@ -69,7 +74,12 @@ class MessageList extends React.Component {
})
})
}
__setLink = () => $(this._list).find('.notification-info a').attr('target', '_blank').addClass('link')
__setLink = () => {
$(this._list).find('.notification-info a').attr('target', '_blank').addClass('link')
.click('click', (e) => {
if (e && e.stopPropagation) e.stopPropagation()
})
}
gotoPage(p) {
if (p === -1 && this.state.page === 1) return
@ -77,7 +87,7 @@ class MessageList extends React.Component {
this.fetchList(this.state.page + p, null)
}
_makeRead(id) {
makeRead(id) {
if (!id) return
$.post(`${rb.baseUrl}/notification/make-read?id=${id}`, () => {
let list = (this.state.list || []).map((item) => {

View file

@ -155,10 +155,7 @@ class RbFormHandler extends RbModalHandler {
// ~~
class RbAlert extends React.Component {
constructor(props) {
super(props)
this.state = { disable: false }
}
state = { ...this.props, disable: false }
render() {
let style = {}
if (this.props.width) style.maxWidth = ~~this.props.width
@ -177,11 +174,12 @@ class RbAlert extends React.Component {
</div>
)
}
renderContent() {
let icon = this.props.type === 'danger' ? 'alert-triangle' : 'help-outline'
if (this.props.type === 'warning') icon = 'alert-circle-o'
if (this.props.type === 'primary') icon = 'info-outline'
let type = this.props.type || 'primary'
const type = this.props.type || 'primary'
let icon = this.props.icon
if (!icon) icon = type === 'danger' ? 'alert-triangle' : (type === 'primary' ? 'help-outline' : 'alert-circle-o')
let content = this.props.htmlMessage ?
<div className="mt-3" style={{ lineHeight: 1.8 }} dangerouslySetInnerHTML={{ __html: this.props.htmlMessage }} />
: <p>{this.props.message || 'INMESSAGE'}</p>
@ -190,9 +188,7 @@ class RbAlert extends React.Component {
let confirm = (this.props.confirm || this.hide).bind(this)
return <div className="text-center ml-6 mr-6">
{this.props.showIcon === false ? null :
<div className={'text-' + type}><span className={'modal-main-icon zmdi zmdi-' + icon} /></div>
}
<div className={`text-${type}`}><i className={`modal-main-icon zmdi zmdi-${icon}`} /></div>
{this.props.title && <h4 className="mb-2 mt-3">{this.props.title}</h4>}
<div className={this.props.title ? '' : 'mt-3'}>{content}</div>
<div className="mt-4 mb-3">

View file

@ -187,7 +187,7 @@ var __loadMessages = function () {
$(res.data).each(function (idx, item) {
var o = $('<li class="notification"></li>').appendTo(dest)
if (item[3] === true) o.addClass('notification-unread')
o = $('<a href="' + rb.baseUrl + '/notifications#id=' + item[4] + '"></a>').appendTo(o)
o = $('<a class="a" href="' + rb.baseUrl + '/notifications#' + (item[3] ? 'unread' : 'read') + '"></a>').appendTo(o)
$('<div class="image"><img src="' + rb.baseUrl + '/account/user-avatar/' + item[0][0] + '" alt="Avatar"></div>').appendTo(o)
o = $('<div class="notification-info"></div>').appendTo(o)
$('<div class="text text-truncate">' + item[1] + '</div>').appendTo(o)

View file

@ -32,11 +32,13 @@ public class SMSenderTest extends TestSupport {
@Ignore
@Test
public void testSendSMS() throws Exception {
SMSender.availableSMS();
SMSender.sendSMS("17187472172", "SMSenderTest#testSendSMS");
}
@Test
public void testSendMail() throws Exception {
SMSender.availableMail();
SMSender.sendMail("getrebuild@sina.com", "SMSenderTest#testSendMail", "test content");
}
}