diff --git a/deploy/legal_text.sql b/deploy/legal_text.sql new file mode 100644 index 0000000..e4dfeee --- /dev/null +++ b/deploy/legal_text.sql @@ -0,0 +1,56 @@ +-- Deploy camper:legal_text to pg +-- requires: roles +-- requires: schema_camper +-- requires: company +-- requires: user_profile + +begin; + +set search_path to camper, public; + +create table legal_text ( + company_id integer not null references company, + slug text constraint valid_slug check(slug ~ '^[a-z0-9]+(-[a-z0-9])*$'), + name text not null constraint name_not_empty check(length(trim(name)) > 0), + content xml not null, + primary key (company_id, slug) +); + +grant select on table legal_text to guest; +grant select on table legal_text to employee; +grant select, insert, update, delete on table legal_text to admin; + + +alter table legal_text enable row level security; + +create policy guest_ok +on legal_text +for select +using (true) +; + +create policy insert_to_company +on legal_text +for insert +with check ( + company_id in (select company_id from user_profile) +) +; + +create policy update_company +on legal_text +for update +using ( + company_id in (select company_id from user_profile) +) +; + +create policy delete_from_company +on legal_text +for delete +using ( + company_id in (select company_id from user_profile) +) +; + +commit; diff --git a/deploy/legal_text_i18n.sql b/deploy/legal_text_i18n.sql new file mode 100644 index 0000000..7b8e46e --- /dev/null +++ b/deploy/legal_text_i18n.sql @@ -0,0 +1,25 @@ +-- Deploy camper:legal_text_i18n to pg +-- requires: roles +-- requires: schema_camper +-- requires: legal_text +-- requires: language + +begin; + +set search_path to camper, public; + +create table legal_text_i18n ( + company_id integer not null, + slug text not null, + lang_tag text not null references language, + name text not null, + content xml not null, + foreign key (company_id, slug) references legal_text, + primary key (company_id, slug, lang_tag) +); + +grant select on table legal_text_i18n to guest; +grant select on table legal_text_i18n to employee; +grant select, insert, update, delete on table legal_text_i18n to admin; + +commit; diff --git a/deploy/translate_legal_text.sql b/deploy/translate_legal_text.sql new file mode 100644 index 0000000..44a3e43 --- /dev/null +++ b/deploy/translate_legal_text.sql @@ -0,0 +1,25 @@ +-- Deploy camper:translate_legal_text to pg +-- requires: roles +-- requires: schema_camper +-- requires: legal_text_i18n + +begin; + +set search_path to camper, public; + +create or replace function translate_legal_text(company_id integer, slug text, lang_tag text, name text, content text) returns void as +$$ + insert into legal_text_i18n (company_id, slug, lang_tag, name, content) + values (company_id, slug, lang_tag, coalesce(name, ''), xmlparse(content coalesce(content, ''))) + on conflict (company_id, slug, lang_tag) do update + set name = excluded.name + , content = excluded.content + ; +$$ + language sql +; + +revoke execute on function translate_legal_text(integer, text, text, text, text) from public; +grant execute on function translate_legal_text(integer, text, text, text, text) to admin; + +commit; diff --git a/pkg/app/admin.go b/pkg/app/admin.go index a89d9bd..e055478 100644 --- a/pkg/app/admin.go +++ b/pkg/app/admin.go @@ -6,7 +6,6 @@ package app import ( - "dev.tandem.ws/tandem/camper/pkg/location" "net/http" "dev.tandem.ws/tandem/camper/pkg/auth" @@ -16,7 +15,9 @@ import ( "dev.tandem.ws/tandem/camper/pkg/database" "dev.tandem.ws/tandem/camper/pkg/home" httplib "dev.tandem.ws/tandem/camper/pkg/http" + "dev.tandem.ws/tandem/camper/pkg/legal" "dev.tandem.ws/tandem/camper/pkg/locale" + "dev.tandem.ws/tandem/camper/pkg/location" "dev.tandem.ws/tandem/camper/pkg/media" "dev.tandem.ws/tandem/camper/pkg/season" "dev.tandem.ws/tandem/camper/pkg/services" @@ -27,6 +28,7 @@ type adminHandler struct { campsite *campsite.AdminHandler company *company.AdminHandler home *home.AdminHandler + legal *legal.AdminHandler location *location.AdminHandler media *media.AdminHandler payment *booking.AdminHandler @@ -39,6 +41,7 @@ func newAdminHandler(locales locale.Locales, mediaDir string) *adminHandler { campsite: campsite.NewAdminHandler(locales), company: company.NewAdminHandler(), home: home.NewAdminHandler(locales), + legal: legal.NewAdminHandler(), location: location.NewAdminHandler(), media: media.NewAdminHandler(mediaDir), payment: booking.NewAdminHandler(), @@ -69,6 +72,8 @@ func (h *adminHandler) Handle(user *auth.User, company *auth.Company, conn *data h.company.Handler(user, company, conn).ServeHTTP(w, r) case "home": h.home.Handler(user, company, conn).ServeHTTP(w, r) + case "legal": + h.legal.Handler(user, company, conn).ServeHTTP(w, r) case "location": h.location.Handler(user, company, conn).ServeHTTP(w, r) case "media": diff --git a/pkg/app/public.go b/pkg/app/public.go index eecbd72..5b10661 100644 --- a/pkg/app/public.go +++ b/pkg/app/public.go @@ -6,6 +6,7 @@ package app import ( + "dev.tandem.ws/tandem/camper/pkg/legal" "net/http" "dev.tandem.ws/tandem/camper/pkg/auth" @@ -23,6 +24,7 @@ type publicHandler struct { home *home.PublicHandler booking *booking.PublicHandler campsite *campsite.PublicHandler + legal *legal.PublicHandler location *location.PublicHandler services *services.PublicHandler } @@ -32,6 +34,7 @@ func newPublicHandler() *publicHandler { home: home.NewPublicHandler(), booking: booking.NewPublicHandler(), campsite: campsite.NewPublicHandler(), + legal: legal.NewPublicHandler(), location: location.NewPublicHandler(), services: services.NewPublicHandler(), } @@ -50,6 +53,8 @@ func (h *publicHandler) Handler(user *auth.User, company *auth.Company, conn *da campgroundHandler(user, company, conn).ServeHTTP(w, r) case "campsites": h.campsite.Handler(user, company, conn).ServeHTTP(w, r) + case "legal": + h.legal.Handler(user, company, conn).ServeHTTP(w, r) case "location": h.location.Handler(user, company, conn).ServeHTTP(w, r) case "services": diff --git a/pkg/database/funcs.go b/pkg/database/funcs.go index 68cf458..0428b34 100644 --- a/pkg/database/funcs.go +++ b/pkg/database/funcs.go @@ -102,3 +102,8 @@ func (tx *Tx) TranslateLocation(ctx context.Context, companyID int, langTag lang _, err := tx.Exec(ctx, "select translate_location($1, $2, $3, $4)", companyID, langTag, directions, openingHours) return err } + +func (tx *Tx) TranslateLegalText(ctx context.Context, companyID int, slug string, langTag language.Tag, name string, content string) error { + _, err := tx.Exec(ctx, "select translate_legal_text($1, $2, $3, $4, $5)", companyID, slug, langTag, name, content) + return err +} diff --git a/pkg/form/input.go b/pkg/form/input.go index 926beed..9b61af1 100644 --- a/pkg/form/input.go +++ b/pkg/form/input.go @@ -7,10 +7,14 @@ package form import ( "database/sql/driver" - "dev.tandem.ws/tandem/camper/pkg/locale" "net/http" "strconv" "strings" + + "golang.org/x/text/language" + + "dev.tandem.ws/tandem/camper/pkg/database" + "dev.tandem.ws/tandem/camper/pkg/locale" ) type Input struct { @@ -69,3 +73,14 @@ func (input I18nInput) FillValue(r *http.Request) { inner.FillValue(r) } } + +func (input I18nInput) FillArray(array database.RecordArray) error { + for _, el := range array.Elements { + tag, err := language.Parse(el.Fields[0].Get().(string)) + if err != nil { + return err + } + input[tag.String()].Val = el.Fields[1].Get().(string) + } + return nil +} diff --git a/pkg/legal/admin.go b/pkg/legal/admin.go new file mode 100644 index 0000000..91e86c2 --- /dev/null +++ b/pkg/legal/admin.go @@ -0,0 +1,263 @@ +package legal + +import ( + "context" + "net/http" + + "github.com/jackc/pgx/v4" + + "dev.tandem.ws/tandem/camper/pkg/auth" + "dev.tandem.ws/tandem/camper/pkg/database" + "dev.tandem.ws/tandem/camper/pkg/form" + httplib "dev.tandem.ws/tandem/camper/pkg/http" + "dev.tandem.ws/tandem/camper/pkg/locale" + "dev.tandem.ws/tandem/camper/pkg/template" +) + +type AdminHandler struct { +} + +func NewAdminHandler() *AdminHandler { + return &AdminHandler{} +} + +func (h *AdminHandler) 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 "": + switch r.Method { + case http.MethodGet: + serveLegalIndex(w, r, user, company, conn) + case http.MethodPost: + addLegal(w, r, user, company, conn) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost) + } + case "new": + switch r.Method { + case http.MethodGet: + f := newLegalForm(company) + f.MustRender(w, r, user, company) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet) + } + default: + f := newLegalForm(company) + if err := f.FillFromDatabase(r.Context(), conn, head); err != nil { + if database.ErrorIsNotFound(err) { + http.NotFound(w, r) + return + } + panic(err) + } + h.legalHandler(user, company, conn, f).ServeHTTP(w, r) + } + }) +} + +func (h *AdminHandler) legalHandler(user *auth.User, company *auth.Company, conn *database.Conn, f *legalForm) 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 "": + switch r.Method { + case http.MethodGet: + f.MustRender(w, r, user, company) + case http.MethodPut: + editLegal(w, r, user, company, conn, f) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut) + } + default: + http.NotFound(w, r) + } + }) +} + +func serveLegalIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { + legals, err := collectLegalEntries(r.Context(), conn, company) + if err != nil { + panic(err) + } + page := &legalIndex{ + Texts: legals, + } + page.MustRender(w, r, user, company) +} + +func collectLegalEntries(ctx context.Context, conn *database.Conn, company *auth.Company) ([]*legalEntry, error) { + rows, err := conn.Query(ctx, ` + select '/admin/legal/' || slug + , name + from legal_text + where company_id = $1 + order by name + `, company.ID) + if err != nil { + return nil, err + } + defer rows.Close() + + var legals []*legalEntry + for rows.Next() { + legal := &legalEntry{} + if err = rows.Scan(&legal.URL, &legal.Name); err != nil { + return nil, err + } + legals = append(legals, legal) + } + + return legals, nil +} + +type legalEntry struct { + URL string + Name string +} + +type legalIndex struct { + Texts []*legalEntry +} + +func (page *legalIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { + template.MustRenderAdmin(w, r, user, company, "legal/index.gohtml", page) +} + +func addLegal(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { + f := newLegalForm(company) + processLegalForm(w, r, user, company, conn, f, func(ctx context.Context, tx *database.Tx) error { + _, err := tx.Exec(ctx, ` + insert into legal_text (company_id, slug, name, content) + values ($1, $2, $3, xmlparse(content $4)) + `, company.ID, f.Slug.Val, f.Name[company.DefaultLanguage.String()].Val, f.Content[company.DefaultLanguage.String()]) + if err != nil { + return err + } + for lang := range company.Locales { + if err := tx.TranslateLegalText(ctx, company.ID, f.Slug.Val, lang, f.Name[lang.String()].Val, f.Content[lang.String()].Val); err != nil { + return err + } + } + return nil + }) +} + +func editLegal(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *legalForm) { + processLegalForm(w, r, user, company, conn, f, func(ctx context.Context, tx *database.Tx) error { + _, err := tx.Exec(ctx, ` + update legal_text + set name = $3 + , content = xmlparse(content $4) + where company_id = $1 + and slug = $2 + `, company.ID, f.Slug.Val, f.Name[company.DefaultLanguage.String()].Val, f.Content[company.DefaultLanguage.String()]) + if err != nil { + return err + } + for lang := range company.Locales { + if err := tx.TranslateLegalText(ctx, company.ID, f.Slug.Val, lang, f.Name[lang.String()].Val, f.Content[lang.String()].Val); err != nil { + return err + } + } + return nil + }) +} + +func processLegalForm(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *legalForm, act func(ctx context.Context, tx *database.Tx) error) { + if ok, err := form.Handle(f, w, r, user); err != nil { + return + } else if !ok { + f.MustRender(w, r, user, company) + return + } + + tx := conn.MustBegin(r.Context()) + if err := act(r.Context(), tx); err == nil { + if err := tx.Commit(r.Context()); err != nil { + panic(err) + } + } else { + if err := tx.Rollback(r.Context()); err != nil { + panic(err) + } + panic(err) + } + httplib.Redirect(w, r, "/admin/legal/"+f.Slug.Val, http.StatusSeeOther) +} + +type legalForm struct { + company *auth.Company + URL string + Slug *form.Input + Name form.I18nInput + Content form.I18nInput +} + +func newLegalForm(company *auth.Company) *legalForm { + f := &legalForm{ + company: company, + Slug: &form.Input{ + Name: "slug", + }, + Name: form.NewI18nInput(company.Locales, "name"), + Content: form.NewI18nInput(company.Locales, "content"), + } + return f +} + +func (f *legalForm) FillFromDatabase(ctx context.Context, conn *database.Conn, slug string) error { + var name database.RecordArray + var content database.RecordArray + row := conn.QueryRow(ctx, ` + select '/admin/legal/' || text.slug + , text.slug + , text.name + , text.content::text + , array_agg((lang_tag, i18n.name)) + , array_agg((lang_tag, i18n.content::text)) + from legal_text as text + left join legal_text_i18n as i18n using (company_id, slug) + where text.company_id = $1 + and text.slug = $2 + group by text.slug + , text.name + , text.content::text + `, pgx.QueryResultFormats{pgx.BinaryFormatCode}, f.company.ID, slug) + if err := row.Scan(&f.URL, &f.Slug.Val, &f.Name[f.company.DefaultLanguage.String()].Val, &f.Content[f.company.DefaultLanguage.String()].Val, &name, &content); err != nil { + return err + } + if err := f.Name.FillArray(name); err != nil { + return err + } + if err := f.Content.FillArray(content); err != nil { + return err + } + return nil +} + +func (f *legalForm) Parse(r *http.Request) error { + if err := r.ParseForm(); err != nil { + return err + } + f.Slug.FillValue(r) + f.Name.FillValue(r) + f.Content.FillValue(r) + return nil +} + +func (f *legalForm) Valid(l *locale.Locale) bool { + v := form.NewValidator(l) + if v.CheckRequired(f.Name[f.company.DefaultLanguage.String()], l.GettextNoop("Name can not be empty.")) { + v.CheckMinLength(f.Name[f.company.DefaultLanguage.String()], 1, l.GettextNoop("Name must have at least one letter.")) + } + return v.AllOK +} + +func (f *legalForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { + template.MustRenderAdmin(w, r, user, company, "legal/form.gohtml", f) +} diff --git a/pkg/legal/public.go b/pkg/legal/public.go new file mode 100644 index 0000000..2425cd3 --- /dev/null +++ b/pkg/legal/public.go @@ -0,0 +1,78 @@ +package legal + +import ( + "context" + "dev.tandem.ws/tandem/camper/pkg/locale" + gotemplate "html/template" + "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 slug string + slug, r.URL.Path = httplib.ShiftPath(r.URL.Path) + + var head string + head, r.URL.Path = httplib.ShiftPath(r.URL.Path) + + switch head { + case "": + switch r.Method { + case http.MethodGet: + page, err := newLegalPage(r.Context(), company, conn, user.Locale, slug) + if database.ErrorIsNotFound(err) { + http.NotFound(w, r) + return + } else if err != nil { + panic(err) + } + page.MustRender(w, r, user, company, conn) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet) + } + default: + http.NotFound(w, r) + } + }) +} + +type legalPage struct { + *template.PublicPage + Name string + Content gotemplate.HTML +} + +func newLegalPage(ctx context.Context, company *auth.Company, conn *database.Conn, loc *locale.Locale, slug string) (*legalPage, error) { + page := &legalPage{ + PublicPage: template.NewPublicPage(), + } + row := conn.QueryRow(ctx, ` + select coalesce(i18n.name, text.name) as l10n_name + , coalesce(i18n.content, text.content)::text as l10n_description + from legal_text as text + left join legal_text_i18n as i18n on text.company_id = i18n.company_id and text.slug = i18n.slug and i18n.lang_tag = $1 + where text.company_id = $2 + and text.slug = $3 + `, loc.Language, company.ID, slug) + if err := row.Scan(&page.Name, &page.Content); err != nil { + return nil, err + } + return page, nil +} + +func (p *legalPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { + p.Setup(r, user, company, conn) + template.MustRenderPublic(w, r, user, company, "legal.gohtml", p) +} diff --git a/pkg/location/admin.go b/pkg/location/admin.go index 7379e5d..68e4434 100644 --- a/pkg/location/admin.go +++ b/pkg/location/admin.go @@ -2,7 +2,6 @@ package location import ( "context" - "golang.org/x/text/language" "net/http" "github.com/jackc/pgx/v4" @@ -116,26 +115,15 @@ func (f *locationForm) FillFromDatabase(ctx context.Context, company *auth.Compa if err != nil { return err } - if err := fillI18nInput(f.Directions, directions); err != nil { + if err := f.Directions.FillArray(directions); err != nil { return err } - if err := fillI18nInput(f.OpeningDates, openingDates); err != nil { + if err := f.OpeningDates.FillArray(openingDates); err != nil { return err } return nil } -func fillI18nInput(input form.I18nInput, array database.RecordArray) error { - for _, el := range array.Elements { - tag, err := language.Parse(el.Fields[0].Get().(string)) - if err != nil { - return err - } - input[tag.String()].Val = el.Fields[1].Get().(string) - } - return nil -} - func (f *locationForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { template.MustRenderAdmin(w, r, user, company, "location.gohtml", f) } diff --git a/po/ca.po b/po/ca.po index 66814de..41612c4 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-12-21 21:08+0100\n" +"POT-Creation-Date: 2023-12-22 02:19+0100\n" "PO-Revision-Date: 2023-07-22 23:45+0200\n" "Last-Translator: jordi fita mas \n" "Language-Team: Catalan \n" @@ -43,7 +43,7 @@ msgstr "Ha fallat el pagament" #: web/templates/public/services.gohtml:6 #: web/templates/public/services.gohtml:15 -#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79 +#: web/templates/public/layout.gohtml:66 web/templates/public/layout.gohtml:94 #: web/templates/admin/services/index.gohtml:66 msgctxt "title" msgid "Services" @@ -55,14 +55,14 @@ msgstr "El càmping disposa de diversos serveis." #: web/templates/public/location.gohtml:6 #: web/templates/public/location.gohtml:12 -#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:81 +#: web/templates/public/layout.gohtml:68 web/templates/public/layout.gohtml:96 #: web/templates/admin/layout.gohtml:60 msgctxt "title" msgid "Location" msgstr "Com arribar" -#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:37 -#: web/templates/public/layout.gohtml:77 +#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:52 +#: web/templates/public/layout.gohtml:92 msgctxt "title" msgid "Home" msgstr "Inici" @@ -87,7 +87,7 @@ msgstr "Vine a gaudir!" #: web/templates/public/home.gohtml:35 #: web/templates/public/surroundings.gohtml:6 #: web/templates/public/surroundings.gohtml:10 -#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80 +#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95 msgctxt "title" msgid "Surroundings" msgstr "L’entorn" @@ -271,13 +271,13 @@ msgstr "Hi ha diversos punts on poder anar amb caiac, des de trams del riu Ter c #: web/templates/public/campground.gohtml:6 #: web/templates/public/campground.gohtml:11 -#: web/templates/public/layout.gohtml:38 web/templates/public/layout.gohtml:78 +#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:93 msgctxt "title" msgid "Campground" msgstr "El càmping" #: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11 -#: web/templates/public/layout.gohtml:54 +#: web/templates/public/layout.gohtml:69 msgctxt "title" msgid "Booking" msgstr "Reserva" @@ -365,8 +365,8 @@ msgctxt "input" msgid "I have read and I accept the reservation conditions" msgstr "He llegit i accepto les condicions de reserves" -#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:32 -#: web/templates/public/layout.gohtml:109 +#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:47 +#: web/templates/public/layout.gohtml:130 msgid "Campsite Montagut" msgstr "Càmping Montagut" @@ -374,28 +374,118 @@ msgstr "Càmping Montagut" msgid "Skip to main content" msgstr "Salta al contingut principal" -#: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88 +#: web/templates/public/layout.gohtml:57 web/templates/public/layout.gohtml:103 #: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:12 -#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79 +#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:82 msgctxt "title" msgid "Campsites" msgstr "Allotjaments" -#: web/templates/public/layout.gohtml:75 +#: web/templates/public/layout.gohtml:90 msgctxt "title" msgid "Sections" msgstr "Apartats" -#: web/templates/public/layout.gohtml:99 +#: web/templates/public/layout.gohtml:114 msgctxt "title" msgid "Opening" msgstr "Obertura" -#: web/templates/public/layout.gohtml:106 +#: web/templates/public/layout.gohtml:121 msgid "RTC #%s" msgstr "Núm. RTC %s" +#: web/templates/admin/legal/form.gohtml:8 +#: web/templates/admin/legal/form.gohtml:25 +msgctxt "title" +msgid "Edit Legal Text" +msgstr "Edició del text legal" + +#: web/templates/admin/legal/form.gohtml:10 +#: web/templates/admin/legal/form.gohtml:27 +msgctxt "title" +msgid "New Legal Text" +msgstr "Nou text legal" + +#: web/templates/admin/legal/form.gohtml:37 +msgctxt "input" +msgid "Slug" +msgstr "Àlies" + +#: web/templates/admin/legal/form.gohtml:46 +#: web/templates/admin/campsite/feature/form.gohtml:52 +#: web/templates/admin/campsite/feature/l10n.gohtml:20 +#: web/templates/admin/campsite/option/form.gohtml:34 +#: web/templates/admin/campsite/option/l10n.gohtml:20 +#: web/templates/admin/campsite/type/form.gohtml:46 +#: web/templates/admin/campsite/type/l10n.gohtml:20 +#: web/templates/admin/season/form.gohtml:46 +#: web/templates/admin/season/l10n.gohtml:20 +#: web/templates/admin/services/form.gohtml:52 +#: web/templates/admin/services/l10n.gohtml:20 +#: web/templates/admin/profile.gohtml:26 +msgctxt "input" +msgid "Name" +msgstr "Nom" + +#: web/templates/admin/legal/form.gohtml:64 +msgctxt "input" +msgid "Content" +msgstr "Contingut" + +#: web/templates/admin/legal/form.gohtml:84 +#: web/templates/admin/carousel/form.gohtml:47 +#: web/templates/admin/campsite/feature/form.gohtml:62 +#: web/templates/admin/campsite/carousel/form.gohtml:47 +#: web/templates/admin/campsite/form.gohtml:70 +#: web/templates/admin/campsite/option/form.gohtml:78 +#: web/templates/admin/campsite/type/form.gohtml:129 +#: web/templates/admin/season/form.gohtml:64 +#: web/templates/admin/services/form.gohtml:69 +#: web/templates/admin/media/form.gohtml:35 +msgctxt "action" +msgid "Update" +msgstr "Actualitza" + +#: web/templates/admin/legal/form.gohtml:86 +#: web/templates/admin/carousel/form.gohtml:49 +#: web/templates/admin/campsite/feature/form.gohtml:64 +#: web/templates/admin/campsite/carousel/form.gohtml:49 +#: web/templates/admin/campsite/form.gohtml:72 +#: web/templates/admin/campsite/option/form.gohtml:80 +#: web/templates/admin/campsite/type/form.gohtml:131 +#: web/templates/admin/season/form.gohtml:66 +#: web/templates/admin/services/form.gohtml:71 +msgctxt "action" +msgid "Add" +msgstr "Afegeix" + +#: web/templates/admin/legal/index.gohtml:6 +#: web/templates/admin/legal/index.gohtml:12 +#: web/templates/admin/layout.gohtml:63 +msgctxt "title" +msgid "Legal Texts" +msgstr "Texts legals" + +#: web/templates/admin/legal/index.gohtml:11 +msgctxt "action" +msgid "Add Legal Text" +msgstr "Afegeix text legal" + +#: web/templates/admin/legal/index.gohtml:17 +#: web/templates/admin/campsite/feature/index.gohtml:26 +#: web/templates/admin/campsite/option/index.gohtml:25 +#: web/templates/admin/campsite/type/index.gohtml:25 +#: web/templates/admin/season/index.gohtml:26 +msgctxt "header" +msgid "Name" +msgstr "Nom" + +#: web/templates/admin/legal/index.gohtml:29 +msgid "No legal texts added yet." +msgstr "No s’ha afegit cap text legal encara." + #: web/templates/admin/carousel/form.gohtml:8 #: web/templates/admin/carousel/form.gohtml:25 msgctxt "title" @@ -416,31 +506,6 @@ msgctxt "input" msgid "Caption" msgstr "Llegenda" -#: web/templates/admin/carousel/form.gohtml:47 -#: web/templates/admin/campsite/feature/form.gohtml:62 -#: web/templates/admin/campsite/carousel/form.gohtml:47 -#: web/templates/admin/campsite/form.gohtml:70 -#: web/templates/admin/campsite/option/form.gohtml:78 -#: web/templates/admin/campsite/type/form.gohtml:129 -#: web/templates/admin/season/form.gohtml:64 -#: web/templates/admin/services/form.gohtml:69 -#: web/templates/admin/media/form.gohtml:35 -msgctxt "action" -msgid "Update" -msgstr "Actualitza" - -#: web/templates/admin/carousel/form.gohtml:49 -#: web/templates/admin/campsite/feature/form.gohtml:64 -#: web/templates/admin/campsite/carousel/form.gohtml:49 -#: web/templates/admin/campsite/form.gohtml:72 -#: web/templates/admin/campsite/option/form.gohtml:80 -#: web/templates/admin/campsite/type/form.gohtml:131 -#: web/templates/admin/season/form.gohtml:66 -#: web/templates/admin/services/form.gohtml:71 -msgctxt "action" -msgid "Add" -msgstr "Afegeix" - #: web/templates/admin/carousel/l10n.gohtml:7 #: web/templates/admin/carousel/l10n.gohtml:14 msgctxt "title" @@ -534,21 +599,6 @@ msgctxt "input" msgid "Icon" msgstr "Icona" -#: web/templates/admin/campsite/feature/form.gohtml:52 -#: web/templates/admin/campsite/feature/l10n.gohtml:20 -#: web/templates/admin/campsite/option/form.gohtml:34 -#: web/templates/admin/campsite/option/l10n.gohtml:20 -#: web/templates/admin/campsite/type/form.gohtml:46 -#: web/templates/admin/campsite/type/l10n.gohtml:20 -#: web/templates/admin/season/form.gohtml:46 -#: web/templates/admin/season/l10n.gohtml:20 -#: web/templates/admin/services/form.gohtml:52 -#: web/templates/admin/services/l10n.gohtml:20 -#: web/templates/admin/profile.gohtml:26 -msgctxt "input" -msgid "Name" -msgstr "Nom" - #: web/templates/admin/campsite/feature/index.gohtml:6 #: web/templates/admin/campsite/feature/index.gohtml:12 msgctxt "title" @@ -560,14 +610,6 @@ msgctxt "action" msgid "Add Feature" msgstr "Afegeix característica" -#: web/templates/admin/campsite/feature/index.gohtml:26 -#: web/templates/admin/campsite/option/index.gohtml:25 -#: web/templates/admin/campsite/type/index.gohtml:25 -#: web/templates/admin/season/index.gohtml:26 -msgctxt "header" -msgid "Name" -msgstr "Nom" - #: web/templates/admin/campsite/feature/index.gohtml:27 #: web/templates/admin/campsite/carousel/index.gohtml:27 #: web/templates/admin/campsite/option/index.gohtml:26 @@ -979,7 +1021,7 @@ msgid "Integration" msgstr "Integració" #: web/templates/admin/dashboard.gohtml:6 -#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:79 msgctxt "title" msgid "Dashboard" msgstr "Tauler" @@ -1162,7 +1204,7 @@ msgctxt "title" msgid "Home Page" msgstr "Pàgina d’inici" -#: web/templates/admin/layout.gohtml:65 +#: web/templates/admin/layout.gohtml:68 msgctxt "action" msgid "Logout" msgstr "Surt" @@ -1232,6 +1274,19 @@ msgctxt "title" msgid "Upload Media" msgstr "Pujada de mèdia" +#: pkg/legal/admin.go:255 pkg/app/user.go:249 pkg/campsite/types/l10n.go:87 +#: pkg/campsite/types/l10n.go:144 pkg/campsite/types/l10n.go:268 +#: pkg/campsite/types/option.go:350 pkg/campsite/types/feature.go:253 +#: pkg/campsite/types/admin.go:447 pkg/season/l10n.go:69 +#: pkg/season/admin.go:405 pkg/services/l10n.go:73 pkg/services/admin.go:266 +msgid "Name can not be empty." +msgstr "No podeu deixar el nom en blanc." + +#: pkg/legal/admin.go:256 pkg/campsite/types/option.go:351 +#: pkg/campsite/types/feature.go:254 pkg/campsite/types/admin.go:448 +msgid "Name must have at least one letter." +msgstr "El nom ha de tenir com a mínim una lletra." + #: pkg/carousel/admin.go:285 pkg/campsite/types/carousel.go:242 msgctxt "input" msgid "Slide image" @@ -1273,14 +1328,6 @@ msgctxt "language option" msgid "Automatic" msgstr "Automàtic" -#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:87 -#: pkg/campsite/types/l10n.go:144 pkg/campsite/types/l10n.go:268 -#: pkg/campsite/types/option.go:350 pkg/campsite/types/feature.go:253 -#: pkg/campsite/types/admin.go:447 pkg/season/l10n.go:69 -#: pkg/season/admin.go:404 pkg/services/l10n.go:73 pkg/services/admin.go:266 -msgid "Name can not be empty." -msgstr "No podeu deixar el nom en blanc." - #: pkg/app/user.go:250 msgid "Confirmation does not match password." msgstr "La confirmació no es correspon amb la contrasenya." @@ -1293,15 +1340,10 @@ msgstr "L’idioma escollit no és vàlid." 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:59 +#: pkg/app/admin.go:62 msgid "Access forbidden" msgstr "Accés prohibit" -#: pkg/campsite/types/option.go:351 pkg/campsite/types/feature.go:254 -#: pkg/campsite/types/admin.go:448 -msgid "Name must have at least one letter." -msgstr "El nom ha de tenir com a mínim una lletra." - #: pkg/campsite/types/option.go:354 msgid "Minimum can not be empty." msgstr "No podeu deixar el mínim en blanc." @@ -1457,32 +1499,32 @@ msgctxt "month" msgid "December" msgstr "desembre" -#: pkg/season/admin.go:405 +#: pkg/season/admin.go:406 msgid "Color can not be empty." msgstr "No podeu deixar el color en blanc." -#: pkg/season/admin.go:406 +#: pkg/season/admin.go:407 msgid "This color is not valid. It must be like #123abc." msgstr "Aquest color no és vàlid. Hauria de ser similar a #123abc." -#: pkg/season/admin.go:506 +#: pkg/season/admin.go:507 msgctxt "action" msgid "Unset" msgstr "Desassigna" -#: pkg/season/admin.go:537 +#: pkg/season/admin.go:538 msgid "Start date can not be empty." msgstr "No podeu deixar la data d’inici en blanc." -#: pkg/season/admin.go:538 +#: pkg/season/admin.go:539 msgid "Start date must be a valid date." msgstr "La data d’inici ha de ser una data vàlida." -#: pkg/season/admin.go:540 +#: pkg/season/admin.go:541 msgid "End date can not be empty." msgstr "No podeu deixar la data de fi en blanc." -#: pkg/season/admin.go:541 +#: pkg/season/admin.go:542 msgid "End date must be a valid date." msgstr "La data de fi ha de ser una data vàlida." @@ -1666,10 +1708,6 @@ msgstr "El valor de %s ha de ser com a mínim %d." msgid "%s must be at most %d." msgstr "El valor de %s ha de ser com a màxim %d." -#~ msgctxt "title" -#~ msgid "Contact" -#~ msgstr "Contacte" - #~ msgctxt "title" #~ msgid "Party Details" #~ msgstr "Dades dels visitants" diff --git a/po/es.po b/po/es.po index 727c026..c804ec3 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-12-21 21:08+0100\n" +"POT-Creation-Date: 2023-12-22 02:19+0100\n" "PO-Revision-Date: 2023-07-22 23:46+0200\n" "Last-Translator: jordi fita mas \n" "Language-Team: Spanish \n" @@ -43,7 +43,7 @@ msgstr "Pago fallido" #: web/templates/public/services.gohtml:6 #: web/templates/public/services.gohtml:15 -#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79 +#: web/templates/public/layout.gohtml:66 web/templates/public/layout.gohtml:94 #: web/templates/admin/services/index.gohtml:66 msgctxt "title" msgid "Services" @@ -55,14 +55,14 @@ msgstr "El camping dispone de varios servicios." #: web/templates/public/location.gohtml:6 #: web/templates/public/location.gohtml:12 -#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:81 +#: web/templates/public/layout.gohtml:68 web/templates/public/layout.gohtml:96 #: web/templates/admin/layout.gohtml:60 msgctxt "title" msgid "Location" msgstr "Cómo llegar" -#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:37 -#: web/templates/public/layout.gohtml:77 +#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:52 +#: web/templates/public/layout.gohtml:92 msgctxt "title" msgid "Home" msgstr "Inicio" @@ -87,7 +87,7 @@ msgstr "¡Ven a disfrutar!" #: web/templates/public/home.gohtml:35 #: web/templates/public/surroundings.gohtml:6 #: web/templates/public/surroundings.gohtml:10 -#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80 +#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95 msgctxt "title" msgid "Surroundings" msgstr "El entorno" @@ -271,13 +271,13 @@ msgstr "Hay diversos puntos dónde podéis ir en kayak, desde tramos del río Te #: web/templates/public/campground.gohtml:6 #: web/templates/public/campground.gohtml:11 -#: web/templates/public/layout.gohtml:38 web/templates/public/layout.gohtml:78 +#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:93 msgctxt "title" msgid "Campground" msgstr "El camping" #: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11 -#: web/templates/public/layout.gohtml:54 +#: web/templates/public/layout.gohtml:69 msgctxt "title" msgid "Booking" msgstr "Reserva" @@ -365,8 +365,8 @@ msgctxt "input" msgid "I have read and I accept the reservation conditions" msgstr "He leído y acepto las condiciones de reserva" -#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:32 -#: web/templates/public/layout.gohtml:109 +#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:47 +#: web/templates/public/layout.gohtml:130 msgid "Campsite Montagut" msgstr "Camping Montagut" @@ -374,28 +374,118 @@ msgstr "Camping Montagut" msgid "Skip to main content" msgstr "Saltar al contenido principal" -#: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88 +#: web/templates/public/layout.gohtml:57 web/templates/public/layout.gohtml:103 #: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:12 -#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79 +#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:82 msgctxt "title" msgid "Campsites" msgstr "Alojamientos" -#: web/templates/public/layout.gohtml:75 +#: web/templates/public/layout.gohtml:90 msgctxt "title" msgid "Sections" msgstr "Apartados" -#: web/templates/public/layout.gohtml:99 +#: web/templates/public/layout.gohtml:114 msgctxt "title" msgid "Opening" msgstr "Apertura" -#: web/templates/public/layout.gohtml:106 +#: web/templates/public/layout.gohtml:121 msgid "RTC #%s" msgstr " RTC %s" +#: web/templates/admin/legal/form.gohtml:8 +#: web/templates/admin/legal/form.gohtml:25 +msgctxt "title" +msgid "Edit Legal Text" +msgstr "Edición del texto legal" + +#: web/templates/admin/legal/form.gohtml:10 +#: web/templates/admin/legal/form.gohtml:27 +msgctxt "title" +msgid "New Legal Text" +msgstr "Nuevo texto legal" + +#: web/templates/admin/legal/form.gohtml:37 +msgctxt "input" +msgid "Slug" +msgstr "Álias" + +#: web/templates/admin/legal/form.gohtml:46 +#: web/templates/admin/campsite/feature/form.gohtml:52 +#: web/templates/admin/campsite/feature/l10n.gohtml:20 +#: web/templates/admin/campsite/option/form.gohtml:34 +#: web/templates/admin/campsite/option/l10n.gohtml:20 +#: web/templates/admin/campsite/type/form.gohtml:46 +#: web/templates/admin/campsite/type/l10n.gohtml:20 +#: web/templates/admin/season/form.gohtml:46 +#: web/templates/admin/season/l10n.gohtml:20 +#: web/templates/admin/services/form.gohtml:52 +#: web/templates/admin/services/l10n.gohtml:20 +#: web/templates/admin/profile.gohtml:26 +msgctxt "input" +msgid "Name" +msgstr "Nombre" + +#: web/templates/admin/legal/form.gohtml:64 +msgctxt "input" +msgid "Content" +msgstr "Contenido" + +#: web/templates/admin/legal/form.gohtml:84 +#: web/templates/admin/carousel/form.gohtml:47 +#: web/templates/admin/campsite/feature/form.gohtml:62 +#: web/templates/admin/campsite/carousel/form.gohtml:47 +#: web/templates/admin/campsite/form.gohtml:70 +#: web/templates/admin/campsite/option/form.gohtml:78 +#: web/templates/admin/campsite/type/form.gohtml:129 +#: web/templates/admin/season/form.gohtml:64 +#: web/templates/admin/services/form.gohtml:69 +#: web/templates/admin/media/form.gohtml:35 +msgctxt "action" +msgid "Update" +msgstr "Actualizar" + +#: web/templates/admin/legal/form.gohtml:86 +#: web/templates/admin/carousel/form.gohtml:49 +#: web/templates/admin/campsite/feature/form.gohtml:64 +#: web/templates/admin/campsite/carousel/form.gohtml:49 +#: web/templates/admin/campsite/form.gohtml:72 +#: web/templates/admin/campsite/option/form.gohtml:80 +#: web/templates/admin/campsite/type/form.gohtml:131 +#: web/templates/admin/season/form.gohtml:66 +#: web/templates/admin/services/form.gohtml:71 +msgctxt "action" +msgid "Add" +msgstr "Añadir" + +#: web/templates/admin/legal/index.gohtml:6 +#: web/templates/admin/legal/index.gohtml:12 +#: web/templates/admin/layout.gohtml:63 +msgctxt "title" +msgid "Legal Texts" +msgstr "Textos legales" + +#: web/templates/admin/legal/index.gohtml:11 +msgctxt "action" +msgid "Add Legal Text" +msgstr "Añadir texto legal" + +#: web/templates/admin/legal/index.gohtml:17 +#: web/templates/admin/campsite/feature/index.gohtml:26 +#: web/templates/admin/campsite/option/index.gohtml:25 +#: web/templates/admin/campsite/type/index.gohtml:25 +#: web/templates/admin/season/index.gohtml:26 +msgctxt "header" +msgid "Name" +msgstr "Nombre" + +#: web/templates/admin/legal/index.gohtml:29 +msgid "No legal texts added yet." +msgstr "No se ha añadido ningún texto legal todavía." + #: web/templates/admin/carousel/form.gohtml:8 #: web/templates/admin/carousel/form.gohtml:25 msgctxt "title" @@ -416,31 +506,6 @@ msgctxt "input" msgid "Caption" msgstr "Leyenda" -#: web/templates/admin/carousel/form.gohtml:47 -#: web/templates/admin/campsite/feature/form.gohtml:62 -#: web/templates/admin/campsite/carousel/form.gohtml:47 -#: web/templates/admin/campsite/form.gohtml:70 -#: web/templates/admin/campsite/option/form.gohtml:78 -#: web/templates/admin/campsite/type/form.gohtml:129 -#: web/templates/admin/season/form.gohtml:64 -#: web/templates/admin/services/form.gohtml:69 -#: web/templates/admin/media/form.gohtml:35 -msgctxt "action" -msgid "Update" -msgstr "Actualizar" - -#: web/templates/admin/carousel/form.gohtml:49 -#: web/templates/admin/campsite/feature/form.gohtml:64 -#: web/templates/admin/campsite/carousel/form.gohtml:49 -#: web/templates/admin/campsite/form.gohtml:72 -#: web/templates/admin/campsite/option/form.gohtml:80 -#: web/templates/admin/campsite/type/form.gohtml:131 -#: web/templates/admin/season/form.gohtml:66 -#: web/templates/admin/services/form.gohtml:71 -msgctxt "action" -msgid "Add" -msgstr "Añadir" - #: web/templates/admin/carousel/l10n.gohtml:7 #: web/templates/admin/carousel/l10n.gohtml:14 msgctxt "title" @@ -534,21 +599,6 @@ msgctxt "input" msgid "Icon" msgstr "Icono" -#: web/templates/admin/campsite/feature/form.gohtml:52 -#: web/templates/admin/campsite/feature/l10n.gohtml:20 -#: web/templates/admin/campsite/option/form.gohtml:34 -#: web/templates/admin/campsite/option/l10n.gohtml:20 -#: web/templates/admin/campsite/type/form.gohtml:46 -#: web/templates/admin/campsite/type/l10n.gohtml:20 -#: web/templates/admin/season/form.gohtml:46 -#: web/templates/admin/season/l10n.gohtml:20 -#: web/templates/admin/services/form.gohtml:52 -#: web/templates/admin/services/l10n.gohtml:20 -#: web/templates/admin/profile.gohtml:26 -msgctxt "input" -msgid "Name" -msgstr "Nombre" - #: web/templates/admin/campsite/feature/index.gohtml:6 #: web/templates/admin/campsite/feature/index.gohtml:12 msgctxt "title" @@ -560,14 +610,6 @@ msgctxt "action" msgid "Add Feature" msgstr "Añadir características" -#: web/templates/admin/campsite/feature/index.gohtml:26 -#: web/templates/admin/campsite/option/index.gohtml:25 -#: web/templates/admin/campsite/type/index.gohtml:25 -#: web/templates/admin/season/index.gohtml:26 -msgctxt "header" -msgid "Name" -msgstr "Nombre" - #: web/templates/admin/campsite/feature/index.gohtml:27 #: web/templates/admin/campsite/carousel/index.gohtml:27 #: web/templates/admin/campsite/option/index.gohtml:26 @@ -979,7 +1021,7 @@ msgid "Integration" msgstr "Integración" #: web/templates/admin/dashboard.gohtml:6 -#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:79 msgctxt "title" msgid "Dashboard" msgstr "Panel" @@ -1162,7 +1204,7 @@ msgctxt "title" msgid "Home Page" msgstr "Página de inicio" -#: web/templates/admin/layout.gohtml:65 +#: web/templates/admin/layout.gohtml:68 msgctxt "action" msgid "Logout" msgstr "Salir" @@ -1232,6 +1274,19 @@ msgctxt "title" msgid "Upload Media" msgstr "Subida de medio" +#: pkg/legal/admin.go:255 pkg/app/user.go:249 pkg/campsite/types/l10n.go:87 +#: pkg/campsite/types/l10n.go:144 pkg/campsite/types/l10n.go:268 +#: pkg/campsite/types/option.go:350 pkg/campsite/types/feature.go:253 +#: pkg/campsite/types/admin.go:447 pkg/season/l10n.go:69 +#: pkg/season/admin.go:405 pkg/services/l10n.go:73 pkg/services/admin.go:266 +msgid "Name can not be empty." +msgstr "No podéis dejar el nombre en blanco." + +#: pkg/legal/admin.go:256 pkg/campsite/types/option.go:351 +#: pkg/campsite/types/feature.go:254 pkg/campsite/types/admin.go:448 +msgid "Name must have at least one letter." +msgstr "El nombre tiene que tener como mínimo una letra." + #: pkg/carousel/admin.go:285 pkg/campsite/types/carousel.go:242 msgctxt "input" msgid "Slide image" @@ -1273,14 +1328,6 @@ msgctxt "language option" msgid "Automatic" msgstr "Automático" -#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:87 -#: pkg/campsite/types/l10n.go:144 pkg/campsite/types/l10n.go:268 -#: pkg/campsite/types/option.go:350 pkg/campsite/types/feature.go:253 -#: pkg/campsite/types/admin.go:447 pkg/season/l10n.go:69 -#: pkg/season/admin.go:404 pkg/services/l10n.go:73 pkg/services/admin.go:266 -msgid "Name can not be empty." -msgstr "No podéis dejar el nombre en blanco." - #: pkg/app/user.go:250 msgid "Confirmation does not match password." msgstr "La confirmación no se corresponde con la contraseña." @@ -1293,15 +1340,10 @@ msgstr "El idioma escogido no es válido." 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:59 +#: pkg/app/admin.go:62 msgid "Access forbidden" msgstr "Acceso prohibido" -#: pkg/campsite/types/option.go:351 pkg/campsite/types/feature.go:254 -#: pkg/campsite/types/admin.go:448 -msgid "Name must have at least one letter." -msgstr "El nombre tiene que tener como mínimo una letra." - #: pkg/campsite/types/option.go:354 msgid "Minimum can not be empty." msgstr "No podéis dejar el mínimo en blanco." @@ -1457,32 +1499,32 @@ msgctxt "month" msgid "December" msgstr "diciembre" -#: pkg/season/admin.go:405 +#: pkg/season/admin.go:406 msgid "Color can not be empty." msgstr "No podéis dejar el color en blanco." -#: pkg/season/admin.go:406 +#: pkg/season/admin.go:407 msgid "This color is not valid. It must be like #123abc." msgstr "Este color no es válido. Tiene que ser parecido a #123abc." -#: pkg/season/admin.go:506 +#: pkg/season/admin.go:507 msgctxt "action" msgid "Unset" msgstr "Desasignar" -#: pkg/season/admin.go:537 +#: pkg/season/admin.go:538 msgid "Start date can not be empty." msgstr "No podéis dejar la fecha de inicio en blanco." -#: pkg/season/admin.go:538 +#: pkg/season/admin.go:539 msgid "Start date must be a valid date." msgstr "La fecha de inicio tiene que ser una fecha válida." -#: pkg/season/admin.go:540 +#: pkg/season/admin.go:541 msgid "End date can not be empty." msgstr "No podéis dejar la fecha final en blanco." -#: pkg/season/admin.go:541 +#: pkg/season/admin.go:542 msgid "End date must be a valid date." msgstr "La fecha final tiene que ser una fecha válida." @@ -1666,10 +1708,6 @@ msgstr "%s tiene que ser como mínimo %d." msgid "%s must be at most %d." msgstr "%s tiene que ser como máximo %d" -#~ msgctxt "title" -#~ msgid "Contact" -#~ msgstr "Contacto" - #~ msgctxt "title" #~ msgid "Party Details" #~ msgstr "Datos de los visitantes" diff --git a/po/fr.po b/po/fr.po index a6d5878..c74b2bb 100644 --- a/po/fr.po +++ b/po/fr.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: camper\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n" -"POT-Creation-Date: 2023-12-21 21:08+0100\n" +"POT-Creation-Date: 2023-12-22 02:19+0100\n" "PO-Revision-Date: 2023-12-20 10:13+0100\n" "Last-Translator: Oriol Carbonell \n" "Language-Team: French \n" @@ -44,7 +44,7 @@ msgstr "Le paiement a échoué" #: web/templates/public/services.gohtml:6 #: web/templates/public/services.gohtml:15 -#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79 +#: web/templates/public/layout.gohtml:66 web/templates/public/layout.gohtml:94 #: web/templates/admin/services/index.gohtml:66 msgctxt "title" msgid "Services" @@ -56,14 +56,14 @@ msgstr "Le camping propose de nombreux services différents." #: web/templates/public/location.gohtml:6 #: web/templates/public/location.gohtml:12 -#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:81 +#: web/templates/public/layout.gohtml:68 web/templates/public/layout.gohtml:96 #: web/templates/admin/layout.gohtml:60 msgctxt "title" msgid "Location" msgstr "Comment nous rejoindre" -#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:37 -#: web/templates/public/layout.gohtml:77 +#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:52 +#: web/templates/public/layout.gohtml:92 msgctxt "title" msgid "Home" msgstr "Accueil" @@ -88,7 +88,7 @@ msgstr "Venez et profitez-en !" #: web/templates/public/home.gohtml:35 #: web/templates/public/surroundings.gohtml:6 #: web/templates/public/surroundings.gohtml:10 -#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80 +#: web/templates/public/layout.gohtml:67 web/templates/public/layout.gohtml:95 msgctxt "title" msgid "Surroundings" msgstr "Entourage" @@ -272,13 +272,13 @@ msgstr "Il y a plusieurs points où vous pouvez aller en kayak, à partir de sec #: web/templates/public/campground.gohtml:6 #: web/templates/public/campground.gohtml:11 -#: web/templates/public/layout.gohtml:38 web/templates/public/layout.gohtml:78 +#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:93 msgctxt "title" msgid "Campground" msgstr "Camping" #: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11 -#: web/templates/public/layout.gohtml:54 +#: web/templates/public/layout.gohtml:69 msgctxt "title" msgid "Booking" msgstr "Reservation" @@ -366,8 +366,8 @@ msgctxt "input" msgid "I have read and I accept the reservation conditions" msgstr "J’ai lu et j’accepte les conditions de réservation" -#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:32 -#: web/templates/public/layout.gohtml:109 +#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:47 +#: web/templates/public/layout.gohtml:130 msgid "Campsite Montagut" msgstr "Camping Montagut" @@ -375,28 +375,120 @@ msgstr "Camping Montagut" msgid "Skip to main content" msgstr "Passer au contenu principal" -#: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88 +#: web/templates/public/layout.gohtml:57 web/templates/public/layout.gohtml:103 #: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:12 -#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79 +#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:82 msgctxt "title" msgid "Campsites" msgstr "Locatifs" -#: web/templates/public/layout.gohtml:75 +#: web/templates/public/layout.gohtml:90 msgctxt "title" msgid "Sections" msgstr "Sections" -#: web/templates/public/layout.gohtml:99 +#: web/templates/public/layout.gohtml:114 msgctxt "title" msgid "Opening" msgstr "Ouverture" -#: web/templates/public/layout.gohtml:106 +#: web/templates/public/layout.gohtml:121 msgid "RTC #%s" msgstr "# RTC %s" +#: web/templates/admin/legal/form.gohtml:8 +#: web/templates/admin/legal/form.gohtml:25 +msgctxt "title" +msgid "Edit Legal Text" +msgstr "" + +#: web/templates/admin/legal/form.gohtml:10 +#: web/templates/admin/legal/form.gohtml:27 +msgctxt "title" +msgid "New Legal Text" +msgstr "" + +#: web/templates/admin/legal/form.gohtml:37 +msgctxt "input" +msgid "Slug" +msgstr "" + +#: web/templates/admin/legal/form.gohtml:46 +#: web/templates/admin/campsite/feature/form.gohtml:52 +#: web/templates/admin/campsite/feature/l10n.gohtml:20 +#: web/templates/admin/campsite/option/form.gohtml:34 +#: web/templates/admin/campsite/option/l10n.gohtml:20 +#: web/templates/admin/campsite/type/form.gohtml:46 +#: web/templates/admin/campsite/type/l10n.gohtml:20 +#: web/templates/admin/season/form.gohtml:46 +#: web/templates/admin/season/l10n.gohtml:20 +#: web/templates/admin/services/form.gohtml:52 +#: web/templates/admin/services/l10n.gohtml:20 +#: web/templates/admin/profile.gohtml:26 +msgctxt "input" +msgid "Name" +msgstr "Nom" + +#: web/templates/admin/legal/form.gohtml:64 +#, fuzzy +msgctxt "input" +msgid "Content" +msgstr "Contact" + +#: web/templates/admin/legal/form.gohtml:84 +#: web/templates/admin/carousel/form.gohtml:47 +#: web/templates/admin/campsite/feature/form.gohtml:62 +#: web/templates/admin/campsite/carousel/form.gohtml:47 +#: web/templates/admin/campsite/form.gohtml:70 +#: web/templates/admin/campsite/option/form.gohtml:78 +#: web/templates/admin/campsite/type/form.gohtml:129 +#: web/templates/admin/season/form.gohtml:64 +#: web/templates/admin/services/form.gohtml:69 +#: web/templates/admin/media/form.gohtml:35 +msgctxt "action" +msgid "Update" +msgstr "Mettre à jour" + +#: web/templates/admin/legal/form.gohtml:86 +#: web/templates/admin/carousel/form.gohtml:49 +#: web/templates/admin/campsite/feature/form.gohtml:64 +#: web/templates/admin/campsite/carousel/form.gohtml:49 +#: web/templates/admin/campsite/form.gohtml:72 +#: web/templates/admin/campsite/option/form.gohtml:80 +#: web/templates/admin/campsite/type/form.gohtml:131 +#: web/templates/admin/season/form.gohtml:66 +#: web/templates/admin/services/form.gohtml:71 +msgctxt "action" +msgid "Add" +msgstr "Ajouter" + +#: web/templates/admin/legal/index.gohtml:6 +#: web/templates/admin/legal/index.gohtml:12 +#: web/templates/admin/layout.gohtml:63 +msgctxt "title" +msgid "Legal Texts" +msgstr "" + +#: web/templates/admin/legal/index.gohtml:11 +msgctxt "action" +msgid "Add Legal Text" +msgstr "" + +#: web/templates/admin/legal/index.gohtml:17 +#: web/templates/admin/campsite/feature/index.gohtml:26 +#: web/templates/admin/campsite/option/index.gohtml:25 +#: web/templates/admin/campsite/type/index.gohtml:25 +#: web/templates/admin/season/index.gohtml:26 +msgctxt "header" +msgid "Name" +msgstr "Nom" + +#: web/templates/admin/legal/index.gohtml:29 +#, fuzzy +msgid "No legal texts added yet." +msgstr "Aucune diapositive n’a encore été ajoutée." + #: web/templates/admin/carousel/form.gohtml:8 #: web/templates/admin/carousel/form.gohtml:25 msgctxt "title" @@ -417,31 +509,6 @@ msgctxt "input" msgid "Caption" msgstr "Légende" -#: web/templates/admin/carousel/form.gohtml:47 -#: web/templates/admin/campsite/feature/form.gohtml:62 -#: web/templates/admin/campsite/carousel/form.gohtml:47 -#: web/templates/admin/campsite/form.gohtml:70 -#: web/templates/admin/campsite/option/form.gohtml:78 -#: web/templates/admin/campsite/type/form.gohtml:129 -#: web/templates/admin/season/form.gohtml:64 -#: web/templates/admin/services/form.gohtml:69 -#: web/templates/admin/media/form.gohtml:35 -msgctxt "action" -msgid "Update" -msgstr "Mettre à jour" - -#: web/templates/admin/carousel/form.gohtml:49 -#: web/templates/admin/campsite/feature/form.gohtml:64 -#: web/templates/admin/campsite/carousel/form.gohtml:49 -#: web/templates/admin/campsite/form.gohtml:72 -#: web/templates/admin/campsite/option/form.gohtml:80 -#: web/templates/admin/campsite/type/form.gohtml:131 -#: web/templates/admin/season/form.gohtml:66 -#: web/templates/admin/services/form.gohtml:71 -msgctxt "action" -msgid "Add" -msgstr "Ajouter" - #: web/templates/admin/carousel/l10n.gohtml:7 #: web/templates/admin/carousel/l10n.gohtml:14 msgctxt "title" @@ -535,21 +602,6 @@ msgctxt "input" msgid "Icon" msgstr "Icône" -#: web/templates/admin/campsite/feature/form.gohtml:52 -#: web/templates/admin/campsite/feature/l10n.gohtml:20 -#: web/templates/admin/campsite/option/form.gohtml:34 -#: web/templates/admin/campsite/option/l10n.gohtml:20 -#: web/templates/admin/campsite/type/form.gohtml:46 -#: web/templates/admin/campsite/type/l10n.gohtml:20 -#: web/templates/admin/season/form.gohtml:46 -#: web/templates/admin/season/l10n.gohtml:20 -#: web/templates/admin/services/form.gohtml:52 -#: web/templates/admin/services/l10n.gohtml:20 -#: web/templates/admin/profile.gohtml:26 -msgctxt "input" -msgid "Name" -msgstr "Nom" - #: web/templates/admin/campsite/feature/index.gohtml:6 #: web/templates/admin/campsite/feature/index.gohtml:12 msgctxt "title" @@ -561,14 +613,6 @@ msgctxt "action" msgid "Add Feature" msgstr "Ajouter une fonctionnalité" -#: web/templates/admin/campsite/feature/index.gohtml:26 -#: web/templates/admin/campsite/option/index.gohtml:25 -#: web/templates/admin/campsite/type/index.gohtml:25 -#: web/templates/admin/season/index.gohtml:26 -msgctxt "header" -msgid "Name" -msgstr "Nom" - #: web/templates/admin/campsite/feature/index.gohtml:27 #: web/templates/admin/campsite/carousel/index.gohtml:27 #: web/templates/admin/campsite/option/index.gohtml:26 @@ -980,7 +1024,7 @@ msgid "Integration" msgstr "Intégration" #: web/templates/admin/dashboard.gohtml:6 -#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:79 msgctxt "title" msgid "Dashboard" msgstr "Tableau de bord" @@ -1163,7 +1207,7 @@ msgctxt "title" msgid "Home Page" msgstr "Page d'accueil" -#: web/templates/admin/layout.gohtml:65 +#: web/templates/admin/layout.gohtml:68 msgctxt "action" msgid "Logout" msgstr "Déconnexion" @@ -1233,6 +1277,19 @@ msgctxt "title" msgid "Upload Media" msgstr "Envoyer un fichier" +#: pkg/legal/admin.go:255 pkg/app/user.go:249 pkg/campsite/types/l10n.go:87 +#: pkg/campsite/types/l10n.go:144 pkg/campsite/types/l10n.go:268 +#: pkg/campsite/types/option.go:350 pkg/campsite/types/feature.go:253 +#: pkg/campsite/types/admin.go:447 pkg/season/l10n.go:69 +#: pkg/season/admin.go:405 pkg/services/l10n.go:73 pkg/services/admin.go:266 +msgid "Name can not be empty." +msgstr "Le nom ne peut pas être laissé vide." + +#: pkg/legal/admin.go:256 pkg/campsite/types/option.go:351 +#: pkg/campsite/types/feature.go:254 pkg/campsite/types/admin.go:448 +msgid "Name must have at least one letter." +msgstr "Le nom doit comporter au moins une lettre." + #: pkg/carousel/admin.go:285 pkg/campsite/types/carousel.go:242 msgctxt "input" msgid "Slide image" @@ -1274,14 +1331,6 @@ msgctxt "language option" msgid "Automatic" msgstr "Automatique" -#: pkg/app/user.go:249 pkg/campsite/types/l10n.go:87 -#: pkg/campsite/types/l10n.go:144 pkg/campsite/types/l10n.go:268 -#: pkg/campsite/types/option.go:350 pkg/campsite/types/feature.go:253 -#: pkg/campsite/types/admin.go:447 pkg/season/l10n.go:69 -#: pkg/season/admin.go:404 pkg/services/l10n.go:73 pkg/services/admin.go:266 -msgid "Name can not be empty." -msgstr "Le nom ne peut pas être laissé vide." - #: pkg/app/user.go:250 msgid "Confirmation does not match password." msgstr "La confirmation ne correspond pas au mot de passe." @@ -1294,15 +1343,10 @@ msgstr "La langue sélectionnée n’est pas valide." msgid "File must be a valid PNG or JPEG image." msgstr "Le fichier doit être une image PNG ou JPEG valide." -#: pkg/app/admin.go:59 +#: pkg/app/admin.go:62 msgid "Access forbidden" msgstr "Accès interdit" -#: pkg/campsite/types/option.go:351 pkg/campsite/types/feature.go:254 -#: pkg/campsite/types/admin.go:448 -msgid "Name must have at least one letter." -msgstr "Le nom doit comporter au moins une lettre." - #: pkg/campsite/types/option.go:354 msgid "Minimum can not be empty." msgstr "Le minimum ne peut pas être vide." @@ -1458,32 +1502,32 @@ msgctxt "month" msgid "December" msgstr "Décembre" -#: pkg/season/admin.go:405 +#: pkg/season/admin.go:406 msgid "Color can not be empty." msgstr "La couleur ne peut pas être vide." -#: pkg/season/admin.go:406 +#: pkg/season/admin.go:407 msgid "This color is not valid. It must be like #123abc." msgstr "Cette couleur n’est pas valide. Il doit être comme #123abc." -#: pkg/season/admin.go:506 +#: pkg/season/admin.go:507 msgctxt "action" msgid "Unset" msgstr "Unset" -#: pkg/season/admin.go:537 +#: pkg/season/admin.go:538 msgid "Start date can not be empty." msgstr "La date de début ne peut pas être vide." -#: pkg/season/admin.go:538 +#: pkg/season/admin.go:539 msgid "Start date must be a valid date." msgstr "La date de début doit être une date valide." -#: pkg/season/admin.go:540 +#: pkg/season/admin.go:541 msgid "End date can not be empty." msgstr "La date de fin ne peut pas être vide." -#: pkg/season/admin.go:541 +#: pkg/season/admin.go:542 msgid "End date must be a valid date." msgstr "La date de fin doit être une date valide." @@ -1666,7 +1710,3 @@ msgstr "%s doit être %d ou plus." #, c-format msgid "%s must be at most %d." msgstr "%s doit être tout au plus %d." - -#~ msgctxt "title" -#~ msgid "Contact" -#~ msgstr "Contact" diff --git a/revert/legal_text.sql b/revert/legal_text.sql new file mode 100644 index 0000000..7a13d82 --- /dev/null +++ b/revert/legal_text.sql @@ -0,0 +1,7 @@ +-- Revert camper:legal_text from pg + +begin; + +drop table if exists camper.legal_text; + +commit; diff --git a/revert/legal_text_i18n.sql b/revert/legal_text_i18n.sql new file mode 100644 index 0000000..9da6ad8 --- /dev/null +++ b/revert/legal_text_i18n.sql @@ -0,0 +1,7 @@ +-- Revert camper:legal_text_i18n from pg + +begin; + +drop table if exists camper.legal_text_i18n; + +commit; diff --git a/revert/translate_legal_text.sql b/revert/translate_legal_text.sql new file mode 100644 index 0000000..393a7d7 --- /dev/null +++ b/revert/translate_legal_text.sql @@ -0,0 +1,7 @@ +-- Revert camper:translate_legal_text from pg + +begin; + +drop function if exists camper.translate_legal_text(integer, text, text, text, text); + +commit; diff --git a/sqitch.plan b/sqitch.plan index 0a967f4..4f094b6 100644 --- a/sqitch.plan +++ b/sqitch.plan @@ -128,3 +128,6 @@ location [schema_camper roles company user_profile] 2023-12-21T17:01:28Z jordi f location_i18n [roles schema_camper location language] 2023-12-21T17:32:50Z jordi fita mas # Add relation for location internationalization translate_location [roles schema_camper location_i18n] 2023-12-21T17:37:47Z jordi fita mas # Add function to translate location setup_location [roles schema_camper location] 2023-12-21T19:26:53Z jordi fita mas # Add function to setup location settings +legal_text [roles schema_camper company user_profile] 2023-12-21T23:29:28Z jordi fita mas # Add relation for legal documents +legal_text_i18n [roles schema_camper legal_text language] 2023-12-21T23:51:09Z jordi fita mas # Add relation for legal text internationalization +translate_legal_text [roles schema_camper legal_text_i18n] 2023-12-22T00:10:05Z jordi fita mas # Add function to translate legal texts diff --git a/test/legal_text.sql b/test/legal_text.sql new file mode 100644 index 0000000..5ab598d --- /dev/null +++ b/test/legal_text.sql @@ -0,0 +1,198 @@ +-- Test legal_text +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(41); + +set search_path to camper, public; + +select has_table('legal_text'); +select has_pk('legal_text'); +select col_is_pk('legal_text', array['company_id', 'slug']); +select table_privs_are('legal_text', 'guest', array['SELECT']); +select table_privs_are('legal_text', 'employee', array['SELECT']); +select table_privs_are('legal_text', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('legal_text', 'authenticator', array[]::text[]); + +select has_column('legal_text', 'slug'); +select col_type_is('legal_text', 'slug', 'text'); +select col_not_null('legal_text', 'slug'); +select col_hasnt_default('legal_text', 'slug'); + +select has_column('legal_text', 'company_id'); +select col_is_fk('legal_text', 'company_id'); +select fk_ok('legal_text', 'company_id', 'company', 'company_id'); +select col_type_is('legal_text', 'company_id', 'integer'); +select col_not_null('legal_text', 'company_id'); +select col_hasnt_default('legal_text', 'company_id'); + +select has_column('legal_text', 'name'); +select col_type_is('legal_text', 'name', 'text'); +select col_not_null('legal_text', 'name'); +select col_hasnt_default('legal_text', 'name'); + +select has_column('legal_text', 'content'); +select col_type_is('legal_text', 'content', 'xml'); +select col_not_null('legal_text', 'content'); +select col_hasnt_default('legal_text', 'content'); + + +set client_min_messages to warning; +truncate legal_text cascade; +truncate company_host cascade; +truncate company_user cascade; +truncate company cascade; +truncate auth."user" cascade; +reset client_min_messages; + +insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at) +values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month') + , (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month') +; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'FR', 'USD', 'ca') +; + +insert into company_user (company_id, user_id, role) +values (2, 1, 'admin') + , (4, 5, 'admin') +; + +insert into company_host (company_id, host) +values (2, 'co2') + , (4, 'co4') +; + +insert into legal_text (company_id, slug, name, content) +values (2, 'reservation', 'Reservation', '') + , (4, 'cookies', 'Cookies', '') +; + +prepare legal_data as +select company_id, slug +from legal_text +order by company_id, slug; + +set role guest; +select bag_eq( + 'legal_data', + $$ values (2, 'reservation') + , (4, 'cookies') + $$, + 'Everyone should be able to list all legal texts across all companies' +); +reset role; + +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); + +select lives_ok( + $$ insert into legal_text(company_id, slug, name, content) values (2, 'tos', 'Terms of Service', '') $$, + 'Admin from company 2 should be able to insert a new legal text to that company.' +); + +select bag_eq( + 'legal_data', + $$ values (2, 'tos') + , (2, 'reservation') + , (4, 'cookies') + $$, + 'The new row should have been added' +); + +select lives_ok( + $$ update legal_text set slug = 'terms' where company_id = 2 and slug = 'tos' $$, + 'Admin from company 2 should be able to update legal text of that company.' +); + +select bag_eq( + 'legal_data', + $$ values (2, 'terms') + , (2, 'reservation') + , (4, 'cookies') + $$, + 'The row should have been updated.' +); + +select lives_ok( + $$ delete from legal_text where company_id = 2 and slug = 'terms' $$, + 'Admin from company 2 should be able to delete legal text from that company.' +); + +select bag_eq( + 'legal_data', + $$ values (2, 'reservation') + , (4, 'cookies') + $$, + 'The row should have been deleted.' +); + +select throws_ok( + $$ insert into legal_text (company_id, slug, name, content) values (4, 'terms', 'Terms', '') $$, + '42501', 'new row violates row-level security policy for table "legal_text"', + 'Admin from company 2 should NOT be able to insert new legal texts to company 4.' +); + +select lives_ok( + $$ update legal_text set slug = 'nope' where company_id = 4 $$, + 'Admin from company 2 should not be able to update new legal texts of company 4, but no error if company_id is not changed.' +); + +select bag_eq( + 'legal_data', + $$ values (2, 'reservation') + , (4, 'cookies') + $$, + 'No row should have been changed.' +); + +select throws_ok( + $$ update legal_text set company_id = 4 where company_id = 2 $$, + '42501', 'new row violates row-level security policy for table "legal_text"', + 'Admin from company 2 should NOT be able to move legal texts to company 4' +); + +select lives_ok( + $$ delete from legal_text where company_id = 4 $$, + 'Admin from company 2 should NOT be able to delete legal texts from company 4, but not error is thrown' +); + +select bag_eq( + 'legal_data', + $$ values (2, 'reservation') + , (4, 'cookies') + $$, + 'No row should have been changed' +); + +select throws_ok( + $$ insert into legal_text (company_id, slug, name, content) values (2, 'ToS', 'Term of Services', '') $$, + '23514', 'new row for relation "legal_text" violates check constraint "valid_slug"', + 'Should not be able to insert legal texts with a invalid slug.' +); + +select throws_ok( + $$ insert into legal_text (company_id, slug, name, content) values (2, 'terms of service', 'Term of Services', '') $$, + '23514', 'new row for relation "legal_text" violates check constraint "valid_slug"', + 'Should not be able to insert legal texts with a spaces in the slug.' +); + +select throws_ok( + $$ insert into legal_text (company_id, slug, name, content) values (2, 'tos', ' ', '') $$, + '23514', 'new row for relation "legal_text" violates check constraint "name_not_empty"', + 'Should not be able to insert legal texts with a blank name.' +); + + +reset role; + + +select * +from finish(); + +rollback; + diff --git a/test/legal_text_i18n.sql b/test/legal_text_i18n.sql new file mode 100644 index 0000000..51dd371 --- /dev/null +++ b/test/legal_text_i18n.sql @@ -0,0 +1,54 @@ +-- Test legal_text_i18n +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(31); + +set search_path to camper, public; + +select has_table('legal_text_i18n'); +select has_pk('legal_text_i18n'); +select col_is_pk('legal_text_i18n', array['company_id', 'slug', 'lang_tag']); +select col_is_fk('legal_text_i18n', array['company_id', 'slug']); +select fk_ok('legal_text_i18n', array['company_id', 'slug'], 'legal_text', array['company_id', 'slug']); +select table_privs_are('legal_text_i18n', 'guest', array['SELECT']); +select table_privs_are('legal_text_i18n', 'employee', array['SELECT']); +select table_privs_are('legal_text_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('legal_text_i18n', 'authenticator', array[]::text[]); + +select has_column('legal_text_i18n', 'company_id'); +select col_type_is('legal_text_i18n', 'company_id', 'integer'); +select col_not_null('legal_text_i18n', 'company_id'); +select col_hasnt_default('legal_text_i18n', 'company_id'); + +select has_column('legal_text_i18n', 'slug'); +select col_type_is('legal_text_i18n', 'slug', 'text'); +select col_not_null('legal_text_i18n', 'slug'); +select col_hasnt_default('legal_text_i18n', 'slug'); + +select has_column('legal_text_i18n', 'lang_tag'); +select col_is_fk('legal_text_i18n', 'lang_tag'); +select fk_ok('legal_text_i18n', 'lang_tag', 'language', 'lang_tag'); +select col_type_is('legal_text_i18n', 'lang_tag', 'text'); +select col_not_null('legal_text_i18n', 'lang_tag'); +select col_hasnt_default('legal_text_i18n', 'lang_tag'); + +select has_column('legal_text_i18n', 'name'); +select col_type_is('legal_text_i18n', 'name', 'text'); +select col_not_null('legal_text_i18n', 'name'); +select col_hasnt_default('legal_text_i18n', 'name'); + +select has_column('legal_text_i18n', 'content'); +select col_type_is('legal_text_i18n', 'content', 'xml'); +select col_not_null('legal_text_i18n', 'content'); +select col_hasnt_default('legal_text_i18n', 'content'); + + +select * +from finish(); + +rollback; + diff --git a/test/translate_legal_text.sql b/test/translate_legal_text.sql new file mode 100644 index 0000000..3e8dabe --- /dev/null +++ b/test/translate_legal_text.sql @@ -0,0 +1,72 @@ +-- Test translate_legal_text +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(13); + +set search_path to camper, public; + +select has_function('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text']); +select function_lang_is('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text'], 'sql'); +select function_returns('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text'], 'void'); +select isnt_definer('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text']); +select volatility_is('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'translate_legal_text', array['integer', 'text', 'text', 'text', 'text'], 'authenticator', array[]::text[]); + +set client_min_messages to warning; +truncate legal_text_i18n cascade; +truncate legal_text cascade; +truncate company cascade; +reset client_min_messages; + +insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, country_code, currency_code, default_lang_tag) +values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'FR', 'USD', 'ca') +; + +insert into legal_text (company_id, slug, name, content) +values (2, 'tos', 'Terms of Service', '

Go away

') + , (4, 'cookies', 'Cookies', '

Yummy

') +; + +insert into legal_text_i18n (company_id, slug, lang_tag, name, content) +values (2, 'tos', 'ca', '

Termes i condicions

', '

Uh?

') + , (4, 'cookies', 'ca', '

Galetes

', '

Uh?

') +; + +select lives_ok( + $$ select translate_legal_text(2, 'tos', 'es', 'Términos', '

Adiós

') $$, + 'Should be able to translate the legal text of the first company to a new language' +); + +select lives_ok( + $$ select translate_legal_text(2, 'tos', 'ca', 'Termes', '

Adéu

') $$, + 'Should be able to overwrite a legal text’s translation' +); + +select lives_ok( + $$ select translate_legal_text(4, 'cookies', 'ca', null, null) $$, + 'Should be able to “translate” a legal_text to empty strings' +); + + +select bag_eq( + $$ select company_id, slug, lang_tag, name, content::text from legal_text_i18n $$, + $$ values (2, 'tos', 'ca', 'Termes', '

Adéu

') + , (2, 'tos', 'es', 'Términos', '

Adiós

') + , (4, 'cookies', 'ca', '', '') + $$, + 'Should have translated all legal texts' +); + + +select * +from finish(); + +rollback; diff --git a/verify/legal_text.sql b/verify/legal_text.sql new file mode 100644 index 0000000..bc6186a --- /dev/null +++ b/verify/legal_text.sql @@ -0,0 +1,18 @@ +-- Verify camper:legal_text on pg + +begin; + +select company_id + , slug + , name + , content +from camper.legal_text +where false; + +select 1 / count(*) from pg_class where oid = 'camper.legal_text'::regclass and relrowsecurity; +select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.legal_text'::regclass; +select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.legal_text'::regclass; +select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.legal_text'::regclass; +select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.legal_text'::regclass; + +rollback; diff --git a/verify/legal_text_i18n.sql b/verify/legal_text_i18n.sql new file mode 100644 index 0000000..1276148 --- /dev/null +++ b/verify/legal_text_i18n.sql @@ -0,0 +1,13 @@ +-- Verify camper:legal_text_i18n on pg + +begin; + +select company_id + , slug + , lang_tag + , name + , content +from camper.legal_text_i18n +where false; + +rollback; diff --git a/verify/translate_legal_text.sql b/verify/translate_legal_text.sql new file mode 100644 index 0000000..a6fd5a1 --- /dev/null +++ b/verify/translate_legal_text.sql @@ -0,0 +1,7 @@ +-- Verify camper:translate_legal_text on pg + +begin; + +select has_function_privilege('camper.translate_legal_text(integer, text, text, text, text)', 'execute'); + +rollback; diff --git a/web/templates/admin/layout.gohtml b/web/templates/admin/layout.gohtml index c3252ea..da3e3a2 100644 --- a/web/templates/admin/layout.gohtml +++ b/web/templates/admin/layout.gohtml @@ -59,6 +59,9 @@
  • {{( pgettext "Location" "title" )}}
  • +
  • + {{( pgettext "Legal Texts" "title" )}} +
  • {{- end }}
  • + {{- end }} + + {{ range $lang, $input := . -}} + + {{- end }} + {{ template "error-message" . }} + + {{- end }} + {{ with .Content -}} +
    + {{( pgettext "Content" "input" )}} + + {{ range $lang, $input := . -}} + + {{- end }} + {{ template "error-message" . }} +
    + {{- end }} + +
    + +
    + +{{- end }} diff --git a/web/templates/admin/legal/index.gohtml b/web/templates/admin/legal/index.gohtml new file mode 100644 index 0000000..6f9da92 --- /dev/null +++ b/web/templates/admin/legal/index.gohtml @@ -0,0 +1,31 @@ + +{{ define "title" -}} + {{( pgettext "Legal Texts" "title" )}} +{{- end }} + +{{ define "content" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/legal.legalIndex*/ -}} + {{( pgettext "Add Legal Text" "action" )}} +

    {{( pgettext "Legal Texts" "title" )}}

    + {{ if .Texts -}} + + + + + + + + {{ range $text := .Texts -}} + + + + {{- end }} + +
    {{( pgettext "Name" "header" )}}
    {{ .Name }}
    + {{ else -}} +

    {{( gettext "No legal texts added yet." )}}

    + {{- end }} +{{- end }} diff --git a/web/templates/public/legal.gohtml b/web/templates/public/legal.gohtml new file mode 100644 index 0000000..d165b65 --- /dev/null +++ b/web/templates/public/legal.gohtml @@ -0,0 +1,17 @@ + +{{ define "title" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/legal.legalPage*/ -}} + {{ .Name }} +{{- end }} + +{{ define "content" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/legal.legalPage*/ -}} + +

    {{ .Name }}

    + +{{- end }}