Use a “proper” struct for the login form
Similar to the profile form, the login form now parses and validates itself, with the InputField structs that the templates expect. I realized that i was doing more work than necessary when parsing fields fro the profile form because i was repeating the operation and the field name, so now it is a function of InputField. This time i needed extra attributes for the login form. I am not sure that the Go source code needs to know about HTML attributes, but it was the easiest way to pass them to the template.
This commit is contained in:
parent
75fd12bf1c
commit
ff5b76b4f5
15
pkg/form.go
15
pkg/form.go
|
@ -2,18 +2,29 @@ package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Attribute struct {
|
||||||
|
Key, Val string
|
||||||
|
}
|
||||||
|
|
||||||
type InputField struct {
|
type InputField struct {
|
||||||
Name string
|
Name string
|
||||||
Label string
|
Label string
|
||||||
Type string
|
Type string
|
||||||
Value string
|
Value string
|
||||||
Required bool
|
Required bool
|
||||||
|
Attributes []*Attribute
|
||||||
Errors []error
|
Errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (field *InputField) FillValue(r *http.Request) {
|
||||||
|
field.Value = strings.TrimSpace(r.FormValue(field.Name))
|
||||||
|
}
|
||||||
|
|
||||||
func (field *InputField) Equals(other *InputField) bool {
|
func (field *InputField) Equals(other *InputField) bool {
|
||||||
return field.Value == other.Value
|
return field.Value == other.Value
|
||||||
}
|
}
|
||||||
|
@ -40,6 +51,10 @@ type SelectField struct {
|
||||||
Errors []error
|
Errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (field *SelectField) FillValue(r *http.Request) {
|
||||||
|
field.Selected = r.FormValue(field.Name)
|
||||||
|
}
|
||||||
|
|
||||||
func (field *SelectField) HasValidOption() bool {
|
func (field *SelectField) HasValidOption() bool {
|
||||||
for _, option := range field.Options {
|
for _, option := range field.Options {
|
||||||
if option.Value == field.Selected {
|
if option.Value == field.Selected {
|
||||||
|
|
96
pkg/login.go
96
pkg/login.go
|
@ -2,6 +2,7 @@ package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -17,17 +18,66 @@ const (
|
||||||
defaultRole = "guest"
|
defaultRole = "guest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginPage struct {
|
type loginForm struct {
|
||||||
LoginError bool
|
locale *Locale
|
||||||
Email string
|
Errors []error
|
||||||
Password string
|
Email *InputField
|
||||||
|
Password *InputField
|
||||||
|
Valid bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppUser struct {
|
func newLoginForm(locale *Locale) *loginForm {
|
||||||
Email string
|
return &loginForm{
|
||||||
LoggedIn bool
|
locale: locale,
|
||||||
Role string
|
Email: &InputField{
|
||||||
Language language.Tag
|
Name: "email",
|
||||||
|
Label: pgettext("input", "Email", locale),
|
||||||
|
Type: "email",
|
||||||
|
Required: true,
|
||||||
|
Attributes: []*Attribute{
|
||||||
|
{"autofocus", "autofocus"},
|
||||||
|
{"autocomplete", "username"},
|
||||||
|
{"autocapitalize", "none"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Password: &InputField{
|
||||||
|
Name: "password",
|
||||||
|
Label: pgettext("input", "Password", locale),
|
||||||
|
Type: "password",
|
||||||
|
Required: true,
|
||||||
|
Attributes: []*Attribute{
|
||||||
|
{"autocomplete", "current-password"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (form *loginForm) Parse(r *http.Request) error {
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
form.Email.FillValue(r)
|
||||||
|
form.Password.FillValue(r)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (form *loginForm) Validate() bool {
|
||||||
|
form.Valid = true
|
||||||
|
if form.Email.IsEmpty() {
|
||||||
|
form.AppendInputError(form.Email, errors.New(gettext("Email can not be empty.", form.locale)))
|
||||||
|
} else if !form.Email.HasValidEmail() {
|
||||||
|
form.AppendInputError(form.Email, errors.New(gettext("This value is not a valid email. It should be like name@domain.com.", form.locale)))
|
||||||
|
}
|
||||||
|
if form.Password.IsEmpty() {
|
||||||
|
form.AppendInputError(form.Password, errors.New(gettext("Password can not be empty.", form.locale)))
|
||||||
|
}
|
||||||
|
return form.Valid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (form *loginForm) AppendInputError(field *InputField, err error) {
|
||||||
|
field.Errors = append(field.Errors, err)
|
||||||
|
form.Valid = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoginHandler() http.Handler {
|
func LoginHandler() http.Handler {
|
||||||
|
@ -37,25 +87,28 @@ func LoginHandler() http.Handler {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.ParseForm()
|
locale := getLocale(r)
|
||||||
page := LoginPage{
|
form := newLoginForm(locale)
|
||||||
Email: r.FormValue("email"),
|
|
||||||
Password: r.FormValue("password"),
|
|
||||||
}
|
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
|
if err := form.Parse(r); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if form.Validate() {
|
||||||
conn := getConn(r)
|
conn := getConn(r)
|
||||||
cookie := conn.MustGetText(r.Context(), "", "select login($1, $2, $3)", page.Email, page.Password, remoteAddr(r))
|
cookie := conn.MustGetText(r.Context(), "", "select login($1, $2, $3)", form.Email.Value, form.Password.Value, remoteAddr(r))
|
||||||
if cookie != "" {
|
if cookie != "" {
|
||||||
setSessionCookie(w, cookie)
|
setSessionCookie(w, cookie)
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
form.Errors = append(form.Errors, errors.New(gettext("Invalid user or password.", locale)))
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
page.LoginError = true
|
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||||
}
|
}
|
||||||
mustRenderWebTemplate(w, r, "login.gohtml", page)
|
}
|
||||||
|
mustRenderWebTemplate(w, r, "login.gohtml", form)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +144,13 @@ func createSessionCookie(value string, duration time.Duration) *http.Cookie {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppUser struct {
|
||||||
|
Email string
|
||||||
|
LoggedIn bool
|
||||||
|
Role string
|
||||||
|
Language language.Tag
|
||||||
|
}
|
||||||
|
|
||||||
func CheckLogin(db *Db, next http.Handler) http.Handler {
|
func CheckLogin(db *Db, next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var ctx = r.Context()
|
var ctx = r.Context()
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LanguageOption struct {
|
type LanguageOption struct {
|
||||||
|
@ -13,6 +12,7 @@ type LanguageOption struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type profileForm struct {
|
type profileForm struct {
|
||||||
|
locale *Locale
|
||||||
Name *InputField
|
Name *InputField
|
||||||
Email *InputField
|
Email *InputField
|
||||||
Password *InputField
|
Password *InputField
|
||||||
|
@ -25,6 +25,7 @@ func newProfileForm(ctx context.Context, conn *Conn, locale *Locale) *profileFor
|
||||||
automaticOption := pgettext("language option", "Automatic", locale)
|
automaticOption := pgettext("language option", "Automatic", locale)
|
||||||
languages := MustGetOptions(ctx, conn, "select 'und', $1 union all select lang_tag, endonym from language where selectable", automaticOption)
|
languages := MustGetOptions(ctx, conn, "select 'und', $1 union all select lang_tag, endonym from language where selectable", automaticOption)
|
||||||
return &profileForm{
|
return &profileForm{
|
||||||
|
locale: locale,
|
||||||
Name: &InputField{
|
Name: &InputField{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Label: pgettext("input", "User name", locale),
|
Label: pgettext("input", "User name", locale),
|
||||||
|
@ -59,29 +60,29 @@ func (form *profileForm) Parse(r *http.Request) error {
|
||||||
if err := r.ParseForm(); err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
form.Email.Value = strings.TrimSpace(r.FormValue("email"))
|
form.Email.FillValue(r)
|
||||||
form.Name.Value = strings.TrimSpace(r.FormValue("name"))
|
form.Name.FillValue(r)
|
||||||
form.Password.Value = strings.TrimSpace(r.FormValue("password"))
|
form.Password.FillValue(r)
|
||||||
form.PasswordConfirm.Value = strings.TrimSpace(r.FormValue("password_confirm"))
|
form.PasswordConfirm.FillValue(r)
|
||||||
form.Language.Selected = r.FormValue("language")
|
form.Language.FillValue(r)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (form *profileForm) Validate(locale *Locale) bool {
|
func (form *profileForm) Validate() bool {
|
||||||
form.Valid = true
|
form.Valid = true
|
||||||
if form.Email.IsEmpty() {
|
if form.Email.IsEmpty() {
|
||||||
form.AppendInputError(form.Email, errors.New(gettext("Email can not be empty.", locale)))
|
form.AppendInputError(form.Email, errors.New(gettext("Email can not be empty.", form.locale)))
|
||||||
} else if !form.Email.HasValidEmail() {
|
} else if !form.Email.HasValidEmail() {
|
||||||
form.AppendInputError(form.Email, errors.New(gettext("This value is not a valid email. It should be like name@domain.com.", locale)))
|
form.AppendInputError(form.Email, errors.New(gettext("This value is not a valid email. It should be like name@domain.com.", form.locale)))
|
||||||
}
|
}
|
||||||
if form.Name.IsEmpty() {
|
if form.Name.IsEmpty() {
|
||||||
form.AppendInputError(form.Name, errors.New(gettext("Name can not be empty.", locale)))
|
form.AppendInputError(form.Name, errors.New(gettext("Name can not be empty.", form.locale)))
|
||||||
}
|
}
|
||||||
if !form.PasswordConfirm.Equals(form.Password) {
|
if !form.PasswordConfirm.Equals(form.Password) {
|
||||||
form.AppendInputError(form.PasswordConfirm, errors.New(gettext("Confirmation does not match password.", locale)))
|
form.AppendInputError(form.PasswordConfirm, errors.New(gettext("Confirmation does not match password.", form.locale)))
|
||||||
}
|
}
|
||||||
if !form.Language.HasValidOption() {
|
if !form.Language.HasValidOption() {
|
||||||
form.AppendSelectError(form.Language, errors.New(gettext("Selected language is not valid.", locale)))
|
form.AppendSelectError(form.Language, errors.New(gettext("Selected language is not valid.", form.locale)))
|
||||||
}
|
}
|
||||||
return form.Valid
|
return form.Valid
|
||||||
}
|
}
|
||||||
|
@ -107,7 +108,7 @@ func ProfileHandler() http.Handler {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ok := form.Validate(locale); ok {
|
if ok := form.Validate(); ok {
|
||||||
//goland:noinspection SqlWithoutWhere
|
//goland:noinspection SqlWithoutWhere
|
||||||
cookie := conn.MustGetText(r.Context(), "", "update user_profile set name = $1, email = $2, lang_tag = $3 returning build_cookie()", form.Name.Value, form.Email.Value, form.Language.Selected)
|
cookie := conn.MustGetText(r.Context(), "", "update user_profile set name = $1, email = $2, lang_tag = $3 returning build_cookie()", form.Name.Value, form.Email.Value, form.Language.Selected)
|
||||||
setSessionCookie(w, cookie)
|
setSessionCookie(w, cookie)
|
||||||
|
|
68
po/ca.po
68
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: numerus\n"
|
"Project-Id-Version: numerus\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-02-01 10:11+0100\n"
|
"POT-Creation-Date: 2023-02-01 10:52+0100\n"
|
||||||
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||||
|
@ -47,27 +47,12 @@ msgctxt "nav"
|
||||||
msgid "Contacts"
|
msgid "Contacts"
|
||||||
msgstr "Contactes"
|
msgstr "Contactes"
|
||||||
|
|
||||||
#: web/template/login.gohtml:2 web/template/login.gohtml:13
|
#: web/template/login.gohtml:2 web/template/login.gohtml:15
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Entrada"
|
msgstr "Entrada"
|
||||||
|
|
||||||
#: web/template/login.gohtml:9
|
#: web/template/login.gohtml:19
|
||||||
msgid "Invalid user or password"
|
|
||||||
msgstr "Nom d’usuari o contrasenya incorrectes"
|
|
||||||
|
|
||||||
#: web/template/login.gohtml:17 web/template/tax-details.gohtml:27
|
|
||||||
#: web/template/contacts-new.gohtml:27 pkg/profile.go:36
|
|
||||||
msgctxt "input"
|
|
||||||
msgid "Email"
|
|
||||||
msgstr "Correu-e"
|
|
||||||
|
|
||||||
#: web/template/login.gohtml:22 pkg/profile.go:42
|
|
||||||
msgctxt "input"
|
|
||||||
msgid "Password"
|
|
||||||
msgstr "Contrasenya"
|
|
||||||
|
|
||||||
#: web/template/login.gohtml:25
|
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Entra"
|
msgstr "Entra"
|
||||||
|
@ -156,6 +141,12 @@ msgctxt "input"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Telèfon"
|
msgstr "Telèfon"
|
||||||
|
|
||||||
|
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
|
||||||
|
#: pkg/login.go:34 pkg/profile.go:37
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "Correu-e"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Web"
|
msgid "Web"
|
||||||
|
@ -230,43 +221,56 @@ msgctxt "title"
|
||||||
msgid "New Contact"
|
msgid "New Contact"
|
||||||
msgstr "Nou contacte"
|
msgstr "Nou contacte"
|
||||||
|
|
||||||
|
#: pkg/login.go:45 pkg/profile.go:43
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Password"
|
||||||
|
msgstr "Contrasenya"
|
||||||
|
|
||||||
|
#: pkg/login.go:68 pkg/profile.go:74
|
||||||
|
msgid "Email can not be empty."
|
||||||
|
msgstr "No podeu deixar el correu-e en blanc."
|
||||||
|
|
||||||
|
#: pkg/login.go:70 pkg/profile.go:76
|
||||||
|
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||||
|
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
|
||||||
|
|
||||||
|
#: pkg/login.go:73
|
||||||
|
msgid "Password can not be empty."
|
||||||
|
msgstr "No podeu deixar la contrasenya en blanc."
|
||||||
|
|
||||||
|
#: pkg/login.go:105
|
||||||
|
msgid "Invalid user or password."
|
||||||
|
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||||
|
|
||||||
#: pkg/profile.go:25
|
#: pkg/profile.go:25
|
||||||
msgctxt "language option"
|
msgctxt "language option"
|
||||||
msgid "Automatic"
|
msgid "Automatic"
|
||||||
msgstr "Automàtic"
|
msgstr "Automàtic"
|
||||||
|
|
||||||
#: pkg/profile.go:30
|
#: pkg/profile.go:31
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "User name"
|
msgid "User name"
|
||||||
msgstr "Nom d’usuari"
|
msgstr "Nom d’usuari"
|
||||||
|
|
||||||
#: pkg/profile.go:47
|
#: pkg/profile.go:48
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Password Confirmation"
|
msgid "Password Confirmation"
|
||||||
msgstr "Confirmació contrasenya"
|
msgstr "Confirmació contrasenya"
|
||||||
|
|
||||||
#: pkg/profile.go:52
|
#: pkg/profile.go:53
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Idioma"
|
msgstr "Idioma"
|
||||||
|
|
||||||
#: pkg/profile.go:73
|
#: pkg/profile.go:79
|
||||||
msgid "Email can not be empty."
|
|
||||||
msgstr "No podeu deixar el correu-e en blanc."
|
|
||||||
|
|
||||||
#: pkg/profile.go:75
|
|
||||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
|
||||||
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
|
|
||||||
|
|
||||||
#: pkg/profile.go:78
|
|
||||||
msgid "Name can not be empty."
|
msgid "Name can not be empty."
|
||||||
msgstr "No podeu deixar el nom en blanc."
|
msgstr "No podeu deixar el nom en blanc."
|
||||||
|
|
||||||
#: pkg/profile.go:81
|
#: pkg/profile.go:82
|
||||||
msgid "Confirmation does not match password."
|
msgid "Confirmation does not match password."
|
||||||
msgstr "La confirmació no és igual a la contrasenya."
|
msgstr "La confirmació no és igual a la contrasenya."
|
||||||
|
|
||||||
#: pkg/profile.go:84
|
#: pkg/profile.go:85
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "Heu seleccionat un idioma que no és vàlid."
|
msgstr "Heu seleccionat un idioma que no és vàlid."
|
||||||
|
|
||||||
|
|
68
po/es.po
68
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: numerus\n"
|
"Project-Id-Version: numerus\n"
|
||||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||||
"POT-Creation-Date: 2023-02-01 10:11+0100\n"
|
"POT-Creation-Date: 2023-02-01 10:52+0100\n"
|
||||||
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
||||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||||
|
@ -47,27 +47,12 @@ msgctxt "nav"
|
||||||
msgid "Contacts"
|
msgid "Contacts"
|
||||||
msgstr "Contactos"
|
msgstr "Contactos"
|
||||||
|
|
||||||
#: web/template/login.gohtml:2 web/template/login.gohtml:13
|
#: web/template/login.gohtml:2 web/template/login.gohtml:15
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Entrada"
|
msgstr "Entrada"
|
||||||
|
|
||||||
#: web/template/login.gohtml:9
|
#: web/template/login.gohtml:19
|
||||||
msgid "Invalid user or password"
|
|
||||||
msgstr "Nombre de usuario o contraseña inválido"
|
|
||||||
|
|
||||||
#: web/template/login.gohtml:17 web/template/tax-details.gohtml:27
|
|
||||||
#: web/template/contacts-new.gohtml:27 pkg/profile.go:36
|
|
||||||
msgctxt "input"
|
|
||||||
msgid "Email"
|
|
||||||
msgstr "Correo-e"
|
|
||||||
|
|
||||||
#: web/template/login.gohtml:22 pkg/profile.go:42
|
|
||||||
msgctxt "input"
|
|
||||||
msgid "Password"
|
|
||||||
msgstr "Contraseña"
|
|
||||||
|
|
||||||
#: web/template/login.gohtml:25
|
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Entrar"
|
msgstr "Entrar"
|
||||||
|
@ -156,6 +141,12 @@ msgctxt "input"
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "Teléfono"
|
msgstr "Teléfono"
|
||||||
|
|
||||||
|
#: web/template/tax-details.gohtml:27 web/template/contacts-new.gohtml:27
|
||||||
|
#: pkg/login.go:34 pkg/profile.go:37
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "Correo-e"
|
||||||
|
|
||||||
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
#: web/template/tax-details.gohtml:31 web/template/contacts-new.gohtml:31
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Web"
|
msgid "Web"
|
||||||
|
@ -230,43 +221,56 @@ msgctxt "title"
|
||||||
msgid "New Contact"
|
msgid "New Contact"
|
||||||
msgstr "Nuevo contacto"
|
msgstr "Nuevo contacto"
|
||||||
|
|
||||||
|
#: pkg/login.go:45 pkg/profile.go:43
|
||||||
|
msgctxt "input"
|
||||||
|
msgid "Password"
|
||||||
|
msgstr "Contraseña"
|
||||||
|
|
||||||
|
#: pkg/login.go:68 pkg/profile.go:74
|
||||||
|
msgid "Email can not be empty."
|
||||||
|
msgstr "No podéis dejar el correo-e en blanco."
|
||||||
|
|
||||||
|
#: pkg/login.go:70 pkg/profile.go:76
|
||||||
|
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||||
|
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
|
||||||
|
|
||||||
|
#: pkg/login.go:73
|
||||||
|
msgid "Password can not be empty."
|
||||||
|
msgstr "No podéis dejar la contaseña en blanco."
|
||||||
|
|
||||||
|
#: pkg/login.go:105
|
||||||
|
msgid "Invalid user or password."
|
||||||
|
msgstr "Nombre de usuario o contraseña inválido."
|
||||||
|
|
||||||
#: pkg/profile.go:25
|
#: pkg/profile.go:25
|
||||||
msgctxt "language option"
|
msgctxt "language option"
|
||||||
msgid "Automatic"
|
msgid "Automatic"
|
||||||
msgstr "Automático"
|
msgstr "Automático"
|
||||||
|
|
||||||
#: pkg/profile.go:30
|
#: pkg/profile.go:31
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "User name"
|
msgid "User name"
|
||||||
msgstr "Nombre de usuario"
|
msgstr "Nombre de usuario"
|
||||||
|
|
||||||
#: pkg/profile.go:47
|
#: pkg/profile.go:48
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Password Confirmation"
|
msgid "Password Confirmation"
|
||||||
msgstr "Confirmación contrasenya"
|
msgstr "Confirmación contrasenya"
|
||||||
|
|
||||||
#: pkg/profile.go:52
|
#: pkg/profile.go:53
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Language"
|
msgid "Language"
|
||||||
msgstr "Idioma"
|
msgstr "Idioma"
|
||||||
|
|
||||||
#: pkg/profile.go:73
|
#: pkg/profile.go:79
|
||||||
msgid "Email can not be empty."
|
|
||||||
msgstr "No podéis dejar el correo-e en blanco."
|
|
||||||
|
|
||||||
#: pkg/profile.go:75
|
|
||||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
|
||||||
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
|
|
||||||
|
|
||||||
#: pkg/profile.go:78
|
|
||||||
msgid "Name can not be empty."
|
msgid "Name can not be empty."
|
||||||
msgstr "No podéis dejar el nombre en blanco."
|
msgstr "No podéis dejar el nombre en blanco."
|
||||||
|
|
||||||
#: pkg/profile.go:81
|
#: pkg/profile.go:82
|
||||||
msgid "Confirmation does not match password."
|
msgid "Confirmation does not match password."
|
||||||
msgstr "La confirmación no corresponde con la contraseña."
|
msgstr "La confirmación no corresponde con la contraseña."
|
||||||
|
|
||||||
#: pkg/profile.go:84
|
#: pkg/profile.go:85
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "Habéis escogido un idioma que no es válido."
|
msgstr "Habéis escogido un idioma que no es válido."
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
{{ define "input-field" -}}
|
{{ define "input-field" -}}
|
||||||
<div class="input {{ if .Errors }}has-errors{{ end }}">
|
<div class="input {{ if .Errors }}has-errors{{ end }}">
|
||||||
<input type="{{ .Type }}" name="{{ .Name }}" id="{{ .Name }}-field"
|
<input type="{{ .Type }}" name="{{ .Name }}" id="{{ .Name }}-field"
|
||||||
|
{{- range $attribute := .Attributes }} {{$attribute.Key}}="{{$attribute.Val}}" {{ end -}}
|
||||||
{{ if .Required }}required="required"{{ end }} value="{{ .Value }}" placeholder="{{ .Label }}">
|
{{ if .Required }}required="required"{{ end }} value="{{ .Value }}" placeholder="{{ .Label }}">
|
||||||
<label for="{{ .Name }}-field">{{ .Label }}</label>
|
<label for="{{ .Name }}-field">{{ .Label }}</label>
|
||||||
{{ if .Errors }}
|
{{- if .Errors }}
|
||||||
<ul>
|
<ul>
|
||||||
{{- range $error := .Errors }}
|
{{- range $error := .Errors }}
|
||||||
<li>{{ . }}</li>
|
<li>{{ . }}</li>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ end }}
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
|
|
@ -4,24 +4,18 @@
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<h1><img src="/static/numerus.svg" alt="Numerus" width="620" height="77"></h1>
|
<h1><img src="/static/numerus.svg" alt="Numerus" width="620" height="77"></h1>
|
||||||
{{ if .LoginError -}}
|
{{ if .Errors -}}
|
||||||
<div class="error" role="alert">
|
<div class="error" role="alert">
|
||||||
<p>{{( gettext "Invalid user or password" )}}</p>
|
{{ range $error := .Errors -}}
|
||||||
|
<p>{{ $error }}</p>
|
||||||
|
{{- end }}
|
||||||
</div>
|
</div>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
<section id="login">
|
<section id="login">
|
||||||
<h2>{{( pgettext "Login" "title" )}}</h2>
|
<h2>{{( pgettext "Login" "title" )}}</h2>
|
||||||
<form method="POST" action="/login">
|
<form method="POST" action="/login">
|
||||||
<div class="input">
|
{{ template "input-field" .Email }}
|
||||||
<input id="user_email" type="email" required autofocus name="email" autocapitalize="none" value="{{ .Email }}" placeholder="{{( pgettext "Email" "input" )}}">
|
{{ template "input-field" .Password }}
|
||||||
<label for="user_email">{{( pgettext "Email" "input" )}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input">
|
|
||||||
<input id="user_password" type="password" required name="password" autocomplete="current-password" value="{{ .Password }}" placeholder="{{( pgettext "Password" "input" )}}">
|
|
||||||
<label for="user_password">{{( pgettext "Password" "input" )}}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit">{{( pgettext "Login" "action" )}}</button>
|
<button type="submit">{{( pgettext "Login" "action" )}}</button>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
Loading…
Reference in New Issue