Support for optional emails.
Reduce boilerplate and allocations. Fix the error shown on AccountEditEmailTokenSubmit when there aren't any emails rows. Add register_account_email_optional phrase. Add account_email_none phrase.
This commit is contained in:
parent
146e5cff0d
commit
d2be6b220e
|
@ -91,7 +91,7 @@ func (s *MemoryGroupStore) LoadGroups() error {
|
|||
s.groupCount = i
|
||||
|
||||
DebugLog("Binding the Not Loggedin Group")
|
||||
GuestPerms = s.dirtyGetUnsafe(6).Perms
|
||||
GuestPerms = s.dirtyGetUnsafe(6).Perms // ! Race?
|
||||
TopicListThaw.Thaw()
|
||||
return nil
|
||||
}
|
||||
|
@ -164,30 +164,30 @@ func (s *MemoryGroupStore) Reload(id int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *MemoryGroupStore) initGroup(group *Group) error {
|
||||
err := json.Unmarshal(group.PermissionsText, &group.Perms)
|
||||
func (s *MemoryGroupStore) initGroup(g *Group) error {
|
||||
err := json.Unmarshal(g.PermissionsText, &g.Perms)
|
||||
if err != nil {
|
||||
log.Printf("group: %+v\n", group)
|
||||
log.Print("bad group perms: ", group.PermissionsText)
|
||||
log.Printf("group: %+v\n", g)
|
||||
log.Print("bad group perms: ", g.PermissionsText)
|
||||
return err
|
||||
}
|
||||
DebugLogf(group.Name+": %+v\n", group.Perms)
|
||||
DebugLogf(g.Name+": %+v\n", g.Perms)
|
||||
|
||||
err = json.Unmarshal(group.PluginPermsText, &group.PluginPerms)
|
||||
err = json.Unmarshal(g.PluginPermsText, &g.PluginPerms)
|
||||
if err != nil {
|
||||
log.Printf("group: %+v\n", group)
|
||||
log.Print("bad group plugin perms: ", group.PluginPermsText)
|
||||
log.Printf("group: %+v\n", g)
|
||||
log.Print("bad group plugin perms: ", g.PluginPermsText)
|
||||
return err
|
||||
}
|
||||
DebugLogf(group.Name+": %+v\n", group.PluginPerms)
|
||||
DebugLogf(g.Name+": %+v\n", g.PluginPerms)
|
||||
|
||||
//group.Perms.ExtData = make(map[string]bool)
|
||||
// TODO: Can we optimise the bit where this cascades down to the user now?
|
||||
if group.IsAdmin || group.IsMod {
|
||||
group.IsBanned = false
|
||||
if g.IsAdmin || g.IsMod {
|
||||
g.IsBanned = false
|
||||
}
|
||||
|
||||
err = s.userCount.QueryRow(group.ID).Scan(&group.UserCount)
|
||||
err = s.userCount.QueryRow(g.ID).Scan(&g.UserCount)
|
||||
if err != sql.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
|
@ -209,9 +209,9 @@ func (s *MemoryGroupStore) SetCanSee(gid int, canSee []int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *MemoryGroupStore) CacheSet(group *Group) error {
|
||||
func (s *MemoryGroupStore) CacheSet(g *Group) error {
|
||||
s.Lock()
|
||||
s.groups[group.ID] = group
|
||||
s.groups[g.ID] = g
|
||||
s.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
@ -242,7 +242,6 @@ func (s *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod b
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
gid64, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
|
@ -90,12 +90,13 @@ type DefaultPageStore struct {
|
|||
}
|
||||
|
||||
func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) {
|
||||
pa := "pages"
|
||||
return &DefaultPageStore{
|
||||
get: acc.Select("pages").Columns("name, title, body, allowedGroups, menuID").Where("pid = ?").Prepare(),
|
||||
getByName: acc.Select("pages").Columns("pid, name, title, body, allowedGroups, menuID").Where("name = ?").Prepare(),
|
||||
getOffset: acc.Select("pages").Columns("pid, name, title, body, allowedGroups, menuID").Orderby("pid DESC").Limit("?,?").Prepare(),
|
||||
count: acc.Count("pages").Prepare(),
|
||||
delete: acc.Delete("pages").Where("pid = ?").Prepare(),
|
||||
get: acc.Select(pa).Columns("name, title, body, allowedGroups, menuID").Where("pid = ?").Prepare(),
|
||||
getByName: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Where("name = ?").Prepare(),
|
||||
getOffset: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Orderby("pid DESC").Limit("?,?").Prepare(),
|
||||
count: acc.Count(pa).Prepare(),
|
||||
delete: acc.Delete(pa).Where("pid = ?").Prepare(),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
|
@ -122,23 +123,23 @@ func (s *DefaultPageStore) parseAllowedGroups(raw string, page *CustomPage) erro
|
|||
}
|
||||
|
||||
func (s *DefaultPageStore) Get(id int) (*CustomPage, error) {
|
||||
page := &CustomPage{ID: id}
|
||||
p := &CustomPage{ID: id}
|
||||
rawAllowedGroups := ""
|
||||
err := s.get.QueryRow(id).Scan(&page.Name, &page.Title, &page.Body, &rawAllowedGroups, &page.MenuID)
|
||||
err := s.get.QueryRow(id).Scan(&p.Name, &p.Title, &p.Body, &rawAllowedGroups, &p.MenuID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return page, s.parseAllowedGroups(rawAllowedGroups, page)
|
||||
return p, s.parseAllowedGroups(rawAllowedGroups, p)
|
||||
}
|
||||
|
||||
func (s *DefaultPageStore) GetByName(name string) (*CustomPage, error) {
|
||||
page := BlankCustomPage()
|
||||
p := BlankCustomPage()
|
||||
rawAllowedGroups := ""
|
||||
err := s.getByName.QueryRow(name).Scan(&page.ID, &page.Name, &page.Title, &page.Body, &rawAllowedGroups, &page.MenuID)
|
||||
err := s.getByName.QueryRow(name).Scan(&p.ID, &p.Name, &p.Title, &p.Body, &rawAllowedGroups, &p.MenuID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return page, s.parseAllowedGroups(rawAllowedGroups, page)
|
||||
return p, s.parseAllowedGroups(rawAllowedGroups, p)
|
||||
}
|
||||
|
||||
func (s *DefaultPageStore) GetOffset(offset int, perPage int) (pages []*CustomPage, err error) {
|
||||
|
@ -149,17 +150,17 @@ func (s *DefaultPageStore) GetOffset(offset int, perPage int) (pages []*CustomPa
|
|||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
page := &CustomPage{ID: 0}
|
||||
p := &CustomPage{ID: 0}
|
||||
rawAllowedGroups := ""
|
||||
err := rows.Scan(&page.ID, &page.Name, &page.Title, &page.Body, &rawAllowedGroups, &page.MenuID)
|
||||
err := rows.Scan(&p.ID, &p.Name, &p.Title, &p.Body, &rawAllowedGroups, &p.MenuID)
|
||||
if err != nil {
|
||||
return pages, err
|
||||
}
|
||||
err = s.parseAllowedGroups(rawAllowedGroups, page)
|
||||
err = s.parseAllowedGroups(rawAllowedGroups, p)
|
||||
if err != nil {
|
||||
return pages, err
|
||||
}
|
||||
pages = append(pages, page)
|
||||
pages = append(pages, p)
|
||||
}
|
||||
return pages, rows.Err()
|
||||
}
|
||||
|
|
|
@ -339,7 +339,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
|
|||
}
|
||||
|
||||
t.AddStd("login", "c.Page", Page{htitle("Login Page"), tList, nil})
|
||||
t.AddStd("register", "c.Page", Page{htitle("Registration Page"), tList, "nananana"})
|
||||
t.AddStd("register", "c.Page", Page{htitle("Registration Page"), tList, false})
|
||||
t.AddStd("error", "c.ErrorPage", ErrorPage{htitle("Error"), "A problem has occurred in the system."})
|
||||
|
||||
ipSearchPage := IPSearchPage{htitle("IP Search"), map[int]*User{1: &user2}, "::1"}
|
||||
|
|
|
@ -319,9 +319,7 @@ func HasSuspiciousEmail(email string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
var dotCount int
|
||||
var shortBits int
|
||||
var currentSegmentLength int
|
||||
var dotCount, shortBits, currentSegmentLength int
|
||||
for _, char := range lowEmail {
|
||||
if char == '.' {
|
||||
dotCount++
|
||||
|
@ -337,25 +335,40 @@ func HasSuspiciousEmail(email string) bool {
|
|||
return dotCount > 7 || shortBits > 2
|
||||
}
|
||||
|
||||
var weakPassStrings = []string{"test", "123","6969","password", "qwerty", "fuck", "love"}
|
||||
|
||||
// TODO: Write a test for this
|
||||
func WeakPassword(password string, username string, email string) error {
|
||||
lowPassword := strings.ToLower(password)
|
||||
switch {
|
||||
case password == "":
|
||||
return errors.New("You didn't put in a password.")
|
||||
case strings.Contains(lowPassword, strings.ToLower(username)) && len(username) > 3:
|
||||
return errors.New("You can't use your username in your password.")
|
||||
case strings.Contains(lowPassword, strings.ToLower(email)):
|
||||
return errors.New("You can't use your email in your password.")
|
||||
case len(password) < 8:
|
||||
return errors.New("Your password needs to be at-least eight characters long")
|
||||
case strings.Contains(lowPassword, strings.ToLower(username)) && len(username) > 3:
|
||||
return errors.New("You can't use your username in your password.")
|
||||
case email != "" && strings.Contains(lowPassword, strings.ToLower(email)):
|
||||
return errors.New("You can't use your email in your password.")
|
||||
}
|
||||
|
||||
if strings.Contains(lowPassword, "test") || strings.Contains(password, "123") || strings.Contains(lowPassword, "password") || strings.Contains(lowPassword, "qwerty") || strings.Contains(lowPassword, "fuck") || strings.Contains(lowPassword, "love") {
|
||||
return errors.New("You may not have 'test', '123', 'password', 'qwerty', 'love' or 'fuck' in your password")
|
||||
for _, passBit := range weakPassStrings {
|
||||
if strings.Contains(lowPassword, passBit) {
|
||||
s := "You may not have "
|
||||
for i, passBit := range weakPassStrings {
|
||||
if i > 0 {
|
||||
if i == len(weakPassStrings)-1 {
|
||||
s += " or "
|
||||
} else {
|
||||
s += ", "
|
||||
}
|
||||
}
|
||||
s += "'" + passBit + "'"
|
||||
}
|
||||
return errors.New(s + " in your password")
|
||||
}
|
||||
}
|
||||
|
||||
var charMap = make(map[rune]int)
|
||||
charMap := make(map[rune]int)
|
||||
var numbers, symbols, upper, lower int
|
||||
for _, char := range password {
|
||||
charItem, ok := charMap[char]
|
||||
|
|
|
@ -474,6 +474,7 @@
|
|||
"register_head":"Create Account",
|
||||
"register_account_name":"Account Name",
|
||||
"register_account_email":"Email",
|
||||
"register_account_email_optional":"Email (optional)",
|
||||
"register_account_password":"Password",
|
||||
"register_account_confirm_password":"Confirm Password",
|
||||
"register_account_anti_spam":"Are you a spambot?",
|
||||
|
@ -515,6 +516,7 @@
|
|||
"account_email_secondary":"Secondary",
|
||||
"account_email_verified":"Verified",
|
||||
"account_email_resend_email":"Resend Verification Email",
|
||||
"account_email_none":"No email addresses found.",
|
||||
|
||||
"account_password_head":"Edit Password",
|
||||
"account_password_current_password":"Current Password",
|
||||
|
|
|
@ -166,7 +166,7 @@ func AccountLoginMFAVerifySubmit(w http.ResponseWriter, r *http.Request, user c.
|
|||
if !mfaVerifySession(provSession, signedSession, uid) {
|
||||
return c.LocalError("Invalid session", w, r, user)
|
||||
}
|
||||
var token = r.PostFormValue("mfa_token")
|
||||
token := r.PostFormValue("mfa_token")
|
||||
|
||||
err = c.Auth.ValidateMFAToken(token, uid)
|
||||
if err != nil {
|
||||
|
@ -188,7 +188,7 @@ func AccountRegister(w http.ResponseWriter, r *http.Request, user c.User, h *c.H
|
|||
}
|
||||
h.Title = p.GetTitlePhrase("register")
|
||||
h.AddScriptAsync("register.js")
|
||||
return renderTemplate("register", w, r, h, c.Page{h, tList, nil})
|
||||
return renderTemplate("register", w, r, h, c.Page{h, tList, h.Settings["activation_type"] != 2})
|
||||
}
|
||||
|
||||
func isNumeric(data string) (numeric bool) {
|
||||
|
@ -227,19 +227,19 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User)
|
|||
}
|
||||
}
|
||||
|
||||
username := c.SanitiseSingleLine(r.PostFormValue("username"))
|
||||
// TODO: Add a dedicated function for validating emails
|
||||
email := c.SanitiseSingleLine(r.PostFormValue("email"))
|
||||
if username == "" {
|
||||
name := c.SanitiseSingleLine(r.PostFormValue("name"))
|
||||
if name == "" {
|
||||
regError(p.GetErrorPhrase("register_need_username"), "no-username")
|
||||
}
|
||||
if email == "" {
|
||||
// TODO: Add a dedicated function for validating emails
|
||||
email := c.SanitiseSingleLine(r.PostFormValue("email"))
|
||||
if headerLite.Settings["activation_type"] == 2 && email == "" {
|
||||
regError(p.GetErrorPhrase("register_need_email"), "no-email")
|
||||
}
|
||||
|
||||
// This is so a numeric name won't interfere with mentioning a user by ID, there might be a better way of doing this like perhaps !@ to mean IDs and @ to mean usernames in the pre-parser
|
||||
usernameBits := strings.Split(username, " ")
|
||||
if isNumeric(usernameBits[0]) {
|
||||
nameBits := strings.Split(name, " ")
|
||||
if isNumeric(nameBits[0]) {
|
||||
regError(p.GetErrorPhrase("register_first_word_numeric"), "numeric-name")
|
||||
}
|
||||
|
||||
|
@ -249,7 +249,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User)
|
|||
|
||||
password := r.PostFormValue("password")
|
||||
// ? Move this into Create()? What if we want to programatically set weak passwords for tests?
|
||||
err := c.WeakPassword(password, username, email)
|
||||
err := c.WeakPassword(password, name, email)
|
||||
if err != nil {
|
||||
regError(err.Error(), "weak-password")
|
||||
} else {
|
||||
|
@ -260,7 +260,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User)
|
|||
}
|
||||
}
|
||||
|
||||
regLog := c.RegLogItem{Username: username, Email: email, FailureReason: regErrReason, Success: regSuccess, IP: user.LastIP}
|
||||
regLog := c.RegLogItem{Username: name, Email: email, FailureReason: regErrReason, Success: regSuccess, IP: user.LastIP}
|
||||
_, err = regLog.Create()
|
||||
if err != nil {
|
||||
return c.InternalError(err, w, r)
|
||||
|
@ -280,7 +280,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User)
|
|||
}
|
||||
|
||||
// TODO: Do the registration attempt logging a little less messily (without having to amend the result after the insert)
|
||||
uid, err := c.Users.Create(username, password, email, group, active)
|
||||
uid, err := c.Users.Create(name, password, email, group, active)
|
||||
if err != nil {
|
||||
regLog.Success = false
|
||||
if err == c.ErrAccountExists {
|
||||
|
@ -325,7 +325,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User)
|
|||
return c.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
err = c.SendValidationEmail(username, email, token)
|
||||
err = c.SendValidationEmail(name, email, token)
|
||||
if err != nil {
|
||||
return c.LocalError(p.GetErrorPhrase("register_email_fail"), w, r, user)
|
||||
}
|
||||
|
@ -345,7 +345,6 @@ func accountEditHead(titlePhrase string, w http.ResponseWriter, r *http.Request,
|
|||
|
||||
func AccountEdit(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError {
|
||||
accountEditHead("account", w, r, &user, header)
|
||||
|
||||
if r.FormValue("avatar_updated") == "1" {
|
||||
header.AddNotice("account_avatar_updated")
|
||||
} else if r.FormValue("username_updated") == "1" {
|
||||
|
@ -387,9 +386,9 @@ func AccountEditPasswordSubmit(w http.ResponseWriter, r *http.Request, user c.Us
|
|||
}
|
||||
|
||||
var realPassword, salt string
|
||||
currentPassword := r.PostFormValue("account-current-password")
|
||||
newPassword := r.PostFormValue("account-new-password")
|
||||
confirmPassword := r.PostFormValue("account-confirm-password")
|
||||
currentPassword := r.PostFormValue("current-password")
|
||||
newPassword := r.PostFormValue("new-password")
|
||||
confirmPassword := r.PostFormValue("confirm-password")
|
||||
|
||||
// TODO: Use a reusable statement
|
||||
err := qgen.NewAcc().Select("users").Columns("password,salt").Where("uid=?").QueryRow(user.ID).Scan(&realPassword, &salt)
|
||||
|
@ -429,7 +428,6 @@ func AccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c.User
|
|||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
ferr = c.ChangeAvatar("." + ext, w, r, user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
|
@ -584,7 +582,7 @@ func AccountEditEmail(w http.ResponseWriter, r *http.Request, user c.User, h *c.
|
|||
|
||||
// Was this site migrated from another forum software? Most of them don't have multiple emails for a single user.
|
||||
// This also applies when the admin switches site.EnableEmails on after having it off for a while.
|
||||
if len(emails) == 0 {
|
||||
if len(emails) == 0 && user.Email != "" {
|
||||
emails = append(emails, c.Email{UserID: user.ID, Email: user.Email, Validated: false, Primary: true})
|
||||
}
|
||||
|
||||
|
@ -612,7 +610,10 @@ func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user c.
|
|||
|
||||
targetEmail := c.Email{UserID: user.ID}
|
||||
emails, err := c.Emails.GetEmailsByUser(&user)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return c.LocalError("A verification email was never sent for you!", w, r, user)
|
||||
} else if err != nil {
|
||||
// TODO: Better error if we don't have an email or it's not in the emails table for some reason
|
||||
return c.LocalError("You are not logged in", w, r, user)
|
||||
}
|
||||
for _, email := range emails {
|
||||
|
@ -635,8 +636,7 @@ func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user c.
|
|||
|
||||
// If Email Activation is on, then activate the account while we're here
|
||||
if header.Settings["activation_type"] == 2 {
|
||||
err = user.Activate()
|
||||
if err != nil {
|
||||
if err = user.Activate(); err != nil {
|
||||
return c.InternalError(err, w, r)
|
||||
}
|
||||
}
|
||||
|
@ -646,10 +646,9 @@ func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user c.
|
|||
|
||||
func AccountLogins(w http.ResponseWriter, r *http.Request, user c.User, h *c.Header) c.RouteError {
|
||||
accountEditHead("account_logins", w, r, &user, h)
|
||||
logCount := c.LoginLogs.CountUser(user.ID)
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
perPage := 12
|
||||
offset, page, lastPage := c.PageOffset(logCount, page, perPage)
|
||||
offset, page, lastPage := c.PageOffset(c.LoginLogs.CountUser(user.ID), page, perPage)
|
||||
|
||||
logs, err := c.LoginLogs.GetOffset(user.ID, offset, perPage)
|
||||
if err != nil {
|
||||
|
@ -779,12 +778,11 @@ func AccountPasswordResetToken(w http.ResponseWriter, r *http.Request, user c.Us
|
|||
header.AddNotice("password_reset_token_token_verified")
|
||||
}*/
|
||||
|
||||
token := r.FormValue("token")
|
||||
uid, err := strconv.Atoi(r.FormValue("uid"))
|
||||
if err != nil {
|
||||
return c.LocalError("Invalid uid", w, r, user)
|
||||
}
|
||||
|
||||
token := r.FormValue("token")
|
||||
err = c.PasswordResetter.ValidateToken(uid, token)
|
||||
if err == sql.ErrNoRows || err == c.ErrBadResetToken {
|
||||
return c.LocalError("This reset token has expired.", w, r, user)
|
||||
|
@ -814,8 +812,7 @@ func AccountPasswordResetTokenSubmit(w http.ResponseWriter, r *http.Request, use
|
|||
return c.LocalError("This reset token has expired.", w, r, user)
|
||||
}
|
||||
|
||||
token := r.FormValue("token")
|
||||
err = c.PasswordResetter.ValidateToken(uid, token)
|
||||
err = c.PasswordResetter.ValidateToken(uid, r.FormValue("token"))
|
||||
if err == sql.ErrNoRows || err == c.ErrBadResetToken {
|
||||
return c.LocalError("This reset token has expired.", w, r, user)
|
||||
} else if err != nil {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<div class="rowitem"><h1>{{lang "account_email_head"}}</h1></div>
|
||||
</div>
|
||||
<div class="colstack_item rowlist">
|
||||
<!-- TODO: Do we need this inline CSS? -->
|
||||
{{range .ItemList}}
|
||||
<div class="rowitem">
|
||||
<a>{{.Email}}</a>
|
||||
|
@ -11,5 +10,5 @@
|
|||
{{if .Validated}}<span class="username validated_email">{{lang "account_email_verified"}}</span>{{else}}<a class="username invalid_email">{{lang "account_email_resend_email"}}</a>{{end}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}<div class="rowitem passive rowmsg">{{lang "account_email_none"}}</div>{{end}}
|
||||
</div>
|
|
@ -9,15 +9,15 @@
|
|||
<form action="/user/edit/password/submit/?s={{.CurrentUser.Session}}" method="post">
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem formlabel"><a>{{lang "account_password_current_password"}}</a></div>
|
||||
<div class="formitem"><input name="account-current-password" type="password" placeholder="*****" autocomplete="current-password" required /></div>
|
||||
<div class="formitem"><input name="current-password" type="password" placeholder="*****" autocomplete="current-password" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "account_password_new_password"}}</a></div>
|
||||
<div class="formitem"><input name="account-new-password" type="password" placeholder="*****" autocomplete="new-password" required /></div>
|
||||
<div class="formitem"><input name="new-password" type="password" placeholder="*****" autocomplete="new-password" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "account_password_confirm_password"}}</a></div>
|
||||
<div class="formitem"><input name="account-confirm-password" type="password" placeholder="*****" autocomplete="new-password" required /></div>
|
||||
<div class="formitem"><input name="confirm-password" type="password" placeholder="*****" autocomplete="new-password" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="account-button" class="formbutton form_middle_button">{{lang "account_password_update_button"}}</button></div>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<span>
|
||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
||||
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎{{end}}</span>
|
||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="{{lang "status.closed_tooltip"}}"> | 🔒︎{{end}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow" style="background-image:url({{.LastUser.Avatar}});background-position:left;background-repeat:no-repeat;background-size:64px;padding-left:72px;">
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
<div class="rowblock the_form">
|
||||
<form action="/accounts/create/submit/" method="post">
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="username_label">{{lang "register_account_name"}}</a></div>
|
||||
<div class="formitem"><input name="username" type="text" placeholder="{{lang "register_account_name"}}" aria-labelledby="username_label" required /></div>
|
||||
<div class="formitem formlabel"><a id="name_label">{{lang "register_account_name"}}</a></div>
|
||||
<div class="formitem"><input name="name" type="text" placeholder="{{lang "register_account_name"}}" aria-labelledby="name_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="email_label">{{lang "register_account_email"}}</a></div>
|
||||
<div class="formitem"><input name="email" type="email" placeholder="joe.doe@example.com" aria-labelledby="email_label" required /></div>
|
||||
<div class="formitem formlabel"><a id="email_label">{{if not .Something}}{{lang "register_account_email"}}{{else}}{{lang "register_account_email_optional"}}{{end}}</a></div>
|
||||
<div class="formitem"><input name="email" type="email" placeholder="joe.doe@example.com" aria-labelledby="email_label"{{if not .Something}} required{{end}} /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="password_label">{{lang "register_account_password"}}</a></div>
|
||||
|
|
Loading…
Reference in New Issue