mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 15:35:55 +08:00
Will done
This commit is contained in:
parent
936298d4e4
commit
8d8a353751
3
crowdin.yml
Normal file
3
crowdin.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
files:
|
||||||
|
- source: /src/main/resources/locales/language_zh-CN.json
|
||||||
|
translation: /src/main/resources/locales/language_%locale%.json
|
|
@ -24,6 +24,7 @@ import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.rebuild.server.Application;
|
import com.rebuild.server.Application;
|
||||||
import com.rebuild.server.helper.SysConfiguration;
|
import com.rebuild.server.helper.SysConfiguration;
|
||||||
|
import com.rebuild.server.helper.language.Languages;
|
||||||
import com.rebuild.server.service.bizz.privileges.User;
|
import com.rebuild.server.service.bizz.privileges.User;
|
||||||
import com.rebuild.server.service.bizz.privileges.ZeroEntry;
|
import com.rebuild.server.service.bizz.privileges.ZeroEntry;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
|
@ -53,7 +54,7 @@ public class LoginToken extends BaseApi {
|
||||||
String password = context.getParameterNotBlank("password");
|
String password = context.getParameterNotBlank("password");
|
||||||
|
|
||||||
if (COUNTER.counter(user).add().seconds(30).than(3)) {
|
if (COUNTER.counter(user).add().seconds(30).than(3)) {
|
||||||
return formatFailure("超出请求频率", ApiInvokeException.ERR_FREQUENCY);
|
return formatFailure("Request frequency exceeded", ApiInvokeException.ERR_FREQUENCY);
|
||||||
}
|
}
|
||||||
|
|
||||||
String hasError = checkUser(user, password);
|
String hasError = checkUser(user, password);
|
||||||
|
@ -92,13 +93,13 @@ public class LoginToken extends BaseApi {
|
||||||
*/
|
*/
|
||||||
public static String checkUser(String user, String password) {
|
public static String checkUser(String user, String password) {
|
||||||
if (!Application.getUserStore().exists(user)) {
|
if (!Application.getUserStore().exists(user)) {
|
||||||
return "用户名或密码错误";
|
return Languages.lang("InputWrong", "UsernameOrPassword");
|
||||||
}
|
}
|
||||||
|
|
||||||
User loginUser = Application.getUserStore().getUser(user);
|
User loginUser = Application.getUserStore().getUser(user);
|
||||||
if (!loginUser.isActive()
|
if (!loginUser.isActive()
|
||||||
|| !Application.getSecurityManager().allowed(loginUser.getId(), ZeroEntry.AllowLogin)) {
|
|| !Application.getSecurityManager().allowed(loginUser.getId(), ZeroEntry.AllowLogin)) {
|
||||||
return "用户未激活或不允许登录";
|
return Languages.lang("UnactiveUserTip");
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] foundUser = Application.createQueryNoFilter(
|
Object[] foundUser = Application.createQueryNoFilter(
|
||||||
|
@ -108,7 +109,7 @@ public class LoginToken extends BaseApi {
|
||||||
.unique();
|
.unique();
|
||||||
if (foundUser == null
|
if (foundUser == null
|
||||||
|| !foundUser[0].equals(EncryptUtils.toSHA256Hex(password))) {
|
|| !foundUser[0].equals(EncryptUtils.toSHA256Hex(password))) {
|
||||||
return "用户名或密码错误";
|
return Languages.lang("InputWrong", "UsernameOrPassword");
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -138,8 +138,8 @@ public class SMSender {
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
protected static Element getMailTemplate() throws IOException {
|
protected static Element getMailTemplate() throws IOException {
|
||||||
File temp = SysConfiguration.getFileOfRes("locales/mail-notify.html");
|
File tmp = SysConfiguration.getFileOfRes("locales/email_zh-CN.html");
|
||||||
Document html = Jsoup.parse(temp, "utf-8");
|
Document html = Jsoup.parse(tmp, "utf-8");
|
||||||
return html.body();
|
return html.body();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
rebuild - Building your business-systems freely.
|
||||||
|
Copyright (C) 2018-2019 devezhao <zhaofang123@gmail.com>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rebuild.server.helper.language;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.EncryptUtils;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.rebuild.utils.JSONable;
|
||||||
|
import org.apache.commons.lang.ArrayUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言包
|
||||||
|
*
|
||||||
|
* @author ZHAO
|
||||||
|
* @since 2019/10/31
|
||||||
|
*/
|
||||||
|
public class LanguageBundle implements JSONable {
|
||||||
|
|
||||||
|
final private String locale;
|
||||||
|
final private JSONObject bundle;
|
||||||
|
private String bundleHash;
|
||||||
|
|
||||||
|
final private Languages parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param locale
|
||||||
|
* @param bundle
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
protected LanguageBundle(String locale, JSONObject bundle, Languages parent) {
|
||||||
|
this.locale = locale;
|
||||||
|
this.bundle = this.merge(bundle);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Pattern VAR_PATTERN = Pattern.compile("\\{([0-9a-zA-Z]+)\\}");
|
||||||
|
/**
|
||||||
|
* 合并语言中的变量
|
||||||
|
*
|
||||||
|
* @param bundle
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private JSONObject merge(JSONObject bundle) {
|
||||||
|
String bundleString = bundle.toJSONString();
|
||||||
|
Matcher matcher = VAR_PATTERN.matcher(bundleString);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String var = matcher.group(1);
|
||||||
|
String lang = bundle.getString(var);
|
||||||
|
if (lang != null) {
|
||||||
|
bundleString = bundleString.replace("{" + var +"}", lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bundleHash = EncryptUtils.toMD5Hex(bundleString);
|
||||||
|
return JSON.parseObject(bundleString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String locale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String lang(String key) {
|
||||||
|
return lang(key, ArrayUtils.EMPTY_STRING_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @param indexKeys 替换语言中的占位符 {0}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String lang(String key, String... indexKeys) {
|
||||||
|
String lang = getLang(key);
|
||||||
|
if (indexKeys == null || indexKeys.length == 0) {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (String ik : indexKeys) {
|
||||||
|
String iLang = getLang(ik);
|
||||||
|
if (iLang != null) {
|
||||||
|
lang = lang.replace("{" + index++ + "}", iLang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getLang(String key) {
|
||||||
|
String lang = bundle.getString(key);
|
||||||
|
if (lang == null) {
|
||||||
|
String d = String.format("[%s]", key.toUpperCase());
|
||||||
|
if (parent != null) {
|
||||||
|
return parent.getDefaultBundle().getLang(key, d);
|
||||||
|
} else {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @param defaultLang
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getLang(String key, String defaultLang) {
|
||||||
|
return StringUtils.defaultIfEmpty(bundle.getString(key), defaultLang);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash for bundle
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getBundleHash() {
|
||||||
|
return bundleHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSON toJSON() {
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSON toJSON(String... special) {
|
||||||
|
return toJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + "#" + locale() + ":" + bundle.size();
|
||||||
|
}
|
||||||
|
}
|
146
src/main/java/com/rebuild/server/helper/language/Languages.java
Normal file
146
src/main/java/com/rebuild/server/helper/language/Languages.java
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
rebuild - Building your business-systems freely.
|
||||||
|
Copyright (C) 2018-2019 devezhao <zhaofang123@gmail.com>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rebuild.server.helper.language;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.rebuild.server.Application;
|
||||||
|
import com.rebuild.server.RebuildException;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.util.ResourceUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多语言
|
||||||
|
*
|
||||||
|
* @author ZHAO
|
||||||
|
* @since 2019/10/31
|
||||||
|
*/
|
||||||
|
public class Languages {
|
||||||
|
|
||||||
|
private static final Log LOG = LogFactory.getLog(Languages.class);
|
||||||
|
|
||||||
|
public static final Languages instance = new Languages();
|
||||||
|
private Languages() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认语言
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_LOCALE = "zh-CN";
|
||||||
|
/**
|
||||||
|
* 语言文件前缀
|
||||||
|
*/
|
||||||
|
private static final String LB_PREFIX = "language_";
|
||||||
|
|
||||||
|
private Map<String, LanguageBundle> bundleMap = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
try {
|
||||||
|
File[] files = ResourceUtils.getFile("classpath:locales/")
|
||||||
|
.listFiles((dir, name) -> name.startsWith(LB_PREFIX) && name.endsWith(".json"));
|
||||||
|
for (File file : Objects.requireNonNull(files)) {
|
||||||
|
String locale = file.getName().substring(LB_PREFIX.length());
|
||||||
|
locale = locale.split("\\.")[0];
|
||||||
|
|
||||||
|
try (InputStream is = new FileInputStream(file)) {
|
||||||
|
LOG.info("Loading language bundle : " + locale);
|
||||||
|
JSONObject o = JSON.parseObject(is, null);
|
||||||
|
bundleMap.remove(locale);
|
||||||
|
bundleMap.put(locale, new LanguageBundle(locale, o, this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RebuildException("Load language bundle failure!", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param locale
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LanguageBundle getBundle(Locale locale) {
|
||||||
|
return getBundle(locale == null ? null : locale.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param locale
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LanguageBundle getBundle(String locale) {
|
||||||
|
if (locale != null) {
|
||||||
|
locale = locale.replace("_", "-");
|
||||||
|
}
|
||||||
|
if (locale != null && bundleMap.containsKey(locale)) {
|
||||||
|
return bundleMap.get(locale);
|
||||||
|
} else {
|
||||||
|
return getDefaultBundle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认语言包
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LanguageBundle getDefaultBundle() {
|
||||||
|
return bundleMap.get(DEFAULT_LOCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前用户语言包
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LanguageBundle getCurrentBundle() {
|
||||||
|
return getBundle(Application.getSessionStore().getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为可用语言
|
||||||
|
*
|
||||||
|
* @param locale
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isAvailable(String locale) {
|
||||||
|
return bundleMap.containsKey(locale.replace("_", "-"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Quick Methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key
|
||||||
|
* @param insideKeys
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String lang(String key, String...insideKeys) {
|
||||||
|
return instance.getCurrentBundle().lang(key, insideKeys);
|
||||||
|
}
|
||||||
|
}
|
|
@ -171,4 +171,17 @@ public class AppUtils {
|
||||||
public static boolean allowed(HttpServletRequest request, ZeroEntry entry) {
|
public static boolean allowed(HttpServletRequest request, ZeroEntry entry) {
|
||||||
return Application.getSecurityManager().allowed(getRequestUser(request), entry);
|
return Application.getSecurityManager().allowed(getRequestUser(request), entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final String SK_LOCALE = WebUtils.KEY_PREFIX + ".LOCALE";
|
||||||
|
/**
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getLocale(HttpServletRequest request) {
|
||||||
|
String locale = (String) ServletUtils.getSessionAttribute(request, SK_LOCALE);
|
||||||
|
if (locale == null) {
|
||||||
|
locale = request.getLocale().toString();
|
||||||
|
}
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import cn.devezhao.commons.web.ServletUtils;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.rebuild.api.Controll;
|
import com.rebuild.api.Controll;
|
||||||
|
import com.rebuild.server.helper.language.LanguageBundle;
|
||||||
|
import com.rebuild.server.helper.language.Languages;
|
||||||
import com.rebuild.utils.AppUtils;
|
import com.rebuild.utils.AppUtils;
|
||||||
import org.apache.commons.lang.BooleanUtils;
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
@ -43,11 +45,20 @@ public abstract class BaseControll extends Controll {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected ID getRequestUser(HttpServletRequest request) {
|
protected ID getRequestUser(HttpServletRequest request) {
|
||||||
ID userId = AppUtils.getRequestUser(request);
|
ID user = AppUtils.getRequestUser(request);
|
||||||
if (userId == null) {
|
if (user == null) {
|
||||||
throw new IllegalParameterException("无效请求用户");
|
throw new IllegalParameterException("无效请求用户");
|
||||||
}
|
}
|
||||||
return userId;
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected LanguageBundle getBundle(HttpServletRequest request) {
|
||||||
|
String locale = AppUtils.getLocale(request);
|
||||||
|
return Languages.instance.getBundle(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,6 +18,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package com.rebuild.web;
|
package com.rebuild.web;
|
||||||
|
|
||||||
|
import com.rebuild.server.Application;
|
||||||
|
import com.rebuild.server.helper.language.LanguageBundle;
|
||||||
|
import com.rebuild.server.helper.language.Languages;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,6 +36,12 @@ public abstract class BasePageControll extends BaseControll {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected ModelAndView createModelAndView(String page) {
|
protected ModelAndView createModelAndView(String page) {
|
||||||
return new ModelAndView(page);
|
ModelAndView mv = new ModelAndView(page);
|
||||||
|
|
||||||
|
// 语言包
|
||||||
|
String locale = Application.getSessionStore().getLocale();
|
||||||
|
LanguageBundle bundle = Languages.instance.getBundle(locale);
|
||||||
|
mv.getModel().put("bundle", bundle);
|
||||||
|
return mv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,12 @@ import cn.devezhao.commons.web.WebUtils;
|
||||||
import cn.devezhao.persist4j.Record;
|
import cn.devezhao.persist4j.Record;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.rebuild.server.Application;
|
import com.rebuild.server.Application;
|
||||||
|
import com.rebuild.server.helper.language.Languages;
|
||||||
import com.rebuild.server.metadata.EntityHelper;
|
import com.rebuild.server.metadata.EntityHelper;
|
||||||
import com.rebuild.server.service.bizz.CurrentCaller;
|
import com.rebuild.server.service.bizz.CurrentCaller;
|
||||||
import com.rebuild.server.service.bizz.UserService;
|
import com.rebuild.server.service.bizz.UserService;
|
||||||
import com.rebuild.web.user.signin.LoginControll;
|
import com.rebuild.web.user.signin.LoginControll;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
@ -53,6 +55,8 @@ public class OnlineSessionStore extends CurrentCaller implements HttpSessionList
|
||||||
|
|
||||||
private static final Set<HttpSession> ONLINE_SESSIONS = new CopyOnWriteArraySet<>();
|
private static final Set<HttpSession> ONLINE_SESSIONS = new CopyOnWriteArraySet<>();
|
||||||
private static final Map<ID, HttpSession> ONLINE_USERS = new ConcurrentHashMap<>();
|
private static final Map<ID, HttpSession> ONLINE_USERS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static final ThreadLocal<String> LOCALE = new ThreadLocal<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sessionCreated(HttpSessionEvent event) {
|
public void sessionCreated(HttpSessionEvent event) {
|
||||||
|
@ -89,8 +93,6 @@ public class OnlineSessionStore extends CurrentCaller implements HttpSessionList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 最近访问时间
|
* 最近访问时间
|
||||||
*/
|
*/
|
||||||
|
@ -126,9 +128,40 @@ public class OnlineSessionStore extends CurrentCaller implements HttpSessionList
|
||||||
public void storeLoginSuccessed(HttpServletRequest request) {
|
public void storeLoginSuccessed(HttpServletRequest request) {
|
||||||
HttpSession s = request.getSession();
|
HttpSession s = request.getSession();
|
||||||
Object loginUser = s.getAttribute(WebUtils.CURRENT_USER);
|
Object loginUser = s.getAttribute(WebUtils.CURRENT_USER);
|
||||||
Assert.notNull(loginUser, "No login user found");
|
Assert.notNull(loginUser, "No login user found in session!");
|
||||||
|
|
||||||
ONLINE_SESSIONS.remove(s);
|
ONLINE_SESSIONS.remove(s);
|
||||||
ONLINE_USERS.put((ID) loginUser, s);
|
ONLINE_USERS.put((ID) loginUser, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param locale
|
||||||
|
*/
|
||||||
|
public void setLocale(String locale) {
|
||||||
|
LOCALE.set(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns default if unset
|
||||||
|
* @see Languages
|
||||||
|
*/
|
||||||
|
public String getLocale() {
|
||||||
|
return StringUtils.defaultIfEmpty(LOCALE.get(), Languages.DEFAULT_LOCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param caller
|
||||||
|
* @param locale
|
||||||
|
* @see #set(ID)
|
||||||
|
*/
|
||||||
|
public void set(ID caller, String locale) {
|
||||||
|
super.set(caller);
|
||||||
|
this.setLocale(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clean() {
|
||||||
|
super.clean();
|
||||||
|
LOCALE.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class RebuildExceptionResolver implements HandlerExceptionResolver {
|
||||||
@Override
|
@Override
|
||||||
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
|
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
|
||||||
Exception ex) {
|
Exception ex) {
|
||||||
LOG.error("Handler - " + handler + "\nException - " + ex);
|
LOG.error("\nHandler : " + handler + "\nException : ", ex);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,10 @@ public class RequestWatchHandler extends HandlerInterceptorAdapter {
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||||
Object handler) throws Exception {
|
Object handler) throws Exception {
|
||||||
response.setCharacterEncoding("utf-8");
|
response.setCharacterEncoding("utf-8");
|
||||||
|
request.getSession(true);
|
||||||
|
|
||||||
|
// for Language
|
||||||
|
Application.getSessionStore().setLocale(AppUtils.getLocale(request));
|
||||||
|
|
||||||
final String requestUrl = request.getRequestURI();
|
final String requestUrl = request.getRequestURI();
|
||||||
if (noCache && !(ServletUtils.isAjaxRequest(request)
|
if (noCache && !(ServletUtils.isAjaxRequest(request)
|
||||||
|
@ -205,6 +209,6 @@ public class RequestWatchHandler extends HandlerInterceptorAdapter {
|
||||||
reqUrl = reqUrl.replaceFirst(ServerListener.getContextPath(), "");
|
reqUrl = reqUrl.replaceFirst(ServerListener.getContextPath(), "");
|
||||||
return reqUrl.startsWith("/gw/") || reqUrl.startsWith("/assets/") || reqUrl.startsWith("/error/")
|
return reqUrl.startsWith("/gw/") || reqUrl.startsWith("/assets/") || reqUrl.startsWith("/error/")
|
||||||
|| reqUrl.startsWith("/t/") || reqUrl.startsWith("/s/")
|
|| reqUrl.startsWith("/t/") || reqUrl.startsWith("/s/")
|
||||||
|| reqUrl.startsWith("/setup/");
|
|| reqUrl.startsWith("/setup/") || reqUrl.startsWith("/language/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
74
src/main/java/com/rebuild/web/common/LanguagesControll.java
Normal file
74
src/main/java/com/rebuild/web/common/LanguagesControll.java
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
rebuild - Building your business-systems freely.
|
||||||
|
Copyright (C) 2018-2019 devezhao <zhaofang123@gmail.com>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rebuild.web.common;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CodecUtils;
|
||||||
|
import cn.devezhao.commons.web.ServletUtils;
|
||||||
|
import com.rebuild.server.helper.language.LanguageBundle;
|
||||||
|
import com.rebuild.server.helper.language.Languages;
|
||||||
|
import com.rebuild.utils.AppUtils;
|
||||||
|
import com.rebuild.web.BaseControll;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static com.rebuild.utils.AppUtils.SK_LOCALE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 语言控制
|
||||||
|
*
|
||||||
|
* @author devezhao
|
||||||
|
* @since 2019/11/29
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/language/")
|
||||||
|
public class LanguagesControll extends BaseControll {
|
||||||
|
|
||||||
|
@RequestMapping(value = "bundle", method = RequestMethod.GET)
|
||||||
|
public void getLanguageBundle(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
|
final LanguageBundle bundle = getBundle(request);
|
||||||
|
// HTTP Headers
|
||||||
|
response.addHeader("Cache-Control", "max-age=60, must-revalidate");
|
||||||
|
response.addHeader("ETag", "W/" + bundle.getBundleHash());
|
||||||
|
response.setContentType(ServletUtils.CT_JS);
|
||||||
|
|
||||||
|
ServletUtils.write(response, "__LANGBUNDLE__ = " + bundle.toJSON().toJSONString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("select")
|
||||||
|
public void selectLanguage(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
|
String locale = request.getParameter("locale");
|
||||||
|
if (locale != null && Languages.instance.isAvailable(locale)) {
|
||||||
|
if (AppUtils.devMode()) Languages.instance.reset();
|
||||||
|
ServletUtils.setSessionAttribute(request, SK_LOCALE, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ServletUtils.isAjaxRequest(request)) {
|
||||||
|
writeSuccess(response);
|
||||||
|
} else {
|
||||||
|
String nexturl = StringUtils.defaultIfBlank(request.getParameter("nexturl"), AppUtils.getContextPath());
|
||||||
|
response.sendRedirect(CodecUtils.urlDecode(nexturl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,6 +50,8 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static com.rebuild.server.helper.language.Languages.lang;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author zhaofang123@gmail.com
|
* @author zhaofang123@gmail.com
|
||||||
* @since 07/25/2018
|
* @since 07/25/2018
|
||||||
|
@ -132,10 +134,10 @@ public class LoginControll extends BasePageControll {
|
||||||
Boolean needVcode = (Boolean) ServletUtils.getSessionAttribute(request, NEED_VCODE);
|
Boolean needVcode = (Boolean) ServletUtils.getSessionAttribute(request, NEED_VCODE);
|
||||||
if (needVcode != null && needVcode
|
if (needVcode != null && needVcode
|
||||||
&& (StringUtils.isBlank(vcode) || !CaptchaUtil.ver(vcode, request))) {
|
&& (StringUtils.isBlank(vcode) || !CaptchaUtil.ver(vcode, request))) {
|
||||||
writeFailure(response, "验证码错误");
|
writeFailure(response, lang("InputWrong", "Captcha"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String user = getParameterNotNull(request, "user");
|
final String user = getParameterNotNull(request, "user");
|
||||||
final String password = getParameterNotNull(request, "passwd");
|
final String password = getParameterNotNull(request, "passwd");
|
||||||
|
|
||||||
|
@ -248,23 +250,23 @@ public class LoginControll extends BasePageControll {
|
||||||
@RequestMapping("user-forgot-passwd")
|
@RequestMapping("user-forgot-passwd")
|
||||||
public void userForgotPasswd(HttpServletRequest request, HttpServletResponse response) {
|
public void userForgotPasswd(HttpServletRequest request, HttpServletResponse response) {
|
||||||
if (!SMSender.availableMail()) {
|
if (!SMSender.availableMail()) {
|
||||||
writeFailure(response, "邮件服务账户未配置,请联系管理员配置");
|
writeFailure(response, lang("EmailAccountUnset"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String email = getParameterNotNull(request, "email");
|
String email = getParameterNotNull(request, "email");
|
||||||
if (!RegexUtils.isEMail(email) || !Application.getUserStore().existsEmail(email)) {
|
if (!RegexUtils.isEMail(email) || !Application.getUserStore().existsEmail(email)) {
|
||||||
writeFailure(response, "无效邮箱");
|
writeFailure(response, lang("InputInvalid", "Email"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String vcode = VCode.generate(email, 2);
|
String vcode = VCode.generate(email, 2);
|
||||||
String content = "<p>你的重置密码验证码是 <b>" + vcode + "</b><p>";
|
String content = String.format(lang("YourVcodeForResetPassword"), vcode);
|
||||||
String sentid = SMSender.sendMail(email, "重置密码", content);
|
String sentid = SMSender.sendMail(email, lang("ResetPassword"), content);
|
||||||
if (sentid != null) {
|
if (sentid != null) {
|
||||||
writeSuccess(response);
|
writeSuccess(response);
|
||||||
} else {
|
} else {
|
||||||
writeFailure(response, "无法发送验证码,请稍后重试");
|
writeFailure(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +277,7 @@ public class LoginControll extends BasePageControll {
|
||||||
String email = data.getString("email");
|
String email = data.getString("email");
|
||||||
String vcode = data.getString("vcode");
|
String vcode = data.getString("vcode");
|
||||||
if (!VCode.verfiy(email, vcode, true)) {
|
if (!VCode.verfiy(email, vcode, true)) {
|
||||||
writeFailure(response, "验证码无效");
|
writeFailure(response, lang("InputInvalid", "Vcode"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Mail Notification Template</title>
|
<title>EMail Template</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div style="background-color:#f5f5f5;margin:0;font-size:0.9rem;padding:20px 30px;color:#333;font-family:Roboto,Helvetica,'Microsoft YaHei','宋体',sans-serif;line-height:1.5">
|
<div style="background-color:#f5f5f5;margin:0;font-size:0.9rem;padding:20px 30px;color:#333;font-family:Roboto,Helvetica,'Microsoft YaHei','宋体',sans-serif;line-height:1.5">
|
44
src/main/resources/locales/language_en-US.json
Normal file
44
src/main/resources/locales/language_en-US.json
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"rebuild": "Building your business-systems freely",
|
||||||
|
"User": "User",
|
||||||
|
"Login": "Login",
|
||||||
|
"Username": "Username",
|
||||||
|
"Captcha": "Captcha",
|
||||||
|
"Vcode": "Code",
|
||||||
|
"Password": "Password",
|
||||||
|
"Email": "Email",
|
||||||
|
"Signup": "Sign Up",
|
||||||
|
"UsernameOrEmail": "Username (or email)",
|
||||||
|
"LoginPassword": "Password",
|
||||||
|
"InputCaptcha": "Enter captcha",
|
||||||
|
"ClickReload": "Click to reload",
|
||||||
|
"RememberMe": "Remember me",
|
||||||
|
"NoAccountYet": "Don't have an account yet?",
|
||||||
|
"SignupNow": "Sign Up",
|
||||||
|
"ForgotPassword": "Forgot password",
|
||||||
|
"ResetPassword": "Reset password",
|
||||||
|
"InputUserOrPasswordPls": "Please enter username and password",
|
||||||
|
"InputCaptchaPls": "Please enter captcha",
|
||||||
|
"InputWrong": "{0} was invalid",
|
||||||
|
"InputInvalid": "{0} was invalid",
|
||||||
|
"UsernameOrPassword": "Username or password",
|
||||||
|
"UnactiveUserTip": "User is not activated or is not allowed to login",
|
||||||
|
"ReturnHome": "Return to 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",
|
||||||
|
"InputEmailPls": "Please enter email",
|
||||||
|
"InputNewPasswordPls": "Enter new password",
|
||||||
|
"PasswordNotMatch": "New passwords entered twice are inconsistent",
|
||||||
|
"NewPassword": "New password",
|
||||||
|
"RepeatNewPassword": "New password again",
|
||||||
|
"GetVcode": "Send code",
|
||||||
|
"GetVcode2": "Resend",
|
||||||
|
"VcodeEmailSent": "Verification code has been sent to your mailbox",
|
||||||
|
"ConfirmReset": "Confirm",
|
||||||
|
"ActionSuccess": "Operation succeeded",
|
||||||
|
"EmailAccountUnset": "Mail service account is not configured, please contact administrator to configure",
|
||||||
|
"YourVcodeForResetPassword": "<p>Your reset password verification code is <b>%s</b><p>",
|
||||||
|
"InputVcodePls": "Please enter code"
|
||||||
|
|
||||||
|
}
|
3
src/main/resources/locales/language_ja-JP.json
Normal file
3
src/main/resources/locales/language_ja-JP.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rebuild": "高度にカスタマイズ可能なエンタープライズ管理システム"
|
||||||
|
}
|
44
src/main/resources/locales/language_zh-CN.json
Normal file
44
src/main/resources/locales/language_zh-CN.json
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"rebuild": "高度可定制化的企业管理系统",
|
||||||
|
"User": "用户",
|
||||||
|
"Login": "登录",
|
||||||
|
"Username": "用户名",
|
||||||
|
"Captcha": "验证码",
|
||||||
|
"Vcode": "验证码",
|
||||||
|
"Password": "密码",
|
||||||
|
"Email": "邮箱",
|
||||||
|
"Signup": "注册",
|
||||||
|
"UsernameOrEmail": "用户名 (或邮箱)",
|
||||||
|
"LoginPassword": "登录密码",
|
||||||
|
"InputCaptcha": "输入验证码",
|
||||||
|
"ClickReload": "点击刷新",
|
||||||
|
"RememberMe": "记住登录",
|
||||||
|
"NoAccountYet": "还没有账号?",
|
||||||
|
"SignupNow": "立即注册",
|
||||||
|
"ForgotPassword": "找回密码",
|
||||||
|
"ResetPassword": "重置密码",
|
||||||
|
"InputUserOrPasswordPls": "请输入用户名和密码",
|
||||||
|
"InputCaptchaPls": "请输入验证码",
|
||||||
|
"InputWrong": "{0}错误",
|
||||||
|
"InputInvalid": "{0}无效",
|
||||||
|
"UsernameOrPassword": "用户名或密码",
|
||||||
|
"UnactiveUserTip": "用户未激活或不允许登录",
|
||||||
|
"ReturnHome": "返回首页",
|
||||||
|
"ReportIssue": "报告此问题",
|
||||||
|
"SignupNotOpenTip": "管理员未开放公开注册",
|
||||||
|
"UnsetEmailTip": "如果你忘记或未配置邮箱,请联系管理员重置密码",
|
||||||
|
"InputEmailPls": "请输入邮箱",
|
||||||
|
"InputNewPasswordPls": "请输入新密码",
|
||||||
|
"PasswordNotMatch": "两次输入的新密码不一致",
|
||||||
|
"NewPassword": "新密码",
|
||||||
|
"RepeatNewPassword": "重复新密码",
|
||||||
|
"GetVcode": "获取验证码",
|
||||||
|
"GetVcode2": "重新获取",
|
||||||
|
"VcodeEmailSent": "验证码已发送至邮箱",
|
||||||
|
"ConfirmReset": "确认重置",
|
||||||
|
"ActionSuccess": "操作成功",
|
||||||
|
"EmailAccountUnset": "邮件服务账户未配置,请联系管理员配置",
|
||||||
|
"YourVcodeForResetPassword": "<p>你的重置密码验证码是 <b>%s</b><p>",
|
||||||
|
"InputVcodePls": "请输入验证码"
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,7 @@
|
||||||
<script src="${baseUrl}/assets/lib/react/babel.js?v=7.6.4"></script>
|
<script src="${baseUrl}/assets/lib/react/babel.js?v=7.6.4"></script>
|
||||||
<script src="${baseUrl}/assets/lib/react/react.development.js?v=16.10.2"></script>
|
<script src="${baseUrl}/assets/lib/react/react.development.js?v=16.10.2"></script>
|
||||||
<script src="${baseUrl}/assets/lib/react/react-dom.development.js?v=16.10.2"></script>
|
<script src="${baseUrl}/assets/lib/react/react-dom.development.js?v=16.10.2"></script>
|
||||||
|
<script src="${baseUrl}/language/bundle?v=1.7.0"></script>
|
||||||
<script src="${baseUrl}/assets/js/rb-components.jsx" type="text/babel"></script>
|
<script src="${baseUrl}/assets/js/rb-components.jsx" type="text/babel"></script>
|
||||||
<script src="${baseUrl}/assets/js/rb-base.js"></script>
|
<script src="${baseUrl}/assets/js/rb-base.js"></script>
|
||||||
<script src="${baseUrl}/assets/js/rb-page.js"></script>
|
<script src="${baseUrl}/assets/js/rb-page.js"></script>
|
||||||
|
|
|
@ -22834,7 +22834,7 @@ select.form-control-xs:not([size]):not([multiple]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-forgot-password {
|
.login-forgot-password {
|
||||||
line-height: 2.1;
|
line-height: 1.92;
|
||||||
text-align: right
|
text-align: right
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
src/main/webapp/assets/img/flag/en-US.png
Normal file
BIN
src/main/webapp/assets/img/flag/en-US.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 609 B |
BIN
src/main/webapp/assets/img/flag/ja-JP.png
Normal file
BIN
src/main/webapp/assets/img/flag/ja-JP.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 420 B |
BIN
src/main/webapp/assets/img/flag/zh-CN.png
Normal file
BIN
src/main/webapp/assets/img/flag/zh-CN.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 472 B |
|
@ -39,7 +39,7 @@
|
||||||
complete: function (xhr) {
|
complete: function (xhr) {
|
||||||
// eslint-disable-next-line no-empty
|
// eslint-disable-next-line no-empty
|
||||||
if (xhr.status === 200 || xhr.status === 0) { } // That's OK
|
if (xhr.status === 200 || xhr.status === 0) { } // That's OK
|
||||||
else if (xhr.status === 403 || xhr.status === 401) RbHighbar.error(xhr.responseText || '未授权访问')
|
else if (xhr.status === 403 || xhr.status === 401) RbHighbar.error(xhr.responseText || 'Unauthorized access')
|
||||||
else {
|
else {
|
||||||
var error = xhr.responseText
|
var error = xhr.responseText
|
||||||
if (rb.env !== 'dev' && error && error.contains('Exception : ')) error = error.split('Exception : ')[1]
|
if (rb.env !== 'dev' && error && error.contains('Exception : ')) error = error.split('Exception : ')[1]
|
||||||
|
@ -313,4 +313,18 @@ var $stopEvent = function (e) {
|
||||||
if (e && e.stopPropagation) e.stopPropagation()
|
if (e && e.stopPropagation) e.stopPropagation()
|
||||||
if (e && e.nativeEvent) e.nativeEvent.stopImmediatePropagation()
|
if (e && e.nativeEvent) e.nativeEvent.stopImmediatePropagation()
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取语言
|
||||||
|
*/
|
||||||
|
var $lang = function (key, insideLangs) {
|
||||||
|
var lang = __getLang(key)
|
||||||
|
if (typeof insideLangs === 'object') {
|
||||||
|
for (var k in insideLangs) lang = lang.replace('{' + k + '}', insideLangs[k])
|
||||||
|
}
|
||||||
|
return lang
|
||||||
|
}
|
||||||
|
var __getLang = function (key) {
|
||||||
|
return (window.__LANGBUNDLE__ || {})[key] || '[' + key.toUpperCase() + ']'
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ class RbModal extends React.Component {
|
||||||
<div className="modal-dialog" style={{ maxWidth: (this.props.width || 680) + 'px' }}>
|
<div className="modal-dialog" style={{ maxWidth: (this.props.width || 680) + 'px' }}>
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
<div className="modal-header modal-header-colored">
|
<div className="modal-header modal-header-colored">
|
||||||
<h3 className="modal-title">{this.props.title || '无标题'}</h3>
|
<h3 className="modal-title">{this.props.title || 'UNTITLED'}</h3>
|
||||||
<button className="close" type="button" onClick={() => this.hide()}><span className="zmdi zmdi-close" /></button>
|
<button className="close" type="button" onClick={() => this.hide()}><span className="zmdi zmdi-close" /></button>
|
||||||
</div>
|
</div>
|
||||||
<div className={'modal-body' + (inFrame ? ' iframe rb-loading' : '') + (inFrame && this.state.frameLoad !== false ? ' rb-loading-active' : '')}>
|
<div className={'modal-body' + (inFrame ? ' iframe rb-loading' : '') + (inFrame && this.state.frameLoad !== false ? ' rb-loading-active' : '')}>
|
||||||
|
@ -183,7 +183,7 @@ class RbAlert extends React.Component {
|
||||||
let type = this.props.type || 'primary'
|
let type = this.props.type || 'primary'
|
||||||
let content = this.props.htmlMessage ?
|
let content = this.props.htmlMessage ?
|
||||||
<div className="mt-3" style={{ lineHeight: 1.8 }} dangerouslySetInnerHTML={{ __html: this.props.htmlMessage }} />
|
<div className="mt-3" style={{ lineHeight: 1.8 }} dangerouslySetInnerHTML={{ __html: this.props.htmlMessage }} />
|
||||||
: <p>{this.props.message || '提示内容'}</p>
|
: <p>{this.props.message || 'INMESSAGE'}</p>
|
||||||
|
|
||||||
let cancel = (this.props.cancel || this.hide).bind(this)
|
let cancel = (this.props.cancel || this.hide).bind(this)
|
||||||
let confirm = (this.props.confirm || this.hide).bind(this)
|
let confirm = (this.props.confirm || this.hide).bind(this)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<%@ include file="/_include/Head.jsp"%>
|
<%@ include file="/_include/Head.jsp"%>
|
||||||
<title>找回密码</title>
|
<title>${bundle.lang('ForgotPassword')}</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="rb-splash-screen">
|
<body class="rb-splash-screen">
|
||||||
<div class="rb-wrapper rb-login">
|
<div class="rb-wrapper rb-login">
|
||||||
|
@ -14,38 +14,41 @@
|
||||||
<div class="card-header"><a class="logo-img"></a></div>
|
<div class="card-header"><a class="logo-img"></a></div>
|
||||||
<div class="card-body J_step1">
|
<div class="card-body J_step1">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input class="form-control" id="email" type="text" placeholder="邮箱" autocomplete="off">
|
<input class="form-control" id="email" type="text" placeholder="${bundle.lang('Email')}" autocomplete="off">
|
||||||
<p class="form-text">如果你忘记或未配置邮箱,请联系管理员重置密码</p>
|
<p class="form-text">${bundle.lang('UnsetEmailTip')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group login-submit">
|
<div class="form-group login-submit">
|
||||||
<button class="btn btn-primary btn-xl J_forgot-btn">重置密码</button>
|
<button class="btn btn-primary btn-xl J_forgot-btn">${bundle.lang('ResetPassword')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body J_step2 hide">
|
<div class="card-body J_step2 ">
|
||||||
|
<div class="alert alert-warning alert-icon alert-icon-border alert-sm">
|
||||||
|
<div class="icon"><span class="zmdi zmdi-info-outline"></span></div>
|
||||||
|
<div class="message"><p>${bundle.lang('VcodeEmailSent')} <b class="J_email ml-1"></b></p></div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-8">
|
<div class="col-7">
|
||||||
<input class="form-control" id="vcode" type="text" placeholder="请输入验证码" autocomplete="off">
|
<input class="form-control" id="vcode" type="text" placeholder="${bundle.lang('InputVcodePls')}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4 pl-0">
|
<div class="col-5 pl-0">
|
||||||
<button type="button" class="btn btn-primary bordered J_vcode-resend" style="height:41px;width:100%">获取验证码</button>
|
<button type="button" class="btn btn-primary bordered J_vcode-resend" style="height:41px;width:100%">${bundle.lang('GetVcode')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="form-text">验证码已发送至邮箱 <b class="J_email"></b></p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input class="form-control" id="newpwd" type="password" placeholder="新密码" autocomplete="off">
|
<input class="form-control" id="newpwd" type="password" placeholder="${bundle.lang('NewPassword')}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input class="form-control" id="newpwd2" type="password" placeholder="重复新密码" autocomplete="off">
|
<input class="form-control" id="newpwd2" type="password" placeholder="${bundle.lang('RepeatNewPassword')}" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group login-submit">
|
<div class="form-group login-submit">
|
||||||
<button class="btn btn-primary btn-xl J_confirm-btn">确认重置</button>
|
<button class="btn btn-primary btn-xl J_confirm-btn">${bundle.lang('ConfirmReset')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="splash-footer">
|
<div class="splash-footer">
|
||||||
<span><a href="login">返回登录</a></span>
|
<span><a href="login">${bundle.lang('ReturnHome')}</a></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,7 +60,7 @@ $(document).ready(function() {
|
||||||
let email = null
|
let email = null
|
||||||
$('.J_forgot-btn, .J_vcode-resend').click(function() {
|
$('.J_forgot-btn, .J_vcode-resend').click(function() {
|
||||||
email = $val('#email')
|
email = $val('#email')
|
||||||
if (!email){ RbHighbar.create('请输入邮箱'); return }
|
if (!email){ RbHighbar.create($lang('InputEmailPls')); return }
|
||||||
$('.J_email').text(email)
|
$('.J_email').text(email)
|
||||||
let _btn = $(this).button('loading')
|
let _btn = $(this).button('loading')
|
||||||
$.post(rb.baseUrl + '/user/user-forgot-passwd?email=' + $encode(email), function(res) {
|
$.post(rb.baseUrl + '/user/user-forgot-passwd?email=' + $encode(email), function(res) {
|
||||||
|
@ -74,15 +77,15 @@ $(document).ready(function() {
|
||||||
let vcode = $val('#vcode')
|
let vcode = $val('#vcode')
|
||||||
let newpwd = $val('#newpwd')
|
let newpwd = $val('#newpwd')
|
||||||
let newpwd2 = $val('#newpwd2')
|
let newpwd2 = $val('#newpwd2')
|
||||||
if (!vcode) { RbHighbar.create('请输入验证码'); return }
|
if (!vcode) { RbHighbar.create($lang('InputVcodePls')); return }
|
||||||
if (!newpwd) { RbHighbar.create('请输入新密码'); return }
|
if (!newpwd) { RbHighbar.create($lang('InputNewPasswordPls')); return }
|
||||||
if (newpwd !== newpwd2) { RbHighbar.create('两次输入的新密码不一致'); return }
|
if (newpwd !== newpwd2) { RbHighbar.create($lang('PasswordNotMatch')); return }
|
||||||
|
|
||||||
let _data = { email: email, vcode: vcode, newpwd: newpwd }
|
let _data = { email: email, vcode: vcode, newpwd: newpwd }
|
||||||
let _btn = $(this).button('loading')
|
let _btn = $(this).button('loading')
|
||||||
$.post(rb.baseUrl + '/user/user-confirm-passwd', JSON.stringify(_data), function(res) {
|
$.post(rb.baseUrl + '/user/user-confirm-passwd', JSON.stringify(_data), function(res) {
|
||||||
if (res.error_code == 0){
|
if (res.error_code == 0){
|
||||||
_btn.text('密码重置成功')
|
_btn.text($lang('ActionSuccess'))
|
||||||
setTimeout(()=>{ location.href = './login' }, 1000)
|
setTimeout(()=>{ location.href = './login' }, 1000)
|
||||||
} else {
|
} else {
|
||||||
RbHighbar.create(res.error_msg)
|
RbHighbar.create(res.error_msg)
|
||||||
|
@ -99,9 +102,9 @@ let resend_countdown = function(first){
|
||||||
if (countdown_timer) clearTimeout(countdown_timer)
|
if (countdown_timer) clearTimeout(countdown_timer)
|
||||||
countdown_seconds = 60
|
countdown_seconds = 60
|
||||||
}
|
}
|
||||||
$('.J_vcode-resend').text('重新获取 (' + (--countdown_seconds) + ')')
|
$('.J_vcode-resend').text($lang('GetVcode2') + ' (' + (--countdown_seconds) + ')')
|
||||||
if (countdown_seconds == 0) {
|
if (countdown_seconds == 0) {
|
||||||
$('.J_vcode-resend').attr('disabled', false).text('重新获取')
|
$('.J_vcode-resend').attr('disabled', false).text($lang('GetVcode2'))
|
||||||
} else {
|
} else {
|
||||||
countdown_timer = setTimeout(resend_countdown, 1000)
|
countdown_timer = setTimeout(resend_countdown, 1000)
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,12 @@
|
||||||
.rb-content {
|
.rb-content {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
.select-lang a {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<title>登录</title>
|
<title>${bundle.lang("Login")}</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="rb-splash-screen">
|
<body class="rb-splash-screen">
|
||||||
<div class="rb-wrapper rb-login">
|
<div class="rb-wrapper rb-login">
|
||||||
|
@ -60,32 +64,37 @@
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form id="login-form">
|
<form id="login-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input class="form-control" id="user" type="text" placeholder="用户名 (或邮箱)">
|
<input class="form-control" id="user" type="text" placeholder="${bundle.lang('UsernameOrEmail')}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input class="form-control" id="passwd" type="password" placeholder="登录密码">
|
<input class="form-control" id="passwd" type="password" placeholder="${bundle.lang('LoginPassword')}">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row pt-0 hide vcode-row" data-state="${sessionScope.needLoginVCode}">
|
<div class="form-group row pt-0 mb-3 hide vcode-row" data-state="${sessionScope.needLoginVCode}">
|
||||||
<div class="col-6 pr-0">
|
<div class="col-6 pr-0">
|
||||||
<input class="form-control" type="text" placeholder="输入右侧验证码">
|
<input class="form-control" type="text" placeholder="${bundle.lang('InputCaptcha')}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 text-right pl-0 pr-0">
|
<div class="col-6 text-right pl-0 pr-0">
|
||||||
<img style="max-width:100%;margin-right:-15px" alt="验证码" title="点击刷新">
|
<img style="max-width:100%;margin-right:-15px" alt="${bundle.lang('Captcha')}" title="${bundle.lang('ClickReload')}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row login-tools">
|
<div class="form-group row login-tools">
|
||||||
<div class="col-6 login-remember">
|
<div class="col-6 login-remember">
|
||||||
<label class="custom-control custom-checkbox custom-control-inline mb-0">
|
<label class="custom-control custom-checkbox custom-control-inline mb-0">
|
||||||
<input class="custom-control-input" type="checkbox" id="autoLogin"><span class="custom-control-label"> 记住登录</span>
|
<input class="custom-control-input" type="checkbox" id="autoLogin"><span class="custom-control-label"> ${bundle.lang('RememberMe')}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 login-forgot-password">
|
<div class="col-6 login-forgot-password">
|
||||||
<a href="forgot-passwd">找回密码</a>
|
<a href="forgot-passwd">${bundle.lang('ForgotPassword')}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group login-submit">
|
<div class="form-group login-submit">
|
||||||
<button class="btn btn-primary btn-xl" type="submit" data-loading-text="登录中">登录</button>
|
<button class="btn btn-primary btn-xl" type="submit">${bundle.lang('Login')}</button>
|
||||||
<div class="mt-4 text-center">还没有账号?<a href="signup">立即注册</a></div>
|
<div class="mt-4 text-center">${bundle.lang('NoAccountYet')} <a href="signup">${bundle.lang('SignupNow')}</a></div>
|
||||||
|
</div>
|
||||||
|
<div class="select-lang text-center mb-2">
|
||||||
|
<a href="?locale=zh_CN" title="中文"><img src="${baseUrl}/assets/img/flag/zh-CN.png" /></a>
|
||||||
|
<a href="?locale=en_US" title="English"><img src="${baseUrl}/assets/img/flag/en-US.png" /></a>
|
||||||
|
<a href="?locale=ja_JP" title="日本語"><img src="${baseUrl}/assets/img/flag/ja-JP.png" /></a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,7 +116,6 @@ useLiveWallpaper = <%=SysConfiguration.getBool(ConfigurableItem.LiveWallpaper)%>
|
||||||
<script type="text/babel">
|
<script type="text/babel">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
if (top != self) { parent.location.reload(); return }
|
if (top != self) { parent.location.reload(); return }
|
||||||
if ($urlp('t') == 99) RbHighbar.create('注册申请已提交,请等待管理员审核', 'success', { timeout: 999999 })
|
|
||||||
|
|
||||||
$('.vcode-row img').click(function(){
|
$('.vcode-row img').click(function(){
|
||||||
$(this).attr('src', rb.baseUrl + '/user/captcha?' + $random())
|
$(this).attr('src', rb.baseUrl + '/user/captcha?' + $random())
|
||||||
|
@ -123,9 +131,9 @@ $(document).ready(function() {
|
||||||
let user = $val('#user'),
|
let user = $val('#user'),
|
||||||
passwd = $val('#passwd'),
|
passwd = $val('#passwd'),
|
||||||
vcode = $val('.vcode-row input')
|
vcode = $val('.vcode-row input')
|
||||||
if (!user || !passwd){ RbHighbar.create('请输入用户名和密码'); return }
|
if (!user || !passwd){ RbHighbar.create($lang('InputUserOrPasswordPls')); return }
|
||||||
if (vcodeState && !vcode){ RbHighbar.create('请输入验证码'); return }
|
if (vcodeState && !vcode){ RbHighbar.create($lang('InputCaptchaPls')); return }
|
||||||
|
|
||||||
let btn = $('.login-submit button').button('loading')
|
let btn = $('.login-submit button').button('loading')
|
||||||
let url = rb.baseUrl + '/user/user-login?user=' + $encode(user) + '&passwd=' + $encode(passwd) + '&autoLogin=' + $val('#autoLogin')
|
let url = rb.baseUrl + '/user/user-login?user=' + $encode(user) + '&passwd=' + $encode(passwd) + '&autoLogin=' + $val('#autoLogin')
|
||||||
if (!!vcode) url += '&vcode=' + vcode
|
if (!!vcode) url += '&vcode=' + vcode
|
||||||
|
@ -138,7 +146,7 @@ $(document).ready(function() {
|
||||||
btn.button('reset')
|
btn.button('reset')
|
||||||
} else {
|
} else {
|
||||||
$('.vcode-row img').trigger('click')
|
$('.vcode-row img').trigger('click')
|
||||||
RbHighbar.create(res.error_msg || '登录失败,请稍后重试')
|
RbHighbar.create(res.error_msg)
|
||||||
btn.button('reset')
|
btn.button('reset')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -154,10 +162,16 @@ $(document).ready(function() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$('.rb-bgimg').css('background-image', 'url(' + res.url + ')').animate({ opacity: 1 })
|
$('.rb-bgimg').css('background-image', 'url(' + res.url + ')').animate({ opacity: 1 })
|
||||||
}, 400)
|
}, 400)
|
||||||
if (res.copyright) $('.rb-bgimg').attr('title', res.copyright + ' (' + res.source + ')')
|
if (res.copyright) $('.rb-bgimg').attr('alt', res.copyright + ' (' + res.source + ')')
|
||||||
}
|
}
|
||||||
}).fail(function () { /* NOOP */ })
|
}).fail(function () { /* NOOP */ })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('.select-lang>a').click(function (event) {
|
||||||
|
event.preventDefault()
|
||||||
|
let locale = $(this).attr('href')
|
||||||
|
$.post(rb.baseUrl + '/language/select' + locale, ()=> location.replace(locale))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
rebuild - Building your business-systems freely.
|
||||||
|
Copyright (C) 2019 devezhao <zhaofang123@gmail.com>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rebuild.server.helper.language;
|
||||||
|
|
||||||
|
import com.rebuild.server.TestSupport;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author devezhao
|
||||||
|
* @since 11/29/2019
|
||||||
|
*/
|
||||||
|
public class LanguagesTest extends TestSupport {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getBundle() {
|
||||||
|
System.out.println(Languages.instance.getDefaultBundle());
|
||||||
|
System.out.println(Languages.instance.getCurrentBundle());
|
||||||
|
System.out.println(Languages.instance.getBundle(Locale.getDefault()));
|
||||||
|
|
||||||
|
assertEquals(Locale.US.toString(), Languages.instance.getBundle(Locale.US).locale());
|
||||||
|
assertEquals(Locale.JAPAN.toString(), Languages.instance.getBundle(Locale.JAPAN).locale());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lang() {
|
||||||
|
System.out.println(Languages.lang("rebuild"));
|
||||||
|
System.out.println(Languages.lang("rebuild-undef"));
|
||||||
|
|
||||||
|
System.out.println(Languages.instance.getBundle(Locale.US).lang("rebuild"));
|
||||||
|
System.out.println(Languages.instance.getBundle(Locale.JAPAN).lang("rebuild"));
|
||||||
|
System.out.println(Languages.instance.getBundle(Locale.GERMAN).lang("rebuild"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void langMerge() {
|
||||||
|
System.out.println(Languages.lang("UsernameOrEmail"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue