From e128680e9a835bce353da8f669ccd0fd341649a5 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Sat, 5 Aug 2023 03:42:37 +0200 Subject: [PATCH] Split templates and handlers into admin and public MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I need to check that the user is an employee (or admin) in administration handlers, but i do not want to do it for each handler, because i am bound to forget it. Thus, i added the /admin sub-path for these resources. The public-facing web is the rest of the resources outside /admin, but for now there is only home, to test whether it works as expected or not. The public-facing web can not relay on the user’s language settings, as the guest user has no way to set that. I would be happy to just use the Accept-Language header for that, but apparently Google does not use that header[0], and they give four alternatives: a country-specific domain, a subdomain with a generic top-level domain (gTLD), subdirectories with a gTLD, or URL parameters (e.g., site.com?loc=de). Of the four, Google does not recommend URL parameters, and the customer is already using subdirectories with the current site, therefor that’s what i have chosen. Google also tells me that it is a very good idea to have links between localized version of the same resources, either with elements, Link HTTP response headers, or a sitemap file[1]; they are all equivalent in the eyes of Google. I have choosen the Link response headers way, because for that i can simply “augment” ResponseHeader to automatically add these headers when the response status is 2xx, otherwise i would need to pass down the original URL path until it reaches the template. Even though Camper is supposed to be a “generic”, multi-company application, i think i will stick to the easiest route and write the templates for just the “first” customer. [0]: https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites [1]: https://developers.google.com/search/docs/specialty/international/localized-versions --- pkg/app/admin.go | 59 +++++++++++++ pkg/app/app.go | 53 ++++++----- pkg/app/login.go | 4 +- pkg/app/public.go | 34 +++++++ pkg/app/user.go | 10 ++- pkg/auth/user.go | 5 ++ pkg/campsite/type.go | 4 +- pkg/http/links.go | 49 +++++++++++ pkg/template/render.go | 19 ++-- po/ca.po | 88 +++++++++++-------- po/es.po | 88 +++++++++++-------- web/static/public.css | 41 +++++++++ .../{ => admin}/campsite/type/index.gohtml | 4 +- .../{ => admin}/campsite/type/new.gohtml | 2 +- web/templates/{ => admin}/dashboard.gohtml | 0 web/templates/{ => admin}/form.gohtml | 0 web/templates/{ => admin}/htmx.gohtml | 0 web/templates/{ => admin}/layout.gohtml | 2 +- web/templates/{ => admin}/login.gohtml | 0 web/templates/{ => admin}/profile.gohtml | 0 web/templates/public/form.gohtml | 1 + web/templates/public/home.gohtml | 10 +++ web/templates/public/layout.gohtml | 23 +++++ 23 files changed, 379 insertions(+), 117 deletions(-) create mode 100644 pkg/app/admin.go create mode 100644 pkg/app/public.go create mode 100644 pkg/http/links.go create mode 100644 web/static/public.css rename web/templates/{ => admin}/campsite/type/index.gohtml (82%) rename web/templates/{ => admin}/campsite/type/new.gohtml (96%) rename web/templates/{ => admin}/dashboard.gohtml (100%) rename web/templates/{ => admin}/form.gohtml (100%) rename web/templates/{ => admin}/htmx.gohtml (100%) rename web/templates/{ => admin}/layout.gohtml (95%) rename web/templates/{ => admin}/login.gohtml (100%) rename web/templates/{ => admin}/profile.gohtml (100%) create mode 120000 web/templates/public/form.gohtml create mode 100644 web/templates/public/home.gohtml create mode 100644 web/templates/public/layout.gohtml diff --git a/pkg/app/admin.go b/pkg/app/admin.go new file mode 100644 index 0000000..3dd2a92 --- /dev/null +++ b/pkg/app/admin.go @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023 jordi fita mas + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package app + +import ( + "net/http" + + "dev.tandem.ws/tandem/camper/pkg/auth" + "dev.tandem.ws/tandem/camper/pkg/campsite" + "dev.tandem.ws/tandem/camper/pkg/database" + httplib "dev.tandem.ws/tandem/camper/pkg/http" + "dev.tandem.ws/tandem/camper/pkg/template" +) + +type adminHandler struct { + campsite *campsite.Handler +} + +func newAdminHandler() *adminHandler { + return &adminHandler{ + campsite: campsite.NewHandler(), + } +} + +func (h *adminHandler) Handle(user *auth.User, company *auth.Company, conn *database.Conn, requestPath string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !user.LoggedIn { + w.WriteHeader(http.StatusUnauthorized) + serveLoginForm(w, r, user, company, requestPath) + return + } + + if !user.IsEmployee() { + http.Error(w, user.Locale.Gettext("Access forbidden"), http.StatusForbidden) + return + } + + var head string + head, r.URL.Path = httplib.ShiftPath(r.URL.Path) + switch head { + case "campsites": + h.campsite.Handler(user, company, conn).ServeHTTP(w, r) + case "": + switch r.Method { + case http.MethodGet: + serveDashboard(w, r, user, company) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet) + } + } + }) +} + +func serveDashboard(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { + template.MustRenderAdmin(w, r, user, company, "dashboard.gohtml", nil) +} diff --git a/pkg/app/app.go b/pkg/app/app.go index 228ca11..aa557df 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -11,18 +11,17 @@ import ( "golang.org/x/text/language" "dev.tandem.ws/tandem/camper/pkg/auth" - "dev.tandem.ws/tandem/camper/pkg/campsite" "dev.tandem.ws/tandem/camper/pkg/database" httplib "dev.tandem.ws/tandem/camper/pkg/http" "dev.tandem.ws/tandem/camper/pkg/locale" - "dev.tandem.ws/tandem/camper/pkg/template" ) type App struct { db *database.DB fileHandler http.Handler profile *profileHandler - campsite *campsite.Handler + admin *adminHandler + public *publicHandler locales locale.Locales defaultLocale *locale.Locale languageMatcher language.Matcher @@ -39,7 +38,8 @@ func New(db *database.DB, avatarsDir string) (http.Handler, error) { db: db, fileHandler: static, profile: profile, - campsite: campsite.NewHandler(), + admin: newAdminHandler(), + public: newPublicHandler(), locales: locales, defaultLocale: locales[language.Catalan], languageMatcher: language.NewMatcher(locales.Tags()), @@ -81,41 +81,38 @@ func (h *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { panic(err) } - if head == "login" { + switch head { + case "": + http.Redirect(w, r, "/"+user.Locale.Language.String()+"/", http.StatusFound) + case "admin": + h.admin.Handle(user, company, conn, requestPath).ServeHTTP(w, r) + case "login": switch r.Method { case http.MethodGet: - serveLoginForm(w, r, user, company, "/") + serveLoginForm(w, r, user, company, "/admin") case http.MethodPost: handleLogin(w, r, user, company, conn) default: httplib.MethodNotAllowed(w, r, http.MethodPost, http.MethodGet) } - } else { - if !user.LoggedIn { - w.WriteHeader(http.StatusUnauthorized) - serveLoginForm(w, r, user, company, requestPath) + case "me": + h.profile.Handler(user, company, conn, requestPath).ServeHTTP(w, r) + default: + langTag, err := language.Parse(head) + if err != nil { + http.NotFound(w, r) return } - - switch head { - case "me": - h.profile.Handler(user, company, conn).ServeHTTP(w, r) - case "campsites": - h.campsite.Handler(user, company, conn).ServeHTTP(w, r) - case "": - switch r.Method { - case http.MethodGet: - serveDashboard(w, r, user, company) - default: - httplib.MethodNotAllowed(w, r, http.MethodGet) - } - default: + urlLocale := h.locales[langTag] + if urlLocale == nil { http.NotFound(w, r) + return } + user.Locale = urlLocale + if r.Method == http.MethodGet || r.Method == http.MethodHead { + w = httplib.LanguageLinks(w, false, r.Host, r.URL.Path, h.locales) + } + h.public.Handler(user, company, conn).ServeHTTP(w, r) } } } - -func serveDashboard(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { - template.MustRender(w, r, user, company, "dashboard.gohtml", nil) -} diff --git a/pkg/app/login.go b/pkg/app/login.go index dd2a524..e874bcc 100644 --- a/pkg/app/login.go +++ b/pkg/app/login.go @@ -46,7 +46,7 @@ func (f *loginForm) Parse(r *http.Request) error { f.Password.FillValue(r) f.Redirect.FillValue(r) if f.Redirect.Val == "" { - f.Redirect.Val = "/" + f.Redirect.Val = "/admin/" } return nil } @@ -61,7 +61,7 @@ func (f *loginForm) Valid(l *locale.Locale) bool { } func (f *loginForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { - template.MustRender(w, r, user, company, "login.gohtml", f) + template.MustRenderAdmin(w, r, user, company, "login.gohtml", f) } func serveLoginForm(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, redirectPath string) { diff --git a/pkg/app/public.go b/pkg/app/public.go new file mode 100644 index 0000000..5afd6ef --- /dev/null +++ b/pkg/app/public.go @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023 jordi fita mas + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package app + +import ( + "net/http" + + "dev.tandem.ws/tandem/camper/pkg/auth" + "dev.tandem.ws/tandem/camper/pkg/database" + httplib "dev.tandem.ws/tandem/camper/pkg/http" + "dev.tandem.ws/tandem/camper/pkg/template" +) + +type publicHandler struct{} + +func newPublicHandler() *publicHandler { + return &publicHandler{} +} + +func (h *publicHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var head string + head, r.URL.Path = httplib.ShiftPath(r.URL.Path) + switch head { + case "": + template.MustRenderPublic(w, r, user, company, "home.gohtml", nil) + default: + http.NotFound(w, r) + } + }) +} diff --git a/pkg/app/user.go b/pkg/app/user.go index f80302f..dce876a 100644 --- a/pkg/app/user.go +++ b/pkg/app/user.go @@ -80,8 +80,14 @@ func newProfileHandler(static http.Handler, avatarsDir string) (*profileHandler, return handler, nil } -func (h *profileHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn) http.HandlerFunc { +func (h *profileHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn, requestPath string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + if !user.LoggedIn { + w.WriteHeader(http.StatusUnauthorized) + serveLoginForm(w, r, user, company, requestPath) + return + } + var head string head, r.URL.Path = httplib.ShiftPath(r.URL.Path) @@ -249,7 +255,7 @@ func (f *profileForm) Valid(l *locale.Locale) bool { } func (f *profileForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { - template.MustRender(w, r, user, company, "profile.gohtml", f) + template.MustRenderAdmin(w, r, user, company, "profile.gohtml", f) } func (f *profileForm) HasAvatarFile() bool { diff --git a/pkg/auth/user.go b/pkg/auth/user.go index 733ded9..67dbe30 100644 --- a/pkg/auth/user.go +++ b/pkg/auth/user.go @@ -39,3 +39,8 @@ func (user *User) VerifyCSRFToken(r *http.Request) error { } return errors.New(user.Locale.Gettext("Cross-site request forgery detected.")) } + +func (user *User) IsEmployee() bool { + role := user.Role[0] + return role == 'e' || role == 'a' +} diff --git a/pkg/campsite/type.go b/pkg/campsite/type.go index 15e9ca2..ef0e8ac 100644 --- a/pkg/campsite/type.go +++ b/pkg/campsite/type.go @@ -89,7 +89,7 @@ type typeIndex struct { } func (page *typeIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { - template.MustRender(w, r, user, company, "campsite/type/index.gohtml", page) + template.MustRenderAdmin(w, r, user, company, "campsite/type/index.gohtml", page) } func addType(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { @@ -145,5 +145,5 @@ func (f *typeForm) Valid(l *locale.Locale) bool { } func (f *typeForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { - template.MustRender(w, r, user, company, "campsite/type/new.gohtml", f) + template.MustRenderAdmin(w, r, user, company, "campsite/type/new.gohtml", f) } diff --git a/pkg/http/links.go b/pkg/http/links.go new file mode 100644 index 0000000..aad70ce --- /dev/null +++ b/pkg/http/links.go @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2023 jordi fita mas + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package http + +import ( + "fmt" + "net/http" + + "dev.tandem.ws/tandem/camper/pkg/locale" +) + +type languageLinks struct { + http.ResponseWriter + schemaAuthority string + path string + locales locale.Locales + wroteHeader bool +} + +func (w *languageLinks) WriteHeader(statusCode int) { + if statusCode >= 200 && statusCode < 300 { + for k := range w.locales { + tag := k.String() + w.Header().Add("Link", fmt.Sprintf(`<%[1]s/%[2]s%[3]s>; rel="alternate"; hreflang="%[2]s"`, w.schemaAuthority, tag, w.path)) + } + } + w.wroteHeader = true + w.ResponseWriter.WriteHeader(statusCode) +} + +func (w *languageLinks) Write(data []byte) (int, error) { + if !w.wroteHeader { + w.WriteHeader(http.StatusOK) + } + return w.ResponseWriter.Write(data) +} + +func LanguageLinks(w http.ResponseWriter, https bool, authority string, path string, locales locale.Locales) http.ResponseWriter { + var schema string + if https { + schema = "https" + } else { + schema = "http" + } + return &languageLinks{w, schema + "://" + authority, path, locales, false} +} diff --git a/pkg/template/render.go b/pkg/template/render.go index 89e869b..5a9d671 100644 --- a/pkg/template/render.go +++ b/pkg/template/render.go @@ -15,19 +15,28 @@ import ( httplib "dev.tandem.ws/tandem/camper/pkg/http" ) -func templateFile(name string) string { - return "web/templates/" + name +func adminTemplateFile(name string) string { + return "web/templates/admin/" + name } -func MustRender(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) { +func publicTemplateFile(name string) string { + return "web/templates/public/" + name +} + +func MustRenderAdmin(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) { layout := "layout.gohtml" if httplib.IsHTMxRequest(r) { layout = "htmx.gohtml" } - mustRenderLayout(w, user, company, layout, filename, data) + mustRenderLayout(w, user, company, adminTemplateFile, layout, filename, data) } -func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, layout string, filename string, data interface{}) { +func MustRenderPublic(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) { + layout := "layout.gohtml" + mustRenderLayout(w, user, company, publicTemplateFile, layout, filename, data) +} + +func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templateFile func(string) string, layout string, filename string, data interface{}) { t := template.New(filename) t.Funcs(template.FuncMap{ "gettext": user.Locale.Get, diff --git a/po/ca.po b/po/ca.po index 8bf07fb..6f6f23a 100644 --- a/po/ca.po +++ b/po/ca.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: camper\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n" -"POT-Creation-Date: 2023-08-04 19:51+0200\n" +"POT-Creation-Date: 2023-08-05 03:23+0200\n" "PO-Revision-Date: 2023-07-22 23:45+0200\n" "Last-Translator: jordi fita mas \n" "Language-Team: Catalan \n" @@ -18,123 +18,133 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: web/templates/campsite/type/new.gohtml:14 -#: web/templates/campsite/type/new.gohtml:20 +#: web/templates/public/home.gohtml:6 +msgctxt "title" +msgid "Home" +msgstr "Inici" + +#: web/templates/public/layout.gohtml:10 web/templates/public/layout.gohtml:17 +msgid "Campsite Montagut" +msgstr "Càmping Montagut" + +#: web/templates/public/layout.gohtml:16 web/templates/admin/layout.gohtml:18 +msgid "Skip to main content" +msgstr "Salta al contingut principal" + +#: web/templates/admin/campsite/type/new.gohtml:14 +#: web/templates/admin/campsite/type/new.gohtml:20 msgctxt "title" msgid "New Campsite Type" msgstr "Nou tipus d’allotjament" -#: web/templates/campsite/type/new.gohtml:25 web/templates/profile.gohtml:26 +#: web/templates/admin/campsite/type/new.gohtml:25 +#: web/templates/admin/profile.gohtml:26 msgctxt "input" msgid "Name" msgstr "Nom" -#: web/templates/campsite/type/new.gohtml:33 +#: web/templates/admin/campsite/type/new.gohtml:33 msgctxt "input" msgid "Description" msgstr "Descripció" -#: web/templates/campsite/type/new.gohtml:40 +#: web/templates/admin/campsite/type/new.gohtml:40 msgctxt "action" msgid "Add" msgstr "Afegeix" -#: web/templates/campsite/type/index.gohtml:6 -#: web/templates/campsite/type/index.gohtml:12 +#: web/templates/admin/campsite/type/index.gohtml:6 +#: web/templates/admin/campsite/type/index.gohtml:12 msgctxt "title" msgid "Campsite Types" msgstr "Tipus d’allotjaments" -#: web/templates/campsite/type/index.gohtml:11 +#: web/templates/admin/campsite/type/index.gohtml:11 msgctxt "action" msgid "Add Type" msgstr "Afegeix tipus" -#: web/templates/campsite/type/index.gohtml:17 +#: web/templates/admin/campsite/type/index.gohtml:17 msgctxt "header" msgid "Name" msgstr "Nom" -#: web/templates/campsite/type/index.gohtml:29 +#: web/templates/admin/campsite/type/index.gohtml:29 msgid "No campsite types added yet." msgstr "No s’ha afegit cap tipus d’allotjament encara." -#: web/templates/dashboard.gohtml:6 web/templates/dashboard.gohtml:10 -#: web/templates/layout.gohtml:44 +#: web/templates/admin/dashboard.gohtml:6 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:44 msgctxt "title" msgid "Dashboard" msgstr "Tauler" -#: web/templates/login.gohtml:6 web/templates/login.gohtml:13 +#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:13 msgctxt "title" msgid "Login" msgstr "Entrada" -#: web/templates/login.gohtml:22 web/templates/profile.gohtml:35 +#: web/templates/admin/login.gohtml:22 web/templates/admin/profile.gohtml:35 msgctxt "input" msgid "Email" msgstr "Correu-e" -#: web/templates/login.gohtml:31 web/templates/profile.gohtml:46 +#: web/templates/admin/login.gohtml:31 web/templates/admin/profile.gohtml:46 msgctxt "input" msgid "Password" msgstr "Contrasenya" -#: web/templates/login.gohtml:40 +#: web/templates/admin/login.gohtml:40 msgctxt "action" msgid "Login" msgstr "Entra" -#: web/templates/profile.gohtml:6 web/templates/profile.gohtml:12 -#: web/templates/layout.gohtml:29 +#: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12 +#: web/templates/admin/layout.gohtml:29 msgctxt "title" msgid "Profile" msgstr "Perfil" -#: web/templates/profile.gohtml:17 +#: web/templates/admin/profile.gohtml:17 msgctxt "inut" msgid "Profile Image" msgstr "Imatge del perfil" -#: web/templates/profile.gohtml:43 +#: web/templates/admin/profile.gohtml:43 msgctxt "legend" msgid "Change password" msgstr "Canvi de contrasenya" -#: web/templates/profile.gohtml:55 +#: web/templates/admin/profile.gohtml:55 msgctxt "input" msgid "Password Confirmation" msgstr "Confirmació de la contrasenya" -#: web/templates/profile.gohtml:65 +#: web/templates/admin/profile.gohtml:65 msgctxt "input" msgid "Language" msgstr "Idioma" -#: web/templates/profile.gohtml:75 +#: web/templates/admin/profile.gohtml:75 msgctxt "action" msgid "Save changes" msgstr "Desa els canvis" -#: web/templates/layout.gohtml:18 -msgid "Skip to main content" -msgstr "Salta al contingut principal" - -#: web/templates/layout.gohtml:25 +#: web/templates/admin/layout.gohtml:25 msgctxt "title" msgid "User Menu" msgstr "Menú d’usuari" -#: web/templates/layout.gohtml:33 +#: web/templates/admin/layout.gohtml:33 msgctxt "action" msgid "Logout" msgstr "Surt" -#: pkg/app/login.go:56 pkg/app/user.go:239 +#: pkg/app/login.go:56 pkg/app/user.go:245 msgid "Email can not be empty." msgstr "No podeu deixar el correu en blanc." -#: pkg/app/login.go:57 pkg/app/user.go:240 +#: pkg/app/login.go:57 pkg/app/user.go:246 msgid "This email is not valid. It should be like name@domain.com." msgstr "Aquest correu-e no és vàlid. Hauria de ser similar a nom@domini.com." @@ -146,27 +156,31 @@ msgstr "No podeu deixar la contrasenya en blanc." msgid "Invalid user or password." msgstr "Nom d’usuari o contrasenya incorrectes." -#: pkg/app/user.go:190 +#: pkg/app/user.go:196 msgctxt "language option" msgid "Automatic" msgstr "Automàtic" -#: pkg/app/user.go:242 pkg/campsite/type.go:143 +#: pkg/app/user.go:248 pkg/campsite/type.go:143 msgid "Name can not be empty." msgstr "No podeu deixar el nom en blanc." -#: pkg/app/user.go:243 +#: pkg/app/user.go:249 msgid "Confirmation does not match password." msgstr "La confirmació no es correspon amb la contrasenya." -#: pkg/app/user.go:244 +#: pkg/app/user.go:250 msgid "Selected language is not valid." msgstr "L’idioma escollit no és vàlid." -#: pkg/app/user.go:246 +#: pkg/app/user.go:252 msgid "File must be a valid PNG or JPEG image." msgstr "El fitxer has de ser una imatge PNG o JPEG vàlida." +#: pkg/app/admin.go:37 +msgid "Access forbidden" +msgstr "Accés prohibit" + #: pkg/auth/user.go:40 msgid "Cross-site request forgery detected." msgstr "S’ha detectat un intent de falsificació de petició a llocs creuats." diff --git a/po/es.po b/po/es.po index 289204d..4ac992e 100644 --- a/po/es.po +++ b/po/es.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: camper\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n" -"POT-Creation-Date: 2023-08-04 19:51+0200\n" +"POT-Creation-Date: 2023-08-05 03:23+0200\n" "PO-Revision-Date: 2023-07-22 23:46+0200\n" "Last-Translator: jordi fita mas \n" "Language-Team: Spanish \n" @@ -18,123 +18,133 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: web/templates/campsite/type/new.gohtml:14 -#: web/templates/campsite/type/new.gohtml:20 +#: web/templates/public/home.gohtml:6 +msgctxt "title" +msgid "Home" +msgstr "Inicio" + +#: web/templates/public/layout.gohtml:10 web/templates/public/layout.gohtml:17 +msgid "Campsite Montagut" +msgstr "Camping Montagut" + +#: web/templates/public/layout.gohtml:16 web/templates/admin/layout.gohtml:18 +msgid "Skip to main content" +msgstr "Saltar al contenido principal" + +#: web/templates/admin/campsite/type/new.gohtml:14 +#: web/templates/admin/campsite/type/new.gohtml:20 msgctxt "title" msgid "New Campsite Type" msgstr "Nuevo tipo de alojamiento" -#: web/templates/campsite/type/new.gohtml:25 web/templates/profile.gohtml:26 +#: web/templates/admin/campsite/type/new.gohtml:25 +#: web/templates/admin/profile.gohtml:26 msgctxt "input" msgid "Name" msgstr "Nombre" -#: web/templates/campsite/type/new.gohtml:33 +#: web/templates/admin/campsite/type/new.gohtml:33 msgctxt "input" msgid "Description" msgstr "Descripción" -#: web/templates/campsite/type/new.gohtml:40 +#: web/templates/admin/campsite/type/new.gohtml:40 msgctxt "action" msgid "Add" msgstr "Añadir" -#: web/templates/campsite/type/index.gohtml:6 -#: web/templates/campsite/type/index.gohtml:12 +#: web/templates/admin/campsite/type/index.gohtml:6 +#: web/templates/admin/campsite/type/index.gohtml:12 msgctxt "title" msgid "Campsite Types" msgstr "Tipos de alojamientos" -#: web/templates/campsite/type/index.gohtml:11 +#: web/templates/admin/campsite/type/index.gohtml:11 msgctxt "action" msgid "Add Type" msgstr "Añadir tipo" -#: web/templates/campsite/type/index.gohtml:17 +#: web/templates/admin/campsite/type/index.gohtml:17 msgctxt "header" msgid "Name" msgstr "Nombre" -#: web/templates/campsite/type/index.gohtml:29 +#: web/templates/admin/campsite/type/index.gohtml:29 msgid "No campsite types added yet." msgstr "No se ha añadido ningún tipo de alojamiento todavía." -#: web/templates/dashboard.gohtml:6 web/templates/dashboard.gohtml:10 -#: web/templates/layout.gohtml:44 +#: web/templates/admin/dashboard.gohtml:6 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:44 msgctxt "title" msgid "Dashboard" msgstr "Panel" -#: web/templates/login.gohtml:6 web/templates/login.gohtml:13 +#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:13 msgctxt "title" msgid "Login" msgstr "Entrada" -#: web/templates/login.gohtml:22 web/templates/profile.gohtml:35 +#: web/templates/admin/login.gohtml:22 web/templates/admin/profile.gohtml:35 msgctxt "input" msgid "Email" msgstr "Correo-e" -#: web/templates/login.gohtml:31 web/templates/profile.gohtml:46 +#: web/templates/admin/login.gohtml:31 web/templates/admin/profile.gohtml:46 msgctxt "input" msgid "Password" msgstr "Contraseña" -#: web/templates/login.gohtml:40 +#: web/templates/admin/login.gohtml:40 msgctxt "action" msgid "Login" msgstr "Entrar" -#: web/templates/profile.gohtml:6 web/templates/profile.gohtml:12 -#: web/templates/layout.gohtml:29 +#: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12 +#: web/templates/admin/layout.gohtml:29 msgctxt "title" msgid "Profile" msgstr "Perfil" -#: web/templates/profile.gohtml:17 +#: web/templates/admin/profile.gohtml:17 msgctxt "inut" msgid "Profile Image" msgstr "Imagen del perfil" -#: web/templates/profile.gohtml:43 +#: web/templates/admin/profile.gohtml:43 msgctxt "legend" msgid "Change password" msgstr "Cambio de contraseña" -#: web/templates/profile.gohtml:55 +#: web/templates/admin/profile.gohtml:55 msgctxt "input" msgid "Password Confirmation" msgstr "Confirmación de la contraseña" -#: web/templates/profile.gohtml:65 +#: web/templates/admin/profile.gohtml:65 msgctxt "input" msgid "Language" msgstr "Idioma" -#: web/templates/profile.gohtml:75 +#: web/templates/admin/profile.gohtml:75 msgctxt "action" msgid "Save changes" msgstr "Guardar los cambios" -#: web/templates/layout.gohtml:18 -msgid "Skip to main content" -msgstr "Saltar al contenido principal" - -#: web/templates/layout.gohtml:25 +#: web/templates/admin/layout.gohtml:25 msgctxt "title" msgid "User Menu" msgstr "Menú de usuario" -#: web/templates/layout.gohtml:33 +#: web/templates/admin/layout.gohtml:33 msgctxt "action" msgid "Logout" msgstr "Salir" -#: pkg/app/login.go:56 pkg/app/user.go:239 +#: pkg/app/login.go:56 pkg/app/user.go:245 msgid "Email can not be empty." msgstr "No podéis dejar el correo-e en blanco." -#: pkg/app/login.go:57 pkg/app/user.go:240 +#: pkg/app/login.go:57 pkg/app/user.go:246 msgid "This email is not valid. It should be like name@domain.com." msgstr "Este correo-e no es válido. Tiene que ser parecido a nombre@dominio.com." @@ -146,27 +156,31 @@ msgstr "No podéis dejar la contraseña en blanco." msgid "Invalid user or password." msgstr "Usuario o contraseña incorrectos." -#: pkg/app/user.go:190 +#: pkg/app/user.go:196 msgctxt "language option" msgid "Automatic" msgstr "Automático" -#: pkg/app/user.go:242 pkg/campsite/type.go:143 +#: pkg/app/user.go:248 pkg/campsite/type.go:143 msgid "Name can not be empty." msgstr "No podéis dejar el nombre en blanco." -#: pkg/app/user.go:243 +#: pkg/app/user.go:249 msgid "Confirmation does not match password." msgstr "La confirmación no se corresponde con la contraseña." -#: pkg/app/user.go:244 +#: pkg/app/user.go:250 msgid "Selected language is not valid." msgstr "El idioma escogido no es válido." -#: pkg/app/user.go:246 +#: pkg/app/user.go:252 msgid "File must be a valid PNG or JPEG image." msgstr "El archivo tiene que ser una imagen PNG o JPEG válida." +#: pkg/app/admin.go:37 +msgid "Access forbidden" +msgstr "Acceso prohibido" + #: pkg/auth/user.go:40 msgid "Cross-site request forgery detected." msgstr "Se ha detectado un intento de falsificación de petición en sitios cruzados." diff --git a/web/static/public.css b/web/static/public.css new file mode 100644 index 0000000..5c78df1 --- /dev/null +++ b/web/static/public.css @@ -0,0 +1,41 @@ +/** + * SPDX-FileCopyrightText: 2023 jordi fita mas + * SPDX-License-Identifier: AGPL-3.0-only + */ + +*, *::before, *::after { + box-sizing: border-box; +} + +* { + margin: 0; +} + +html, body { + height: 100%; +} + +html { + font-size: 62.5%; +} + +body { + font-size: 1.6rem; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + background-color: white; + color: #3f3b37; +} + +img, picture, video, canvas, svg { + display: block; + max-width: 100%; +} + +input, button, textarea, select { + font: inherit; +} + +p, h1, h2, h3, h4, h5, h6 { + overflow-wrap: break-word; +} diff --git a/web/templates/campsite/type/index.gohtml b/web/templates/admin/campsite/type/index.gohtml similarity index 82% rename from web/templates/campsite/type/index.gohtml rename to web/templates/admin/campsite/type/index.gohtml index 385bf9e..c703b53 100644 --- a/web/templates/campsite/type/index.gohtml +++ b/web/templates/admin/campsite/type/index.gohtml @@ -8,7 +8,7 @@ {{ define "content" -}} {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/campsite.typeIndex*/ -}} - {{( pgettext "Add Type" "action" )}} + {{( pgettext "Add Type" "action" )}}

