refactor and add i18n, to be continue

pull/680/head
shiqstone 2021-03-30 16:18:02 +08:00
parent 782ea44388
commit 1239620f75
9 changed files with 240 additions and 135 deletions

View File

@ -163,40 +163,40 @@ func RegisterLogger(log string) {
if level := beego.AppConfig.DefaultString("log_level", "Trace"); level != "" {
switch level {
case "Emergency":
config["level"] = beego.LevelEmergency
config["level"] = logs.LevelEmergency
break
case "Alert":
config["level"] = beego.LevelAlert
config["level"] = logs.LevelAlert
break
case "Critical":
config["level"] = beego.LevelCritical
config["level"] = logs.LevelCritical
break
case "Error":
config["level"] = beego.LevelError
config["level"] = logs.LevelError
break
case "Warning":
config["level"] = beego.LevelWarning
config["level"] = logs.LevelWarning
break
case "Notice":
config["level"] = beego.LevelNotice
config["level"] = logs.LevelNotice
break
case "Informational":
config["level"] = beego.LevelInformational
config["level"] = logs.LevelInformational
break
case "Debug":
config["level"] = beego.LevelDebug
config["level"] = logs.LevelDebug
break
}
}
b, err := json.Marshal(config)
if err != nil {
logs.Error("初始化文件日志时出错 ->", err)
_ = beego.SetLogger("file", `{"filename":"`+logPath+`"}`)
_ = logs.SetLogger("file", `{"filename":"`+logPath+`"}`)
} else {
_ = beego.SetLogger(logs.AdapterFile, string(b))
_ = logs.SetLogger(logs.AdapterFile, string(b))
}
beego.SetLogFuncCall(true)
logs.SetLogFuncCall(true)
}
// RunCommand 注册orm命令行工具
@ -454,7 +454,7 @@ func RegisterCache() {
} else {
cache.Init(&cache.NullCache{})
beego.Warn("不支持的缓存管道,缓存将禁用 ->", cacheProvider)
logs.Warn("不支持的缓存管道,缓存将禁用 ->", cacheProvider)
return
}
logs.Info("缓存初始化完成.")

View File

@ -7,8 +7,58 @@ person_center = Persona Center
my_project = My Project
my_blog = My Article
manage = Management
login = Login
logout = Logout
login = Log In
logout = Log Out
official_website = Official Website
feedback = Feedback
source_code = Source Code
manual = Manual
username = Username
email = Email
password = Password
captcha = Captcha
keep_login = Stay signed in
forgot_password = Forgot password?
register = Create New Account
dingtalk_login = DingTalk QrCode login
account_recovery = Account recovery
new_password = New password
confirm_password = Confirm password
[message]
keyword_placeholder = input keyword please...
wrong_account_password = Incorrect username or password
click_to_change = Click to change one
logging_in = logging in...
return_account_login = Return account password login
no_account_yet = No account yet?
account_empty = Account cannot be empty
email_empty = Email cannot be empty
password_empty = Password cannot be empty
captcha_empty = Captcha cannot be empty
system_error = System error
processing = Processing...
email_sent = The email is sent successfully, please log in to check it.
password_empty = Confirm password cannot be empty
incorrect_confirm_password = Incorrect confirm password
illegal_request = Illegal request
account_or_password_empty = Account or Password cannot be empty
captcha_wrong = Incorrect captcha
password_length_invalid = The password cannot be empty and must be between 6-50 characters
mail_expired = Mail has expired
captcha_expired = The verification code has expired, please try again.
user_not_existed = User does not exist
email_not_exist = Email does not exist
failed_save_password = Failed to save password
mail_service_not_enable = Mail service is not enabled
account_disable = Account has been disabled
failed_send_mail = Failed to send mail
sent_too_many_times = Send too many times, please try again later
account_not_support_retrieval = The current user does not support password retrieval
username_invalid_format = The account number can only be composed of English alphanumerics and 3-50 characters
email_invalid_format = Email format is incorrect
account_existed = Username already existed
failed_register = Registration failed, please contact the system administrator
failed_obtain_user_info = Failed to obtain identity information
dingtalk_auto_login_not_enable = DingTalk automatic login function is not enabled
failed_auto_login = Automatic login failed

View File

@ -9,6 +9,56 @@ my_blog = 我的文章
manage = 管理后台
login = 登录
logout = 退出登录
official_website = 官方网站
feedback = 意见反馈
source_code = 项目源码
manual = 使用手册
username = 用户名
email = 邮箱
password = 密码
captcha = 验证码
keep_login = 保持登录
forgot_password = 忘记密码?
register = 立即注册
dingtalk_login = 扫码登录
account_recovery = 找回密码
new_password = 新密码
confirm_password = 确认密码
[message]
keyword_placeholder = 请输入关键词...
wrong_account_password = 账号或密码错误
click_to_change = 点击换一张
logging_in = 正在登录...
return_account_login = 返回账号密码登录
no_account_yet = 还没有账号?
account_empty = 账号不能为空
email_empty = 邮箱不能为空
password_empty = 密码不能为空
captcha_empty = 验证码不能为空
system_error = 系统错误
processing = 正在处理...
email_sent = 邮件发送成功,请登录邮箱查看。
confirm_password_empty = 确认密码不能为空
incorrect_confirm_password = 确认密码输入不正确
illegal_request = 非法请求
account_or_password_empty = 账号或密码不能为空
captcha_wrong = 验证码不正确
password_length_invalid = 密码不能为空且必须在6-50个字符之间
mail_expired = 邮件已失效
captcha_expired = 验证码已过期,请重新操作。
user_not_existed = 用户不存在
email_not_exist = 邮箱不存在
failed_save_password = 保存密码失败
mail_service_not_enable = 未启用邮件服务
account_disable = 账号已被禁用
failed_send_mail = 发送邮件失败
sent_too_many_times = 发送次数太多,请稍候再试
account_not_support_retrieval = 当前用户不支持找回密码
username_invalid_format = 账号只能由英文字母数字组成且在3-50个字符
email_invalid_format = 邮箱格式不正确
account_existed = 账号已存在
failed_register = 注册失败,请联系管理员
failed_obtain_user_info = 获取身份信息失败
dingtalk_auto_login_not_enable = 未开启钉钉自动登录功能
failed_auto_login = 自动登录失败

View File

@ -54,17 +54,17 @@ func (c *AccountController) Prepare() {
}
if token == "" {
if c.IsAjax() {
c.JsonResult(403, "非法请求")
c.JsonResult(403, i18n.Tr(c.Lang, "message.illegal_request"))
} else {
c.ShowErrorPage(403, "非法请求")
c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.illegal_request"))
}
}
xsrfToken := c.XSRFToken()
if xsrfToken != token {
if c.IsAjax() {
c.JsonResult(403, "非法请求")
c.JsonResult(403, i18n.Tr(c.Lang, "message.illegal_request"))
} else {
c.ShowErrorPage(403, "非法请求")
c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.illegal_request"))
}
}
}
@ -108,12 +108,12 @@ func (c *AccountController) Login() {
if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") {
v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确")
c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
}
}
if account == "" || password == "" {
c.JsonResult(6002, "账号或密码不能为空")
c.JsonResult(6002, i18n.Tr(c.Lang, "message.account_or_password_empty"))
}
member, err := models.NewMember().Login(account, password)
@ -149,7 +149,7 @@ func (c *AccountController) DingTalkLogin() {
code := c.GetString("dingtalk_code")
if code == "" {
c.JsonResult(500, "获取身份信息失败", nil)
c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_obtain_user_info"), nil)
}
appKey := beego.AppConfig.String("dingtalk_app_key")
@ -157,29 +157,29 @@ func (c *AccountController) DingTalkLogin() {
tmpReader := beego.AppConfig.String("dingtalk_tmp_reader")
if appKey == "" || appSecret == "" || tmpReader == "" {
c.JsonResult(500, "未开启钉钉自动登录功能", nil)
c.JsonResult(500, i18n.Tr(c.Lang, "message.dingtalk_auto_login_not_enable"), nil)
c.StopRun()
}
dingtalkAgent := dingtalk.NewDingTalkAgent(appSecret, appKey)
err := dingtalkAgent.GetAccesstoken()
if err != nil {
beego.Warn("获取钉钉临时Token失败 ->", err)
c.JsonResult(500, "自动登录失败", nil)
logs.Warn("获取钉钉临时Token失败 ->", err)
c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_auto_login"), nil)
c.StopRun()
}
userid, err := dingtalkAgent.GetUserIDByCode(code)
if err != nil {
beego.Warn("获取钉钉用户ID失败 ->", err)
c.JsonResult(500, "自动登录失败", nil)
logs.Warn("获取钉钉用户ID失败 ->", err)
c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_auto_login"), nil)
c.StopRun()
}
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
if err != nil {
beego.Warn("获取钉钉用户信息失败 ->", err)
c.JsonResult(500, "自动登录失败", nil)
logs.Warn("获取钉钉用户信息失败 ->", err)
c.JsonResult(500, i18n.Tr(c.Lang, "message.failed_auto_login"), nil)
c.StopRun()
}
@ -218,7 +218,7 @@ func (c *AccountController) QRLogin() {
qrDingtalk := dingtalk.NewDingtalkQRLogin(appSecret, appKey)
unionID, err := qrDingtalk.GetUnionIDByCode(code)
if err != nil {
beego.Warn("获取钉钉临时UnionID失败 ->", err)
logs.Warn("获取钉钉临时UnionID失败 ->", err)
c.Redirect(conf.URLFor("AccountController.Login"), 302)
c.StopRun()
}
@ -230,21 +230,21 @@ func (c *AccountController) QRLogin() {
dingtalkAgent := dingtalk.NewDingTalkAgent(appSecret, appKey)
err = dingtalkAgent.GetAccesstoken()
if err != nil {
beego.Warn("获取钉钉临时Token失败 ->", err)
logs.Warn("获取钉钉临时Token失败 ->", err)
c.Redirect(conf.URLFor("AccountController.Login"), 302)
c.StopRun()
}
userid, err := dingtalkAgent.GetUserIDByUnionID(unionID)
if err != nil {
beego.Warn("获取钉钉用户ID失败 ->", err)
logs.Warn("获取钉钉用户ID失败 ->", err)
c.Redirect(conf.URLFor("AccountController.Login"), 302)
c.StopRun()
}
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
if err != nil {
beego.Warn("获取钉钉用户信息失败 ->", err)
logs.Warn("获取钉钉用户信息失败 ->", err)
c.Redirect(conf.URLFor("AccountController.Login"), 302)
c.StopRun()
}
@ -308,29 +308,29 @@ func (c *AccountController) Register() {
captcha := c.GetString("code")
if ok, err := regexp.MatchString(conf.RegexpAccount, account); account == "" || !ok || err != nil {
c.JsonResult(6001, "账号只能由英文字母数字组成且在3-50个字符")
c.JsonResult(6001, i18n.Tr(c.Lang, "message.username_invalid_format"))
}
if l := strings.Count(password1, ""); password1 == "" || l > 50 || l < 6 {
c.JsonResult(6002, "密码必须在6-50个字符之间")
c.JsonResult(6002, i18n.Tr(c.Lang, "message.password_length_invalid"))
}
if password1 != password2 {
c.JsonResult(6003, "确认密码不正确")
c.JsonResult(6003, i18n.Tr(c.Lang, "message.incorrect_confirm_password"))
}
if ok, err := regexp.MatchString(conf.RegexpEmail, email); !ok || err != nil || email == "" {
c.JsonResult(6004, "邮箱格式不正确")
c.JsonResult(6004, i18n.Tr(c.Lang, "message.email_invalid_format"))
}
// 如果开启了验证码
if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") {
v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确")
c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
}
}
member := models.NewMember()
if _, err := member.FindByAccount(account); err == nil && member.MemberId > 0 {
c.JsonResult(6005, "账号已存在")
c.JsonResult(6005, i18n.Tr(c.Lang, "message.account_existed"))
}
member.Account = account
@ -341,7 +341,7 @@ func (c *AccountController) Register() {
member.Email = email
member.Status = 0
if err := member.Add(); err != nil {
c.JsonResult(6006, "注册失败,请联系系统管理员处理")
c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed_register"))
}
c.JsonResult(0, "ok", member)
@ -359,39 +359,39 @@ func (c *AccountController) FindPassword() {
captcha := c.GetString("code")
if email == "" {
c.JsonResult(6005, "邮箱地址不能为空")
c.JsonResult(6005, i18n.Tr(c.Lang, "message.email_empty"))
}
if !mailConf.EnableMail {
c.JsonResult(6004, "未启用邮件服务")
c.JsonResult(6004, i18n.Tr(c.Lang, "message.mail_service_not_enable"))
}
// 如果开启了验证码
if v, ok := c.Option["ENABLED_CAPTCHA"]; ok && strings.EqualFold(v, "true") {
v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确")
c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
}
}
member, err := models.NewMember().FindByFieldFirst("email", email)
if err != nil {
c.JsonResult(6006, "邮箱不存在")
c.JsonResult(6006, i18n.Tr(c.Lang, "message.email_not_exist"))
}
if member.Status != 0 {
c.JsonResult(6007, "账号已被禁用")
if member == nil || member.Status != 0 {
c.JsonResult(6007, i18n.Tr(c.Lang, "message.account_disable"))
}
if member.AuthMethod == conf.AuthMethodLDAP {
c.JsonResult(6011, "当前用户不支持找回密码")
if member == nil || member.AuthMethod == conf.AuthMethodLDAP {
c.JsonResult(6011, i18n.Tr(c.Lang, "message.account_not_support_retrieval"))
}
count, err := models.NewMemberToken().FindSendCount(email, time.Now().Add(-1*time.Hour), time.Now())
if err != nil {
logs.Error(err)
c.JsonResult(6008, "发送邮件失败")
c.JsonResult(6008, i18n.Tr(c.Lang, "message.failed_send_mail"))
}
if count > mailConf.MailNumber {
c.JsonResult(6008, "发送次数太多,请稍候再试")
c.JsonResult(6008, i18n.Tr(c.Lang, "message.sent_too_many_times"))
}
memberToken := models.NewMemberToken()
@ -401,7 +401,7 @@ func (c *AccountController) FindPassword() {
memberToken.MemberId = member.MemberId
memberToken.IsValid = false
if _, err := memberToken.InsertOrUpdate(); err != nil {
c.JsonResult(6009, "邮件发送失败")
c.JsonResult(6009, i18n.Tr(c.Lang, "message.failed_send_mail"))
}
data := map[string]interface{}{
@ -413,7 +413,7 @@ func (c *AccountController) FindPassword() {
body, err := c.ExecuteViewPathTemplate("account/mail_template.tpl", data)
if err != nil {
logs.Error(err)
c.JsonResult(6003, "邮件发送失败")
c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed_send_mail"))
}
go func(mailConf *conf.SmtpConf, email string, body string) {
@ -472,17 +472,16 @@ func (c *AccountController) FindPassword() {
if token != "" && email != "" {
memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token)
if err != nil {
logs.Error(err)
c.Data["ErrorMessage"] = "邮件已失效"
c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.mail_expired")
c.TplName = "errors/error.tpl"
return
}
subTime := memberToken.SendTime.Sub(time.Now())
if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() {
c.Data["ErrorMessage"] = "验证码已过期,请重新操作。"
c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.captcha_expired")
c.TplName = "errors/error.tpl"
return
}
@ -503,48 +502,46 @@ func (c *AccountController) ValidEmail() {
email := c.GetString("mail")
if password1 == "" {
c.JsonResult(6001, "密码不能为空")
c.JsonResult(6001, i18n.Tr(c.Lang, "message.password_empty"))
}
if l := strings.Count(password1, ""); l < 6 || l > 50 {
c.JsonResult(6001, "密码不能为空且必须在6-50个字符之间")
c.JsonResult(6001, i18n.Tr(c.Lang, "message.password_length_invalid"))
}
if password2 == "" {
c.JsonResult(6002, "确认密码不能为空")
c.JsonResult(6002, i18n.Tr(c.Lang, "message.confirm_password_empty"))
}
if password1 != password2 {
c.JsonResult(6003, "确认密码输入不正确")
c.JsonResult(6003, i18n.Tr(c.Lang, "message.incorrect_confirm_password"))
}
if captcha == "" {
c.JsonResult(6004, "验证码不能为空")
c.JsonResult(6004, i18n.Tr(c.Lang, "message.captcha_empty"))
}
v, ok := c.GetSession(conf.CaptchaSessionName).(string)
if !ok || !strings.EqualFold(v, captcha) {
c.JsonResult(6001, "验证码不正确")
c.JsonResult(6001, i18n.Tr(c.Lang, "message.captcha_wrong"))
}
mailConf := conf.GetMailConfig()
memberToken, err := models.NewMemberToken().FindByFieldFirst("token", token)
if err != nil {
logs.Error(err)
c.JsonResult(6007, "邮件已失效")
c.JsonResult(6007, i18n.Tr(c.Lang, "message.mail_expired"))
}
subTime := memberToken.SendTime.Sub(time.Now())
if !strings.EqualFold(memberToken.Email, email) || subTime.Minutes() > float64(mailConf.MailExpired) || !memberToken.ValidTime.IsZero() {
c.JsonResult(6008, "验证码已过期,请重新操作。")
c.JsonResult(6008, i18n.Tr(c.Lang, "message.captcha_expired"))
}
member, err := models.NewMember().Find(memberToken.MemberId)
if err != nil {
logs.Error(err)
c.JsonResult(6005, "用户不存在")
c.JsonResult(6005, i18n.Tr(c.Lang, "message.user_not_existed"))
}
hash, err := utils.PasswordHash(password1)
if err != nil {
logs.Error(err)
c.JsonResult(6006, "保存密码失败")
c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed_save_password"))
}
member.Password = hash
@ -556,7 +553,7 @@ func (c *AccountController) ValidEmail() {
if err != nil {
logs.Error(err)
c.JsonResult(6006, "保存密码失败")
c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed_save_password"))
}
c.JsonResult(0, "ok", conf.URLFor("AccountController.Login"))
}
@ -564,11 +561,8 @@ func (c *AccountController) ValidEmail() {
// Logout 退出登录
func (c *AccountController) Logout() {
c.SetMember(models.Member{})
c.SetSecureCookie(conf.GetAppKey(), "login", "", -3600)
u := c.Ctx.Request.Header.Get("Referer")
c.Redirect(conf.URLFor("AccountController.Login", "url", u), 302)
}
@ -578,11 +572,6 @@ func (c *AccountController) Captcha() {
captchaImage := gocaptcha.NewCaptchaImage(140, 40, gocaptcha.RandLightColor())
//if err != nil {
// logs.Error(err)
// c.Abort("500")
//}
captchaImage.DrawNoise(gocaptcha.CaptchaComplexLower)
// captchaImage.DrawTextNoise(gocaptcha.CaptchaComplexHigh)

View File

@ -2,9 +2,9 @@ package controllers
import (
"bytes"
"github.com/beego/i18n"
"encoding/json"
"github.com/astaxie/beego/logs"
"github.com/beego/i18n"
"io"
"strings"
"time"
@ -24,6 +24,7 @@ type BaseController struct {
Option map[string]string
EnableAnonymous bool
EnableDocumentHistory bool
Lang string
}
type CookieRemember struct {
@ -45,7 +46,6 @@ func (c *BaseController) Prepare() {
c.EnableDocumentHistory = false
if member, ok := c.GetSession(conf.LoginSessionName).(models.Member); ok && member.MemberId > 0 {
c.Member = &member
c.Data["Member"] = c.Member
} else {
@ -78,13 +78,8 @@ func (c *BaseController) Prepare() {
if b, err := ioutil.ReadFile(filepath.Join(beego.BConfig.WebConfig.ViewsPath, "widgets", "scripts.tpl")); err == nil {
c.Data["Scripts"] = template.HTML(string(b))
}
lang := c.Input().Get("lang")
if len(lang) == 0 ||
!i18n.IsExist(lang) {
lang = "zh-cn"
}
c.Data["Lang"] = lang
c.SetLang()
}
//判断用户是否登录.
@ -117,14 +112,16 @@ func (c *BaseController) JsonResult(errCode int, errMsg string, data ...interfac
}
returnJSON, err := json.Marshal(jsonData)
if err != nil {
beego.Error(err)
logs.Error(err)
}
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8")
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache, no-store")
io.WriteString(c.Ctx.ResponseWriter, string(returnJSON))
_, err = io.WriteString(c.Ctx.ResponseWriter, string(returnJSON))
if err != nil {
logs.Error(err)
}
c.StopRun()
}
@ -141,14 +138,16 @@ func (c *BaseController) CheckJsonError(code int, err error) {
jsonData["message"] = err.Error()
returnJSON, err := json.Marshal(jsonData)
if err != nil {
beego.Error(err)
logs.Error(err)
}
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json; charset=utf-8")
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache, no-store")
io.WriteString(c.Ctx.ResponseWriter, string(returnJSON))
_, err = io.WriteString(c.Ctx.ResponseWriter, string(returnJSON))
if err != nil {
logs.Error(err)
}
c.StopRun()
}
@ -201,9 +200,26 @@ func (c *BaseController) ShowErrorPage(errCode int, errMsg string) {
}
}
func (c *BaseController) CheckErrorResult(code int, err error) {
if err != nil {
c.ShowErrorPage(code, err.Error())
}
}
func (c *BaseController) SetLang() {
hasCookie := false
lang := c.Input().Get("lang")
if len(lang) == 0 {
lang = c.Ctx.GetCookie("lang")
hasCookie = true
}
if len(lang) == 0 ||
!i18n.IsExist(lang) {
lang = beego.AppConfig.String("default_lang")
}
if !hasCookie {
c.Ctx.SetCookie("lang", lang, 1<<31-1, "/")
}
c.Data["Lang"] = lang
c.Lang = lang
}

View File

@ -7,7 +7,7 @@
<meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="MinDoc" />
<title>找回密码 - Powered by MinDoc</title>
<title>{{i18n .Lang "common.account_recovery"}} - Powered by MinDoc</title>
<!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -35,13 +35,13 @@
<div class="login-body">
<form role="form" method="post" id="findPasswordForm">
{{ .xsrfdata }}
<h3 class="text-center">找回密码</h3>
<h3 class="text-center">{{i18n .Lang "common.account_recovery"}}</h3>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">
<i class="fa fa-at"></i>
</div>
<input type="text" class="form-control" placeholder="邮箱" name="email" id="email" autocomplete="off">
<input type="text" class="form-control" placeholder="{{i18n .Lang "common.email"}}" name="email" id="email" autocomplete="off">
</div>
</div>
<div class="form-group">
@ -49,14 +49,14 @@
<div class="input-group-addon">
<i class="fa fa-check-square"></i>
</div>
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">&nbsp;
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="{{i18n .Lang "common.captcha"}}" autocomplete="off">&nbsp;
</div>
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张">
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="{{i18n .Lang "message.click_to_change"}}">
<div class="clearfix"></div>
</div>
<div class="form-group">
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="正在处理..." autocomplete="off">找回密码</button>
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="{{i18n .Lang "message.processing"}}" autocomplete="off">{{i18n .Lang "common.account_recovery"}}</button>
</div>
</form>
@ -88,7 +88,7 @@
var email = $.trim($("#email").val());
if(email === ""){
$("#email").tooltip({placement:"auto",title : "邮箱不能为空",trigger : 'manual'})
$("#email").tooltip({placement:"auto",title : "{{i18n .Lang "message.email_empty"}}",trigger : 'manual'})
.tooltip('show')
.parents('.form-group').addClass('has-error');
$btn.button('reset');
@ -97,7 +97,7 @@
}
var code = $.trim($("#code").val());
if(code === ""){
$("#code").tooltip({title : '验证码不能为空',trigger : 'manual'})
$("#code").tooltip({title : '{{i18n .Lang "message.captcha_empty"}}',trigger : 'manual'})
.tooltip('show')
.parents('.form-group').addClass('has-error');
$btn.button('reset');
@ -113,14 +113,14 @@
layer.msg(res.message);
$("#btnSendMail").button('reset');
}else{
alert("邮件发送成功,请登录邮箱查看。")
alert("{{i18n .Lang "message.email_sent"}}")
window.location = res.data;
}
},
error :function () {
$("#captcha-img").click();
$("#code").val('');
layer.msg('系统错误');
layer.msg('{{i18n .Lang "message.system_error"}}');
$("#btnSendMail").button('reset');
}
});

View File

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="SmartWiki" />
<title>找回密码 - Powered by MinDoc</title>
<meta name="author" content="MinDoc" />
<title>{{i18n .Lang "common.account_recovery"}} - Powered by MinDoc</title>
<!-- Bootstrap -->
<link href="{{cdncss "/static/bootstrap/css/bootstrap.min.css"}}" rel="stylesheet">
@ -37,14 +37,14 @@
{{ .xsrfdata }}
<input type="hidden" name="token" value="{{.Token}}">
<input type="hidden" name="mail" value="{{.Email}}">
<h3 class="text-center">找回密码</h3>
<h3 class="text-center">{{i18n .Lang "common.account_recovery"}}</h3>
<div class="form-group">
<label for="newPasswd">新密码</label>
<input type="password" class="form-control" name="password1" id="newPassword" maxlength="20" placeholder="新密码" autocomplete="off">
<label for="newPasswd">{{i18n .Lang "common.new_password"}}</label>
<input type="password" class="form-control" name="password1" id="newPassword" maxlength="20" placeholder="{{i18n .Lang "common.new_password"}}" autocomplete="off">
</div>
<div class="form-group">
<label for="configPasswd">确认密码</label>
<input type="password" class="form-control" id="confirmPassword" name="password2" maxlength="20" placeholder="确认密码" autocomplete="off">
<label for="configPasswd">{{i18n .Lang "common.confirm_password"}}</label>
<input type="password" class="form-control" id="confirmPassword" name="password2" maxlength="20" placeholder="{{i18n .Lang "common.confirm_password"}}" autocomplete="off">
</div>
<div class="form-group">
@ -52,13 +52,13 @@
<div class="input-group-addon">
<i class="fa fa-check-square"></i>
</div>
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">&nbsp;
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="{{i18n .Lang "common.captcha"}}" autocomplete="off">&nbsp;
</div>
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张">
<div class="clearfix"></div>
</div>
<div class="form-group">
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="正在处理..." autocomplete="off">找回密码</button>
<button type="submit" id="btnSendMail" class="btn btn-success" style="width: 100%" data-loading-text="{{i18n .Lang "message.processing"}}" autocomplete="off">{{i18n .Lang "common.account_recovery"}}</button>
</div>
</form>
@ -92,26 +92,26 @@
var code = $.trim($("#code").val());
if(newPassword === ""){
$("#newPassword").tooltip({placement:"auto",title : "密码不能为空",trigger : 'manual'})
$("#newPassword").tooltip({placement:"auto",title : "{{i18n .Lang "message.password_empty"}}",trigger : 'manual'})
.tooltip('show')
.parents('.form-group').addClass('has-error');
return false;
}else if(confirmPassword === ""){
$("#confirmPassword").tooltip({placement:"auto",title : "确认密码不能为空",trigger : 'manual'})
$("#confirmPassword").tooltip({placement:"auto",title : "{{i18n .Lang "message.confirm_password_empty"}}",trigger : 'manual'})
.tooltip('show')
.parents('.form-group').addClass('has-error');
return false;
}else if(newPassword !== confirmPassword) {
$("#confirmPassword").tooltip({placement:"auto",title : "确认密码输入不正确",trigger : 'manual'})
$("#confirmPassword").tooltip({placement:"auto",title : "{{i18n .Lang "message.incorrect_confirm_password"}}",trigger : 'manual'})
.tooltip('show')
.parents('.form-group').addClass('has-error');
return false;
}else if(code === ""){
$("#code").tooltip({title : '验证码不能为空',trigger : 'manual'})
$("#code").tooltip({title : '{{i18n .Lang "message.captcha_empty"}}',trigger : 'manual'})
.tooltip('show')
.parents('.form-group').addClass('has-error');
@ -134,7 +134,7 @@
error :function () {
$("#captcha-img").click();
$("#code").val('');
layer.msg('系统错误');
layer.msg('{{i18n .Lang "message.system_error"}}');
$("#btnSendMail").button('reset');
}
});

View File

@ -7,7 +7,7 @@
<meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="MinDoc" />
<title>用户登录 - Powered by MinDoc</title>
<title>{{i18n .Lang "common.login"}} - Powered by MinDoc</title>
<meta name="keywords" content="MinDoc,文档在线管理系统,WIKI,wiki,wiki在线,文档在线管理,接口文档在线管理,接口文档管理">
<meta name="description" content="MinDoc文档在线管理系统 {{.site_description}}">
<!-- Bootstrap -->
@ -30,13 +30,13 @@
<div class="login-body">
<form role="form" method="post">
{{ .xsrfdata }}
<h3 class="text-center">用户登录</h3>
<h3 class="text-center">{{i18n .Lang "common.login"}}</h3>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">
<i class="fa fa-user"></i>
</div>
<input type="text" class="form-control" placeholder="邮箱 / 用户名" name="account" id="account" autocomplete="off">
<input type="text" class="form-control" placeholder="{{i18n .Lang "common.email"}} / {{i18n .Lang "common.username"}}" name="account" id="account" autocomplete="off">
</div>
</div>
<div class="form-group">
@ -44,7 +44,7 @@
<div class="input-group-addon">
<i class="fa fa-lock"></i>
</div>
<input type="password" class="form-control" placeholder="密码" name="password" id="password" autocomplete="off">
<input type="password" class="form-control" placeholder="{{i18n .Lang "common.password"}}" name="password" id="password" autocomplete="off">
</div>
</div>
{{if .ENABLED_CAPTCHA }}
@ -54,38 +54,38 @@
<div class="input-group-addon">
<i class="fa fa-check-square"></i>
</div>
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="验证码" autocomplete="off">&nbsp;
<input type="text" name="code" id="code" class="form-control" style="width: 150px" maxlength="5" placeholder="{{i18n .Lang "common.captcha"}}" autocomplete="off">&nbsp;
</div>
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title="点击换一张">
<img id="captcha-img" style="width: 140px;height: 40px;display: inline-block;float: right" src="{{urlfor "AccountController.Captcha"}}" onclick="this.src='{{urlfor "AccountController.Captcha"}}?key=login&t='+(new Date()).getTime();" title={{i18n .Lang "message.click_to_change"}}>
<div class="clearfix"></div>
</div>
{{end}}
{{end}}
<div class="checkbox">
<label>
<input type="checkbox" name="is_remember" value="yes"> 保持登录
<input type="checkbox" name="is_remember" value="yes"> {{i18n .Lang "common.keep_login"}}
</label>
<a href="{{urlfor "AccountController.FindPassword" }}" style="display: inline-block;float: right">忘记密码?</a>
<a href="{{urlfor "AccountController.FindPassword" }}" style="display: inline-block;float: right">{{i18n .Lang "common.forgot_password"}}</a>
</div>
<div class="form-group">
<button type="button" id="btn-login" class="btn btn-success" style="width: 100%" data-loading-text="正在登录..." autocomplete="off">立即登录</button>
<button type="button" id="btn-login" class="btn btn-success" style="width: 100%" data-loading-text="{{i18n .Lang "common.logging_in"}}" autocomplete="off">{{i18n .Lang "common.login"}}</button>
</div>
{{if .ENABLE_QR_DINGTALK}}
<div class="form-group">
<a id="btn-dingtalk-qr" class="btn btn-default" style="width: 100%" data-loading-text="" autocomplete="off">钉钉扫码登录</a>
<a id="btn-dingtalk-qr" class="btn btn-default" style="width: 100%" data-loading-text="" autocomplete="off">{{i18n .Lang "common.dingtalk_login"}}</a>
</div>
{{end}}
{{if .ENABLED_REGISTER}}
{{if ne .ENABLED_REGISTER "false"}}
<div class="form-group">
还没有账号?<a href="{{urlfor "AccountController.Register" }}" title="立即注册">立即注册</a>
{{i18n .Lang "message.no_account_yet"}}<a href="{{urlfor "AccountController.Register" }}" title={{i18n .Lang "common.register"}}>{{i18n .Lang "common.register"}}</a>
</div>
{{end}}
{{end}}
</form>
<div class="form-group dingtalk-container" style="display: none;">
<div id="dingtalk-qr-container"></div>
<a class="btn btn-default btn-dingtalk" style="width: 100%" data-loading-text="" autocomplete="off">返回账号密码登录</a>
<a class="btn btn-default btn-dingtalk" style="width: 100%" data-loading-text="" autocomplete="off">{{i18n .Lang "message.return_account_login"}}</a>
</div>
</div>
</div>
@ -196,19 +196,19 @@
var code = $("#code").val();
if (account === "") {
$("#account").tooltip({ placement: "auto", title: "账号不能为空", trigger: 'manual' })
$("#account").tooltip({ placement: "auto", title: "{{i18n .Lang "message.account_empty"}}", trigger: 'manual' })
.tooltip('show')
.parents('.form-group').addClass('has-error');
$btn.button('reset');
return false;
} else if (password === "") {
$("#password").tooltip({ title: '密码不能为空', trigger: 'manual' })
$("#password").tooltip({ title: '{{i18n .Lang "message.password_empty"}}', trigger: 'manual' })
.tooltip('show')
.parents('.form-group').addClass('has-error');
$btn.button('reset');
return false;
} else if (code !== undefined && code === "") {
$("#code").tooltip({ title: '验证码不能为空', trigger: 'manual' })
$("#code").tooltip({ title: '{{i18n .Lang "message.captcha_empty"}}', trigger: 'manual' })
.tooltip('show')
.parents('.form-group').addClass('has-error');
$btn.button('reset');
@ -236,7 +236,7 @@
error: function () {
$("#captcha-img").click();
$("#code").val('');
layer.msg('系统错误');
layer.msg('{{i18n .Lang "message.system_error"}}');
$btn.button('reset');
}
});

View File

@ -1,13 +1,13 @@
<div class="footer">
<div class="container">
<div class="row text-center border-top">
<span><a href="https://www.iminho.me" target="_blank">官方网站</a></span>
<span><a href="https://www.iminho.me" target="_blank">{{i18n .Lang "common.official_website"}}</a></span>
<span>&nbsp;·&nbsp;</span>
<span><a href="https://github.com/mindoc-org/mindoc/issues" target="_blank">意见反馈</a></span>
<span><a href="https://github.com/mindoc-org/mindoc/issues" target="_blank">{{i18n .Lang "common.feedback"}}</a></span>
<span>&nbsp;·&nbsp;</span>
<span><a href="https://github.com/mindoc-org/mindoc" target="_blank">项目源码</a></span>
<span><a href="https://github.com/mindoc-org/mindoc" target="_blank">{{i18n .Lang "common.source_code"}}</a></span>
<span>&nbsp;·&nbsp;</span>
<span><a href="https://www.iminho.me/wiki/docs/mindoc/" target="_blank">使用手册</a></span>
<span><a href="https://www.iminho.me/wiki/docs/mindoc/" target="_blank">{{i18n .Lang "common.manual"}}</a></span>
</div>
{{if .site_beian}}
<div class="row text-center">