{{( pgettext "Campsite Types" "title" )}}

{{ if .Types -}} @@ -20,7 +20,7 @@ {{ range .Types -}} - + {{- end }} diff --git a/web/templates/campsite/type/new.gohtml b/web/templates/admin/campsite/type/new.gohtml similarity index 96% rename from web/templates/campsite/type/new.gohtml rename to web/templates/admin/campsite/type/new.gohtml index 1be73bb..68c67bd 100644 --- a/web/templates/campsite/type/new.gohtml +++ b/web/templates/admin/campsite/type/new.gohtml @@ -16,7 +16,7 @@ {{ define "content" -}} {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/campsite.typeForm*/ -}} - +

{{( pgettext "New Campsite Type" "title" )}}

{{ CSRFInput }}
diff --git a/web/templates/dashboard.gohtml b/web/templates/admin/dashboard.gohtml similarity index 100% rename from web/templates/dashboard.gohtml rename to web/templates/admin/dashboard.gohtml diff --git a/web/templates/form.gohtml b/web/templates/admin/form.gohtml similarity index 100% rename from web/templates/form.gohtml rename to web/templates/admin/form.gohtml diff --git a/web/templates/htmx.gohtml b/web/templates/admin/htmx.gohtml similarity index 100% rename from web/templates/htmx.gohtml rename to web/templates/admin/htmx.gohtml diff --git a/web/templates/layout.gohtml b/web/templates/admin/layout.gohtml similarity index 95% rename from web/templates/layout.gohtml rename to web/templates/admin/layout.gohtml index 035f687..f59154c 100644 --- a/web/templates/layout.gohtml +++ b/web/templates/admin/layout.gohtml @@ -41,7 +41,7 @@ diff --git a/web/templates/login.gohtml b/web/templates/admin/login.gohtml similarity index 100% rename from web/templates/login.gohtml rename to web/templates/admin/login.gohtml diff --git a/web/templates/profile.gohtml b/web/templates/admin/profile.gohtml similarity index 100% rename from web/templates/profile.gohtml rename to web/templates/admin/profile.gohtml diff --git a/web/templates/public/form.gohtml b/web/templates/public/form.gohtml new file mode 120000 index 0000000..ca7a896 --- /dev/null +++ b/web/templates/public/form.gohtml @@ -0,0 +1 @@ +../admin/form.gohtml \ No newline at end of file diff --git a/web/templates/public/home.gohtml b/web/templates/public/home.gohtml new file mode 100644 index 0000000..3c23057 --- /dev/null +++ b/web/templates/public/home.gohtml @@ -0,0 +1,10 @@ + +{{ define "title" -}} + {{( pgettext "Home" "title" )}} +{{- end }} + +{{ define "content" -}} +{{- end }} diff --git a/web/templates/public/layout.gohtml b/web/templates/public/layout.gohtml new file mode 100644 index 0000000..4b31e58 --- /dev/null +++ b/web/templates/public/layout.gohtml @@ -0,0 +1,23 @@ + + + + + + + {{ template "title" . }} — {{( gettext "Campsite Montagut" )}} + + {{ block "head" . }}{{ end }} + + +
+ {{( gettext "Skip to main content" )}} +

{{( gettext "Campsite Montagut" )}}

+
+
+ {{- template "content" . }} +
+ +
{{ .Name }}{{ .Name }}