From ff6e9497b55029af54532bc56bc37c69b4f2c987 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Thu, 21 Dec 2023 21:17:04 +0100 Subject: [PATCH] Replace contact page with location Customer does not want a contact page, but a page where they can write the direction on how to reach the campground, with a Google map embed instead of using Leaflet, because Google Maps shows the reviews right in the map. That means i had to replace the GPS locations with XML fields for the customer to write. In all four languages. This time i tried a translation approach inspired by PrestaShop: instead of opening a new page for each language, i have all languages in the same page and use AlpineJS to show just a single language. It is far easier to write the translations, even though you do not have the source text visible, specially in this section that there is no place for me to put the language links. --- demo/demo.sql | 4 - deploy/extension_postgis.sql | 8 - .../{company_geography.sql => location.sql} | 28 +- deploy/location_i18n.sql | 23 + deploy/setup_location.sql | 26 + deploy/translate_location.sql | 25 + pkg/app/admin.go | 5 + pkg/app/public.go | 57 +- pkg/auth/company.go | 9 +- pkg/database/funcs.go | 10 + pkg/form/input.go | 19 + pkg/location/admin.go | 156 +++++ pkg/location/public.go | 67 ++ pkg/template/page.go | 37 +- po/ca.po | 120 ++-- po/es.po | 120 ++-- po/fr.po | 120 ++-- revert/company_geography.sql | 7 - revert/extension_postgis.sql | 7 - revert/location.sql | 7 + revert/location_i18n.sql | 7 + revert/setup_location.sql | 7 + revert/translate_location.sql | 7 + sqitch.plan | 6 +- test/company_geography.sql | 168 ----- test/extensions.sql | 1 - test/location.sql | 178 +++++ test/location_i18n.sql | 49 ++ test/setup_location.sql | 76 ++ test/translate_location.sql | 72 ++ verify/company_geography.sql | 16 - verify/extension_postgis.sql | 10 - verify/location.sql | 18 + verify/location_i18n.sql | 12 + verify/setup_location.sql | 7 + verify/translate_location.sql | 7 + web/static/alpinejs@3.13.3.min.js | 5 + web/static/camper.css | 34 +- web/static/leaflet@1.9.4/images/layers-2x.png | Bin 1259 -> 0 bytes web/static/leaflet@1.9.4/images/layers.png | Bin 696 -> 0 bytes .../leaflet@1.9.4/images/marker-icon-2x.png | Bin 2464 -> 0 bytes .../leaflet@1.9.4/images/marker-icon.png | Bin 1466 -> 0 bytes .../leaflet@1.9.4/images/marker-shadow.png | Bin 618 -> 0 bytes web/static/leaflet@1.9.4/leaflet.css | 661 ------------------ web/static/leaflet@1.9.4/leaflet.js | 6 - web/static/public.css | 4 +- web/templates/admin/layout.gohtml | 4 + web/templates/admin/location.gohtml | 63 ++ web/templates/public/contact.gohtml | 62 -- web/templates/public/layout.gohtml | 9 +- web/templates/public/location.gohtml | 21 + 51 files changed, 1187 insertions(+), 1178 deletions(-) delete mode 100644 deploy/extension_postgis.sql rename deploy/{company_geography.sql => location.sql} (54%) create mode 100644 deploy/location_i18n.sql create mode 100644 deploy/setup_location.sql create mode 100644 deploy/translate_location.sql create mode 100644 pkg/location/admin.go create mode 100644 pkg/location/public.go delete mode 100644 revert/company_geography.sql delete mode 100644 revert/extension_postgis.sql create mode 100644 revert/location.sql create mode 100644 revert/location_i18n.sql create mode 100644 revert/setup_location.sql create mode 100644 revert/translate_location.sql delete mode 100644 test/company_geography.sql create mode 100644 test/location.sql create mode 100644 test/location_i18n.sql create mode 100644 test/setup_location.sql create mode 100644 test/translate_location.sql delete mode 100644 verify/company_geography.sql delete mode 100644 verify/extension_postgis.sql create mode 100644 verify/location.sql create mode 100644 verify/location_i18n.sql create mode 100644 verify/setup_location.sql create mode 100644 verify/translate_location.sql create mode 100644 web/static/alpinejs@3.13.3.min.js delete mode 100644 web/static/leaflet@1.9.4/images/layers-2x.png delete mode 100644 web/static/leaflet@1.9.4/images/layers.png delete mode 100644 web/static/leaflet@1.9.4/images/marker-icon-2x.png delete mode 100644 web/static/leaflet@1.9.4/images/marker-icon.png delete mode 100644 web/static/leaflet@1.9.4/images/marker-shadow.png delete mode 100644 web/static/leaflet@1.9.4/leaflet.css delete mode 100644 web/static/leaflet@1.9.4/leaflet.js create mode 100644 web/templates/admin/location.gohtml delete mode 100644 web/templates/public/contact.gohtml create mode 100644 web/templates/public/location.gohtml diff --git a/demo/demo.sql b/demo/demo.sql index 12c4624..2f702f6 100644 --- a/demo/demo.sql +++ b/demo/demo.sql @@ -18,10 +18,6 @@ values (52, 'localhost:8080') , (52, 'camper.tandem.ws') ; -insert into company_geography (company_id, geog) -values (52, 'SRID=4326;POINT(2.598825853208973 42.24256146290889)') -; - insert into company_user (company_id, user_id, role) values (52, 42, 'employee') , (52, 43, 'admin') diff --git a/deploy/extension_postgis.sql b/deploy/extension_postgis.sql deleted file mode 100644 index e6e3d95..0000000 --- a/deploy/extension_postgis.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Deploy camper:extension_postgis to pg --- requires: schema_public - -begin; - -create extension if not exists postgis; - -commit; diff --git a/deploy/company_geography.sql b/deploy/location.sql similarity index 54% rename from deploy/company_geography.sql rename to deploy/location.sql index 86b9a56..c513677 100644 --- a/deploy/company_geography.sql +++ b/deploy/location.sql @@ -1,33 +1,33 @@ --- Deploy camper:company_geography to pg --- requires: roles +-- Deploy camper:location to pg -- requires: schema_camper +-- requires: roles -- requires: company --- requires: user_profile --- requires: extension_postgis begin; set search_path to camper, public; -create table company_geography ( +create table location ( company_id integer primary key references company, - geog geography not null + directions xml not null, + map_embed xml not null, + opening_dates xml not null ); -grant select on table company_geography to guest; -grant select on table company_geography to employee; -grant select, insert, update, delete on table company_geography to admin; +grant select on table location to guest; +grant select on table location to employee; +grant select, insert, update, delete on table location to admin; -alter table company_geography enable row level security; +alter table location enable row level security; create policy guest_ok -on company_geography +on location for select using (true) ; create policy insert_to_company -on company_geography +on location for insert with check ( company_id in (select company_id from user_profile) @@ -35,7 +35,7 @@ with check ( ; create policy update_company -on company_geography +on location for update using ( company_id in (select company_id from user_profile) @@ -43,7 +43,7 @@ using ( ; create policy delete_from_company -on company_geography +on location for delete using ( company_id in (select company_id from user_profile) diff --git a/deploy/location_i18n.sql b/deploy/location_i18n.sql new file mode 100644 index 0000000..9e2af51 --- /dev/null +++ b/deploy/location_i18n.sql @@ -0,0 +1,23 @@ +-- Deploy camper:location_i18n to pg +-- requires: roles +-- requires: schema_camper +-- requires: location +-- requires: language + +begin; + +set search_path to camper, public; + +create table location_i18n ( + company_id integer not null references location, + lang_tag text not null references language, + directions xml not null, + opening_dates xml not null, + primary key (company_id, lang_tag) +); + +grant select on table location_i18n to guest; +grant select on table location_i18n to employee; +grant select, insert, update, delete on table location_i18n to admin; + +commit; diff --git a/deploy/setup_location.sql b/deploy/setup_location.sql new file mode 100644 index 0000000..e4e1cb9 --- /dev/null +++ b/deploy/setup_location.sql @@ -0,0 +1,26 @@ +-- Deploy camper:setup_location to pg +-- requires: roles +-- requires: schema_camper +-- requires: location + +begin; + +set search_path to camper, public; + +create or replace function setup_location(company integer, directions text, map_embed text, opening_dates text) returns void as +$$ + insert into location (company_id, directions, map_embed, opening_dates) + values (company, xmlparse(content directions), xmlparse(content map_embed), xmlparse(content opening_dates)) + on conflict (company_id) do update + set directions = excluded.directions + , map_embed = excluded.map_embed + , opening_dates = excluded.opening_dates + ; +$$ + language sql +; + +revoke execute on function setup_location(integer, text, text, text) from public; +grant execute on function setup_location(integer, text, text, text) to admin; + +commit; diff --git a/deploy/translate_location.sql b/deploy/translate_location.sql new file mode 100644 index 0000000..88c731d --- /dev/null +++ b/deploy/translate_location.sql @@ -0,0 +1,25 @@ +-- Deploy camper:translate_location to pg +-- requires: roles +-- requires: schema_camper +-- requires: location_i18n + +begin; + +set search_path to camper, public; + +create or replace function translate_location(company_id integer, lang_tag text, directions text, opening_dates text) returns void as +$$ + insert into location_i18n (company_id, lang_tag, directions, opening_dates) + values (company_id, lang_tag, xmlparse(content coalesce(directions, '')), xmlparse(content coalesce(opening_dates, ''))) + on conflict (company_id, lang_tag) do update + set directions = excluded.directions + , opening_dates = excluded.opening_dates + ; +$$ + language sql +; + +revoke execute on function translate_location(integer, text, text, text) from public; +grant execute on function translate_location(integer, text, text, text) to admin; + +commit; diff --git a/pkg/app/admin.go b/pkg/app/admin.go index a84fdc6..a89d9bd 100644 --- a/pkg/app/admin.go +++ b/pkg/app/admin.go @@ -6,6 +6,7 @@ package app import ( + "dev.tandem.ws/tandem/camper/pkg/location" "net/http" "dev.tandem.ws/tandem/camper/pkg/auth" @@ -26,6 +27,7 @@ type adminHandler struct { campsite *campsite.AdminHandler company *company.AdminHandler home *home.AdminHandler + location *location.AdminHandler media *media.AdminHandler payment *booking.AdminHandler season *season.AdminHandler @@ -37,6 +39,7 @@ func newAdminHandler(locales locale.Locales, mediaDir string) *adminHandler { campsite: campsite.NewAdminHandler(locales), company: company.NewAdminHandler(), home: home.NewAdminHandler(locales), + location: location.NewAdminHandler(), media: media.NewAdminHandler(mediaDir), payment: booking.NewAdminHandler(), season: season.NewAdminHandler(locales), @@ -66,6 +69,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 "location": + h.location.Handler(user, company, conn).ServeHTTP(w, r) case "media": h.media.Handler(user, company, conn).ServeHTTP(w, r) case "payment": diff --git a/pkg/app/public.go b/pkg/app/public.go index e2a59db..eecbd72 100644 --- a/pkg/app/public.go +++ b/pkg/app/public.go @@ -14,6 +14,7 @@ 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/location" "dev.tandem.ws/tandem/camper/pkg/services" "dev.tandem.ws/tandem/camper/pkg/template" ) @@ -22,6 +23,7 @@ type publicHandler struct { home *home.PublicHandler booking *booking.PublicHandler campsite *campsite.PublicHandler + location *location.PublicHandler services *services.PublicHandler } @@ -30,6 +32,7 @@ func newPublicHandler() *publicHandler { home: home.NewPublicHandler(), booking: booking.NewPublicHandler(), campsite: campsite.NewPublicHandler(), + location: location.NewPublicHandler(), services: services.NewPublicHandler(), } } @@ -47,8 +50,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 "contact": - contactHandler(user, company, conn).ServeHTTP(w, r) + case "location": + h.location.Handler(user, company, conn).ServeHTTP(w, r) case "services": h.services.Handler(user, company, conn).ServeHTTP(w, r) case "surroundings": @@ -98,53 +101,3 @@ func campgroundHandler(user *auth.User, company *auth.Company, conn *database.Co } }) } - -func contactHandler(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: - page := newContactPage() - page.MustRender(w, r, user, company, conn) - default: - httplib.MethodNotAllowed(w, r, http.MethodGet) - } - default: - http.NotFound(w, r) - } - }) -} - -type contactPage struct { - *template.PublicPage - CompanyGeography *geographyPoint -} - -func newContactPage() *contactPage { - page := &contactPage{ - PublicPage: template.NewPublicPage(), - } - return page -} - -func (p *contactPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { - p.Setup(r, user, company, conn) - row := conn.QueryRow(r.Context(), `select st_x(geog::geometry)::text, st_y(geog::geometry)::text from company_geography where company_id = $1`, company.ID) - geography := &geographyPoint{} - if err := row.Scan(&geography.Lng, &geography.Lat); err != nil { - if !database.ErrorIsNotFound(err) { - panic(err) - } - } else { - p.CompanyGeography = geography - } - template.MustRenderPublic(w, r, user, company, "contact.gohtml", p) -} - -type geographyPoint struct { - Lat string - Lng string -} diff --git a/pkg/auth/company.go b/pkg/auth/company.go index 755641c..2833bed 100644 --- a/pkg/auth/company.go +++ b/pkg/auth/company.go @@ -7,19 +7,22 @@ package auth import ( "context" + "golang.org/x/text/language" "dev.tandem.ws/tandem/camper/pkg/database" "dev.tandem.ws/tandem/camper/pkg/locale" ) type Company struct { - ID int - Locales locale.Locales + ID int + DefaultLanguage language.Tag + Locales locale.Locales } func CompanyByHost(ctx context.Context, conn *database.Conn, host string, allLocales locale.Locales) (*Company, error) { company := &Company{ - Locales: allLocales, + Locales: allLocales, + DefaultLanguage: language.Catalan, } if err := conn.QueryRow(ctx, ` select company_id diff --git a/pkg/database/funcs.go b/pkg/database/funcs.go index da1788b..68cf458 100644 --- a/pkg/database/funcs.go +++ b/pkg/database/funcs.go @@ -92,3 +92,13 @@ func (c *Conn) OrderSeasons(ctx context.Context, slugs []string) error { _, err := c.Exec(ctx, "select order_seasons($1)", slugs) return err } + +func (tx *Tx) SetupLocation(ctx context.Context, companyID int, directions string, mapEmbed string, openingHours string) error { + _, err := tx.Exec(ctx, "select setup_location($1, $2, $3, $4)", companyID, directions, mapEmbed, openingHours) + return err +} + +func (tx *Tx) TranslateLocation(ctx context.Context, companyID int, langTag language.Tag, directions string, openingHours string) error { + _, err := tx.Exec(ctx, "select translate_location($1, $2, $3, $4)", companyID, langTag, directions, openingHours) + return err +} diff --git a/pkg/form/input.go b/pkg/form/input.go index 0e74c3c..926beed 100644 --- a/pkg/form/input.go +++ b/pkg/form/input.go @@ -7,6 +7,7 @@ package form import ( "database/sql/driver" + "dev.tandem.ws/tandem/camper/pkg/locale" "net/http" "strconv" "strings" @@ -50,3 +51,21 @@ type L10nInput struct { Input Source string } + +type I18nInput map[string]*Input + +func NewI18nInput(locales locale.Locales, name string) I18nInput { + input := make(I18nInput) + for lang := range locales { + input[lang.String()] = &Input{ + Name: name + "." + lang.String(), + } + } + return input +} + +func (input I18nInput) FillValue(r *http.Request) { + for _, inner := range input { + inner.FillValue(r) + } +} diff --git a/pkg/location/admin.go b/pkg/location/admin.go new file mode 100644 index 0000000..7379e5d --- /dev/null +++ b/pkg/location/admin.go @@ -0,0 +1,156 @@ +package location + +import ( + "context" + "golang.org/x/text/language" + "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: + f := newLocationForm(company.Locales) + if err := f.FillFromDatabase(r.Context(), company, conn); err != nil { + if !database.ErrorIsNotFound(err) { + panic(err) + } + } + f.MustRender(w, r, user, company) + case http.MethodPut: + updateLocationSettings(w, r, user, company, conn) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet) + } + default: + http.NotFound(w, r) + } + }) +} + +func updateLocationSettings(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { + f := newLocationForm(company.Locales) + 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()) + defer tx.Rollback(r.Context()) + var defaultLang = company.DefaultLanguage.String() + if err := tx.SetupLocation(r.Context(), company.ID, f.Directions[defaultLang].Val, f.MapEmbed.Val, f.OpeningDates[defaultLang].Val); err != nil { + panic(err) + } + for lang := range company.Locales { + if lang == company.DefaultLanguage { + continue + } + if err := tx.TranslateLocation(r.Context(), company.ID, lang, f.Directions[lang.String()].Val, f.OpeningDates[lang.String()].Val); err != nil { + panic(err) + } + } + tx.MustCommit(r.Context()) + httplib.Redirect(w, r, "/admin/location", http.StatusSeeOther) +} + +type locationForm struct { + Directions form.I18nInput + MapEmbed *form.Input + OpeningDates form.I18nInput +} + +func newLocationForm(locales locale.Locales) *locationForm { + return &locationForm{ + Directions: form.NewI18nInput(locales, "directions"), + MapEmbed: &form.Input{ + Name: "map_embed", + }, + OpeningDates: form.NewI18nInput(locales, "opening_dates"), + } +} + +func (f *locationForm) FillFromDatabase(ctx context.Context, company *auth.Company, conn *database.Conn) error { + var directions database.RecordArray + var openingDates database.RecordArray + err := conn.QueryRow(ctx, ` + select location.directions::text + , location.map_embed::text + , location.opening_dates::text + , array_agg((lang_tag, i18n.directions::text)) + , array_agg((lang_tag, i18n.opening_dates::text)) + from location + left join location_i18n as i18n using (company_id) + where company_id = $1 + group by location.directions::text + , location.map_embed::text + , location.opening_dates::text +`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, company.ID).Scan( + &f.Directions[company.DefaultLanguage.String()].Val, + &f.MapEmbed.Val, + &f.OpeningDates[company.DefaultLanguage.String()].Val, + &directions, + &openingDates, + ) + if err != nil { + return err + } + if err := fillI18nInput(f.Directions, directions); err != nil { + return err + } + if err := fillI18nInput(f.OpeningDates, 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) +} + +func (f *locationForm) Parse(r *http.Request) error { + if err := r.ParseForm(); err != nil { + return err + } + f.Directions.FillValue(r) + f.MapEmbed.FillValue(r) + f.OpeningDates.FillValue(r) + return nil +} + +func (f *locationForm) Valid(l *locale.Locale) bool { + v := form.NewValidator(l) + return v.AllOK +} diff --git a/pkg/location/public.go b/pkg/location/public.go new file mode 100644 index 0000000..ccbedb9 --- /dev/null +++ b/pkg/location/public.go @@ -0,0 +1,67 @@ +package location + +import ( + "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" + gotemplate "html/template" + "net/http" +) + +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 "": + switch r.Method { + case http.MethodGet: + page := newLocationPage() + page.MustRender(w, r, user, company, conn) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet) + } + default: + http.NotFound(w, r) + } + }) +} + +type locationPage struct { + *template.PublicPage + Directions gotemplate.HTML + MapEmbed gotemplate.HTML +} + +func newLocationPage() *locationPage { + page := &locationPage{ + PublicPage: template.NewPublicPage(), + } + return page +} + +func (p *locationPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { + p.Setup(r, user, company, conn) + row := conn.QueryRow(r.Context(), ` + select coalesce(i18n.directions, location.directions)::text + , map_embed::text + from location + left join location_i18n as i18n on location.company_id = i18n.company_id and i18n.lang_tag = $1 + where location.company_id = $2 + `, user.Locale.Language, company.ID) + if err := row.Scan(&p.Directions, &p.MapEmbed); err != nil { + if !database.ErrorIsNotFound(err) { + panic(err) + } + } + template.MustRenderPublic(w, r, user, company, "location.gohtml", p) +} diff --git a/pkg/template/page.go b/pkg/template/page.go index 2264573..e9727c0 100644 --- a/pkg/template/page.go +++ b/pkg/template/page.go @@ -8,6 +8,7 @@ package template import ( "context" "fmt" + gotemplate "html/template" "net/http" "sort" @@ -21,6 +22,7 @@ type PublicPage struct { LocalizedAlternates []*LocalizedAlternate Menu *siteMenu CompanyAddress *address + OpeningDates gotemplate.HTML } func NewPublicPage() *PublicPage { @@ -54,6 +56,15 @@ func (p *PublicPage) Setup(r *http.Request, user *auth.User, company *auth.Compa `, user.Locale.Language, company.ID), } + if err := conn.QueryRow(r.Context(), ` + select coalesce(i18n.opening_dates, location.opening_dates) + from location + left join location_i18n as i18n on location.company_id = i18n.company_id and i18n.lang_tag = $1 + where location.company_id = $2 + `, user.Locale.Language, company.ID).Scan(&p.OpeningDates); err != nil { + panic(err) + } + if err := p.CompanyAddress.FillFromDatabase(r.Context(), conn, user, company); err != nil { panic(err) } @@ -113,19 +124,19 @@ type address struct { func (addr *address) FillFromDatabase(ctx context.Context, conn *database.Conn, user *auth.User, company *auth.Company) error { row := conn.QueryRow(ctx, ` - select trade_name - , address - , postal_code - , province - , city - , coalesce(country_i18n.name, country.name) as country_name - , phone::text - , email::text - , rtc_number - from company - join country using (country_code) - left join country_i18n on country.country_code = country_i18n.country_code and country_i18n.lang_tag = $2 - where company_id = $1 +select trade_name +, address +, postal_code +, province +, city +, coalesce(country_i18n.name, country.name) as country_name +, phone::text +, email::text +, rtc_number +from company +join country using (country_code) +left join country_i18n on country.country_code = country_i18n.country_code and country_i18n.lang_tag = $2 +where company_id = $1 `, company.ID, user.Locale.Language) return row.Scan( &addr.TradeName, diff --git a/po/ca.po b/po/ca.po index 4fd22b7..66814de 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 17:32+0100\n" +"POT-Creation-Date: 2023-12-21 21:08+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:52 web/templates/public/layout.gohtml:79 +#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79 #: web/templates/admin/services/index.gohtml:66 msgctxt "title" msgid "Services" @@ -53,6 +53,14 @@ msgstr "Serveis" msgid "The campsite offers many different services." 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/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 msgctxt "title" @@ -79,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:53 web/templates/public/layout.gohtml:80 +#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80 msgctxt "title" msgid "Surroundings" msgstr "L’entorn" @@ -268,14 +276,8 @@ msgctxt "title" msgid "Campground" msgstr "El càmping" -#: web/templates/public/contact.gohtml:6 web/templates/public/contact.gohtml:18 -#: web/templates/public/layout.gohtml:54 web/templates/public/layout.gohtml:81 -msgctxt "title" -msgid "Contact" -msgstr "Contacte" - #: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11 -#: web/templates/public/layout.gohtml:51 +#: web/templates/public/layout.gohtml:54 msgctxt "title" msgid "Booking" msgstr "Reserva" @@ -364,18 +366,18 @@ 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:110 +#: web/templates/public/layout.gohtml:109 msgid "Campsite Montagut" msgstr "Càmping Montagut" -#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:19 +#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:20 msgid "Skip to main content" msgstr "Salta al contingut principal" #: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88 #: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:12 -#: web/templates/admin/layout.gohtml:44 web/templates/admin/layout.gohtml:75 +#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79 msgctxt "title" msgid "Campsites" msgstr "Allotjaments" @@ -385,7 +387,12 @@ msgctxt "title" msgid "Sections" msgstr "Apartats" -#: web/templates/public/layout.gohtml:107 +#: web/templates/public/layout.gohtml:99 +msgctxt "title" +msgid "Opening" +msgstr "Obertura" + +#: web/templates/public/layout.gohtml:106 msgid "RTC #%s" msgstr "Núm. RTC %s" @@ -482,6 +489,33 @@ msgctxt "action" msgid "Translate" msgstr "Tradueix" +#: web/templates/admin/location.gohtml:6 web/templates/admin/location.gohtml:12 +msgctxt "title" +msgid "Location Settings" +msgstr "Paràmetres de com arribar" + +#: web/templates/admin/location.gohtml:17 +msgctxt "input" +msgid "Directions" +msgstr "Instruccions" + +#: web/templates/admin/location.gohtml:35 +msgctxt "input" +msgid "Opening Dates" +msgstr "Data d’obertura" + +#: web/templates/admin/location.gohtml:53 +msgctxt "input" +msgid "Map Embed" +msgstr "Incrustació del mapa" + +#: web/templates/admin/location.gohtml:60 web/templates/admin/payment.gohtml:62 +#: web/templates/admin/profile.gohtml:75 +#: web/templates/admin/taxDetails.gohtml:152 +msgctxt "action" +msgid "Save changes" +msgstr "Desa els canvis" + #: web/templates/admin/campsite/feature/form.gohtml:8 #: web/templates/admin/campsite/feature/form.gohtml:25 msgctxt "title" @@ -802,7 +836,7 @@ msgstr "Descripció" #: web/templates/admin/campsite/type/index.gohtml:6 #: web/templates/admin/campsite/type/index.gohtml:12 -#: web/templates/admin/layout.gohtml:41 +#: web/templates/admin/layout.gohtml:42 msgctxt "title" msgid "Campsite Types" msgstr "Tipus d’allotjaments" @@ -877,7 +911,7 @@ msgstr "Color" #: web/templates/admin/season/index.gohtml:6 #: web/templates/admin/season/index.gohtml:12 -#: web/templates/admin/layout.gohtml:47 +#: web/templates/admin/layout.gohtml:48 msgctxt "title" msgid "Seasons" msgstr "Temporades" @@ -909,7 +943,7 @@ msgid "Cancel" msgstr "Canceŀla" #: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12 -#: web/templates/admin/layout.gohtml:38 +#: web/templates/admin/layout.gohtml:39 msgctxt "title" msgid "Payment Settings" msgstr "Paràmetres de pagament" @@ -944,14 +978,8 @@ msgctxt "title" msgid "Integration" msgstr "Integració" -#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75 -#: web/templates/admin/taxDetails.gohtml:152 -msgctxt "action" -msgid "Save changes" -msgstr "Desa els canvis" - #: web/templates/admin/dashboard.gohtml:6 -#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:72 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76 msgctxt "title" msgid "Dashboard" msgstr "Tauler" @@ -984,7 +1012,7 @@ msgid "New Service" msgstr "Nou servei" #: web/templates/admin/services/index.gohtml:6 -#: web/templates/admin/layout.gohtml:56 +#: web/templates/admin/layout.gohtml:57 msgctxt "title" msgid "Services Page" msgstr "Pàgina de serveis" @@ -1020,7 +1048,7 @@ msgid "Translate Service to %s" msgstr "Traducció del servei a %s" #: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12 -#: web/templates/admin/layout.gohtml:31 +#: web/templates/admin/layout.gohtml:32 msgctxt "title" msgid "Profile" msgstr "Perfil" @@ -1112,29 +1140,29 @@ msgctxt "input" msgid "Legal Disclaimer" msgstr "Nota legal" -#: web/templates/admin/layout.gohtml:27 +#: web/templates/admin/layout.gohtml:28 msgctxt "title" msgid "User Menu" msgstr "Menú d’usuari" -#: web/templates/admin/layout.gohtml:35 +#: web/templates/admin/layout.gohtml:36 msgctxt "title" msgid "Company Settings" msgstr "Paràmetres de l’empresa" -#: web/templates/admin/layout.gohtml:50 +#: web/templates/admin/layout.gohtml:51 #: web/templates/admin/media/index.gohtml:6 #: web/templates/admin/media/index.gohtml:11 msgctxt "title" msgid "Media" msgstr "Mèdia" -#: web/templates/admin/layout.gohtml:53 web/templates/admin/home/index.gohtml:6 +#: web/templates/admin/layout.gohtml:54 web/templates/admin/home/index.gohtml:6 msgctxt "title" msgid "Home Page" msgstr "Pàgina d’inici" -#: web/templates/admin/layout.gohtml:61 +#: web/templates/admin/layout.gohtml:65 msgctxt "action" msgid "Logout" msgstr "Surt" @@ -1265,7 +1293,7 @@ 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:56 +#: pkg/app/admin.go:59 msgid "Access forbidden" msgstr "Accés prohibit" @@ -1534,55 +1562,55 @@ msgstr "No podeu deixar el fitxer del mèdia en blanc." msgid "Filename can not be empty." msgstr "No podeu deixar el nom del fitxer en blanc." -#: pkg/booking/admin.go:74 +#: pkg/booking/admin.go:76 msgctxt "redsys environment" msgid "Test" msgstr "Proves" -#: pkg/booking/admin.go:78 +#: pkg/booking/admin.go:80 msgctxt "redsys environment" msgid "Live" msgstr "Real" -#: pkg/booking/admin.go:87 +#: pkg/booking/admin.go:89 msgctxt "redsys integration" msgid "InSite" msgstr "InSite" -#: pkg/booking/admin.go:91 +#: pkg/booking/admin.go:93 msgctxt "redsys integration" msgid "Redirect" msgstr "Redirecció" -#: pkg/booking/admin.go:135 +#: pkg/booking/admin.go:137 msgid "Merchant code can not be empty." msgstr "No podeu deixar el codi del comerç en blanc." -#: pkg/booking/admin.go:136 +#: pkg/booking/admin.go:138 msgid "Merchant code must be exactly nine digits long." msgstr "El codi del comerç ha de ser de nou dígits." -#: pkg/booking/admin.go:137 +#: pkg/booking/admin.go:139 msgid "Merchant code must be a number." msgstr "El codi del comerç." -#: pkg/booking/admin.go:141 +#: pkg/booking/admin.go:143 msgid "Terminal number can not be empty." msgstr "No podeu deixar el número del terminal en blanc." -#: pkg/booking/admin.go:142 +#: pkg/booking/admin.go:144 msgid "Terminal number must be a number between 1 and 999." msgstr "El número del terminal ha de ser entre 1 i 999" -#: pkg/booking/admin.go:150 +#: pkg/booking/admin.go:152 msgid "Selected environment is not valid." msgstr "L’entorn escollit no és vàlid." -#: pkg/booking/admin.go:151 +#: pkg/booking/admin.go:153 msgid "Selected integration is not valid." msgstr "La integració escollida no és vàlida." -#: pkg/booking/admin.go:154 +#: pkg/booking/admin.go:156 msgid "The merchant key is not valid." msgstr "Aquesta clau del comerç no és vàlid." @@ -1638,6 +1666,10 @@ 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 a01d616..727c026 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 17:32+0100\n" +"POT-Creation-Date: 2023-12-21 21:08+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:52 web/templates/public/layout.gohtml:79 +#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79 #: web/templates/admin/services/index.gohtml:66 msgctxt "title" msgid "Services" @@ -53,6 +53,14 @@ msgstr "Servicios" msgid "The campsite offers many different services." 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/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 msgctxt "title" @@ -79,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:53 web/templates/public/layout.gohtml:80 +#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80 msgctxt "title" msgid "Surroundings" msgstr "El entorno" @@ -268,14 +276,8 @@ msgctxt "title" msgid "Campground" msgstr "El camping" -#: web/templates/public/contact.gohtml:6 web/templates/public/contact.gohtml:18 -#: web/templates/public/layout.gohtml:54 web/templates/public/layout.gohtml:81 -msgctxt "title" -msgid "Contact" -msgstr "Contacto" - #: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11 -#: web/templates/public/layout.gohtml:51 +#: web/templates/public/layout.gohtml:54 msgctxt "title" msgid "Booking" msgstr "Reserva" @@ -364,18 +366,18 @@ 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:110 +#: web/templates/public/layout.gohtml:109 msgid "Campsite Montagut" msgstr "Camping Montagut" -#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:19 +#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:20 msgid "Skip to main content" msgstr "Saltar al contenido principal" #: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88 #: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:12 -#: web/templates/admin/layout.gohtml:44 web/templates/admin/layout.gohtml:75 +#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79 msgctxt "title" msgid "Campsites" msgstr "Alojamientos" @@ -385,7 +387,12 @@ msgctxt "title" msgid "Sections" msgstr "Apartados" -#: web/templates/public/layout.gohtml:107 +#: web/templates/public/layout.gohtml:99 +msgctxt "title" +msgid "Opening" +msgstr "Apertura" + +#: web/templates/public/layout.gohtml:106 msgid "RTC #%s" msgstr " RTC %s" @@ -482,6 +489,33 @@ msgctxt "action" msgid "Translate" msgstr "Traducir" +#: web/templates/admin/location.gohtml:6 web/templates/admin/location.gohtml:12 +msgctxt "title" +msgid "Location Settings" +msgstr "Parámetros de cómo llegar" + +#: web/templates/admin/location.gohtml:17 +msgctxt "input" +msgid "Directions" +msgstr "Instrucciones" + +#: web/templates/admin/location.gohtml:35 +msgctxt "input" +msgid "Opening Dates" +msgstr "Fechas de apertura" + +#: web/templates/admin/location.gohtml:53 +msgctxt "input" +msgid "Map Embed" +msgstr "Incrustación del mapa" + +#: web/templates/admin/location.gohtml:60 web/templates/admin/payment.gohtml:62 +#: web/templates/admin/profile.gohtml:75 +#: web/templates/admin/taxDetails.gohtml:152 +msgctxt "action" +msgid "Save changes" +msgstr "Guardar los cambios" + #: web/templates/admin/campsite/feature/form.gohtml:8 #: web/templates/admin/campsite/feature/form.gohtml:25 msgctxt "title" @@ -802,7 +836,7 @@ msgstr "Descripción" #: web/templates/admin/campsite/type/index.gohtml:6 #: web/templates/admin/campsite/type/index.gohtml:12 -#: web/templates/admin/layout.gohtml:41 +#: web/templates/admin/layout.gohtml:42 msgctxt "title" msgid "Campsite Types" msgstr "Tipos de alojamientos" @@ -877,7 +911,7 @@ msgstr "Color" #: web/templates/admin/season/index.gohtml:6 #: web/templates/admin/season/index.gohtml:12 -#: web/templates/admin/layout.gohtml:47 +#: web/templates/admin/layout.gohtml:48 msgctxt "title" msgid "Seasons" msgstr "Temporadas" @@ -909,7 +943,7 @@ msgid "Cancel" msgstr "Cancelar" #: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12 -#: web/templates/admin/layout.gohtml:38 +#: web/templates/admin/layout.gohtml:39 msgctxt "title" msgid "Payment Settings" msgstr "Parámetros de pago" @@ -944,14 +978,8 @@ msgctxt "title" msgid "Integration" msgstr "Integración" -#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75 -#: web/templates/admin/taxDetails.gohtml:152 -msgctxt "action" -msgid "Save changes" -msgstr "Guardar los cambios" - #: web/templates/admin/dashboard.gohtml:6 -#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:72 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76 msgctxt "title" msgid "Dashboard" msgstr "Panel" @@ -984,7 +1012,7 @@ msgid "New Service" msgstr "Nuevo servicio" #: web/templates/admin/services/index.gohtml:6 -#: web/templates/admin/layout.gohtml:56 +#: web/templates/admin/layout.gohtml:57 msgctxt "title" msgid "Services Page" msgstr "Página de servicios" @@ -1020,7 +1048,7 @@ msgid "Translate Service to %s" msgstr "Traducción del servicio a %s" #: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12 -#: web/templates/admin/layout.gohtml:31 +#: web/templates/admin/layout.gohtml:32 msgctxt "title" msgid "Profile" msgstr "Perfil" @@ -1112,29 +1140,29 @@ msgctxt "input" msgid "Legal Disclaimer" msgstr "Nota legal" -#: web/templates/admin/layout.gohtml:27 +#: web/templates/admin/layout.gohtml:28 msgctxt "title" msgid "User Menu" msgstr "Menú de usuario" -#: web/templates/admin/layout.gohtml:35 +#: web/templates/admin/layout.gohtml:36 msgctxt "title" msgid "Company Settings" msgstr "Parámetros de la empresa" -#: web/templates/admin/layout.gohtml:50 +#: web/templates/admin/layout.gohtml:51 #: web/templates/admin/media/index.gohtml:6 #: web/templates/admin/media/index.gohtml:11 msgctxt "title" msgid "Media" msgstr "Medios" -#: web/templates/admin/layout.gohtml:53 web/templates/admin/home/index.gohtml:6 +#: web/templates/admin/layout.gohtml:54 web/templates/admin/home/index.gohtml:6 msgctxt "title" msgid "Home Page" msgstr "Página de inicio" -#: web/templates/admin/layout.gohtml:61 +#: web/templates/admin/layout.gohtml:65 msgctxt "action" msgid "Logout" msgstr "Salir" @@ -1265,7 +1293,7 @@ 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:56 +#: pkg/app/admin.go:59 msgid "Access forbidden" msgstr "Acceso prohibido" @@ -1534,55 +1562,55 @@ msgstr "No podéis dejar el archivo del medio en blanco." msgid "Filename can not be empty." msgstr "No podéis dejar el nombre del archivo en blanco." -#: pkg/booking/admin.go:74 +#: pkg/booking/admin.go:76 msgctxt "redsys environment" msgid "Test" msgstr "Pruebas" -#: pkg/booking/admin.go:78 +#: pkg/booking/admin.go:80 msgctxt "redsys environment" msgid "Live" msgstr "Real" -#: pkg/booking/admin.go:87 +#: pkg/booking/admin.go:89 msgctxt "redsys integration" msgid "InSite" msgstr "InSite" -#: pkg/booking/admin.go:91 +#: pkg/booking/admin.go:93 msgctxt "redsys integration" msgid "Redirect" msgstr "Redirección" -#: pkg/booking/admin.go:135 +#: pkg/booking/admin.go:137 msgid "Merchant code can not be empty." msgstr "No podéis dejar el código del comercio en blanco." -#: pkg/booking/admin.go:136 +#: pkg/booking/admin.go:138 msgid "Merchant code must be exactly nine digits long." msgstr "El código del comercio tiene que ser de nueve dígitos." -#: pkg/booking/admin.go:137 +#: pkg/booking/admin.go:139 msgid "Merchant code must be a number." msgstr "El código del comercio tiene que ser un número." -#: pkg/booking/admin.go:141 +#: pkg/booking/admin.go:143 msgid "Terminal number can not be empty." msgstr "No podéis dejar el número de terminal en blanco." -#: pkg/booking/admin.go:142 +#: pkg/booking/admin.go:144 msgid "Terminal number must be a number between 1 and 999." msgstr "El número de terminal tiene que ser entre 1 y 999." -#: pkg/booking/admin.go:150 +#: pkg/booking/admin.go:152 msgid "Selected environment is not valid." msgstr "El entorno escogido no es válido." -#: pkg/booking/admin.go:151 +#: pkg/booking/admin.go:153 msgid "Selected integration is not valid." msgstr "La integración escogida no es válida." -#: pkg/booking/admin.go:154 +#: pkg/booking/admin.go:156 msgid "The merchant key is not valid." msgstr "Esta clave del comercio no es válida." @@ -1638,6 +1666,10 @@ 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 38c4018..a6d5878 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 17:32+0100\n" +"POT-Creation-Date: 2023-12-21 21:08+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:52 web/templates/public/layout.gohtml:79 +#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79 #: web/templates/admin/services/index.gohtml:66 msgctxt "title" msgid "Services" @@ -54,6 +54,14 @@ msgstr "Services" msgid "The campsite offers many different services." 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/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 msgctxt "title" @@ -80,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:53 web/templates/public/layout.gohtml:80 +#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80 msgctxt "title" msgid "Surroundings" msgstr "Entourage" @@ -269,14 +277,8 @@ msgctxt "title" msgid "Campground" msgstr "Camping" -#: web/templates/public/contact.gohtml:6 web/templates/public/contact.gohtml:18 -#: web/templates/public/layout.gohtml:54 web/templates/public/layout.gohtml:81 -msgctxt "title" -msgid "Contact" -msgstr "Contact" - #: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11 -#: web/templates/public/layout.gohtml:51 +#: web/templates/public/layout.gohtml:54 msgctxt "title" msgid "Booking" msgstr "Reservation" @@ -365,18 +367,18 @@ 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:110 +#: web/templates/public/layout.gohtml:109 msgid "Campsite Montagut" msgstr "Camping Montagut" -#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:19 +#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:20 msgid "Skip to main content" msgstr "Passer au contenu principal" #: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88 #: web/templates/admin/campsite/index.gohtml:6 #: web/templates/admin/campsite/index.gohtml:12 -#: web/templates/admin/layout.gohtml:44 web/templates/admin/layout.gohtml:75 +#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79 msgctxt "title" msgid "Campsites" msgstr "Locatifs" @@ -386,7 +388,12 @@ msgctxt "title" msgid "Sections" msgstr "Sections" -#: web/templates/public/layout.gohtml:107 +#: web/templates/public/layout.gohtml:99 +msgctxt "title" +msgid "Opening" +msgstr "Ouverture" + +#: web/templates/public/layout.gohtml:106 msgid "RTC #%s" msgstr "# RTC %s" @@ -483,6 +490,33 @@ msgctxt "action" msgid "Translate" msgstr "Traduire" +#: web/templates/admin/location.gohtml:6 web/templates/admin/location.gohtml:12 +msgctxt "title" +msgid "Location Settings" +msgstr "Paramètres de comment nous rejoindre" + +#: web/templates/admin/location.gohtml:17 +msgctxt "input" +msgid "Directions" +msgstr "Directions" + +#: web/templates/admin/location.gohtml:35 +msgctxt "input" +msgid "Opening Dates" +msgstr "Dates d’ouverture" + +#: web/templates/admin/location.gohtml:53 +msgctxt "input" +msgid "Map Embed" +msgstr "Carte intégrée" + +#: web/templates/admin/location.gohtml:60 web/templates/admin/payment.gohtml:62 +#: web/templates/admin/profile.gohtml:75 +#: web/templates/admin/taxDetails.gohtml:152 +msgctxt "action" +msgid "Save changes" +msgstr "Enregistrer les changements" + #: web/templates/admin/campsite/feature/form.gohtml:8 #: web/templates/admin/campsite/feature/form.gohtml:25 msgctxt "title" @@ -803,7 +837,7 @@ msgstr "Description" #: web/templates/admin/campsite/type/index.gohtml:6 #: web/templates/admin/campsite/type/index.gohtml:12 -#: web/templates/admin/layout.gohtml:41 +#: web/templates/admin/layout.gohtml:42 msgctxt "title" msgid "Campsite Types" msgstr "Types d’emplacements de camping" @@ -878,7 +912,7 @@ msgstr "Couleur" #: web/templates/admin/season/index.gohtml:6 #: web/templates/admin/season/index.gohtml:12 -#: web/templates/admin/layout.gohtml:47 +#: web/templates/admin/layout.gohtml:48 msgctxt "title" msgid "Seasons" msgstr "Saisons" @@ -910,7 +944,7 @@ msgid "Cancel" msgstr "Annuler" #: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12 -#: web/templates/admin/layout.gohtml:38 +#: web/templates/admin/layout.gohtml:39 msgctxt "title" msgid "Payment Settings" msgstr "Paramètres de paiement" @@ -945,14 +979,8 @@ msgctxt "title" msgid "Integration" msgstr "Intégration" -#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75 -#: web/templates/admin/taxDetails.gohtml:152 -msgctxt "action" -msgid "Save changes" -msgstr "Enregistrer les changements" - #: web/templates/admin/dashboard.gohtml:6 -#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:72 +#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76 msgctxt "title" msgid "Dashboard" msgstr "Tableau de bord" @@ -985,7 +1013,7 @@ msgid "New Service" msgstr "Nouveau service" #: web/templates/admin/services/index.gohtml:6 -#: web/templates/admin/layout.gohtml:56 +#: web/templates/admin/layout.gohtml:57 msgctxt "title" msgid "Services Page" msgstr "La page des services" @@ -1021,7 +1049,7 @@ msgid "Translate Service to %s" msgstr "Traduire le service en %s" #: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12 -#: web/templates/admin/layout.gohtml:31 +#: web/templates/admin/layout.gohtml:32 msgctxt "title" msgid "Profile" msgstr "Profil" @@ -1113,29 +1141,29 @@ msgctxt "input" msgid "Legal Disclaimer" msgstr "Avertissement juridique" -#: web/templates/admin/layout.gohtml:27 +#: web/templates/admin/layout.gohtml:28 msgctxt "title" msgid "User Menu" msgstr "Menu utilisateur" -#: web/templates/admin/layout.gohtml:35 +#: web/templates/admin/layout.gohtml:36 msgctxt "title" msgid "Company Settings" msgstr "Paramètres de l'entreprise" -#: web/templates/admin/layout.gohtml:50 +#: web/templates/admin/layout.gohtml:51 #: web/templates/admin/media/index.gohtml:6 #: web/templates/admin/media/index.gohtml:11 msgctxt "title" msgid "Media" msgstr "Média" -#: web/templates/admin/layout.gohtml:53 web/templates/admin/home/index.gohtml:6 +#: web/templates/admin/layout.gohtml:54 web/templates/admin/home/index.gohtml:6 msgctxt "title" msgid "Home Page" msgstr "Page d'accueil" -#: web/templates/admin/layout.gohtml:61 +#: web/templates/admin/layout.gohtml:65 msgctxt "action" msgid "Logout" msgstr "Déconnexion" @@ -1266,7 +1294,7 @@ 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:56 +#: pkg/app/admin.go:59 msgid "Access forbidden" msgstr "Accès interdit" @@ -1535,55 +1563,55 @@ msgstr "Le fichier téléchargé ne peut pas être vide." msgid "Filename can not be empty." msgstr "Le nom de fichier ne peut pas être vide." -#: pkg/booking/admin.go:74 +#: pkg/booking/admin.go:76 msgctxt "redsys environment" msgid "Test" msgstr "Test" -#: pkg/booking/admin.go:78 +#: pkg/booking/admin.go:80 msgctxt "redsys environment" msgid "Live" msgstr "Live" -#: pkg/booking/admin.go:87 +#: pkg/booking/admin.go:89 msgctxt "redsys integration" msgid "InSite" msgstr "Insite" -#: pkg/booking/admin.go:91 +#: pkg/booking/admin.go:93 msgctxt "redsys integration" msgid "Redirect" msgstr "Redirection" -#: pkg/booking/admin.go:135 +#: pkg/booking/admin.go:137 msgid "Merchant code can not be empty." msgstr "Le code marchand ne peut pas être vide." -#: pkg/booking/admin.go:136 +#: pkg/booking/admin.go:138 msgid "Merchant code must be exactly nine digits long." msgstr "Le code marchand doit comporter exactement neuf chiffres." -#: pkg/booking/admin.go:137 +#: pkg/booking/admin.go:139 msgid "Merchant code must be a number." msgstr "Le code du commerçant doit être un chiffre." -#: pkg/booking/admin.go:141 +#: pkg/booking/admin.go:143 msgid "Terminal number can not be empty." msgstr "Le numéro de terminal ne peut pas être vide." -#: pkg/booking/admin.go:142 +#: pkg/booking/admin.go:144 msgid "Terminal number must be a number between 1 and 999." msgstr "Le numéro de terminal doit être compris entre 1 et 999." -#: pkg/booking/admin.go:150 +#: pkg/booking/admin.go:152 msgid "Selected environment is not valid." msgstr "L’environnement sélectionné n’est pas valide." -#: pkg/booking/admin.go:151 +#: pkg/booking/admin.go:153 msgid "Selected integration is not valid." msgstr "L’intégration sélectionnée n’est pas valide." -#: pkg/booking/admin.go:154 +#: pkg/booking/admin.go:156 msgid "The merchant key is not valid." msgstr "La clé marchand n’est pas valide." @@ -1638,3 +1666,7 @@ 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/company_geography.sql b/revert/company_geography.sql deleted file mode 100644 index 3f3c0b8..0000000 --- a/revert/company_geography.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert camper:company_geography from pg - -begin; - -drop table if exists camper.company_geography; - -commit; diff --git a/revert/extension_postgis.sql b/revert/extension_postgis.sql deleted file mode 100644 index d70b18b..0000000 --- a/revert/extension_postgis.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Revert camper:extension_postgis from pg - -begin; - -drop extension if exists postgis; - -commit; diff --git a/revert/location.sql b/revert/location.sql new file mode 100644 index 0000000..dd46108 --- /dev/null +++ b/revert/location.sql @@ -0,0 +1,7 @@ +-- Revert camper:location from pg + +begin; + +drop table if exists camper.location; + +commit; diff --git a/revert/location_i18n.sql b/revert/location_i18n.sql new file mode 100644 index 0000000..b74c366 --- /dev/null +++ b/revert/location_i18n.sql @@ -0,0 +1,7 @@ +-- Revert camper:location_i18n from pg + +begin; + +drop table if exists camper.location_i18n; + +commit; diff --git a/revert/setup_location.sql b/revert/setup_location.sql new file mode 100644 index 0000000..c26ea2f --- /dev/null +++ b/revert/setup_location.sql @@ -0,0 +1,7 @@ +-- Revert camper:setup_location from pg + +begin; + +drop function if exists camper.setup_location(integer, text, text, text); + +commit; diff --git a/revert/translate_location.sql b/revert/translate_location.sql new file mode 100644 index 0000000..fa42585 --- /dev/null +++ b/revert/translate_location.sql @@ -0,0 +1,7 @@ +-- Revert camper:translate_location from pg + +begin; + +drop function if exists camper.translate_location(integer, text, text, text); + +commit; diff --git a/sqitch.plan b/sqitch.plan index 49e8b19..38f8bdc 100644 --- a/sqitch.plan +++ b/sqitch.plan @@ -97,8 +97,6 @@ campsite_type_option_cost [roles schema_camper campsite_type season campsite_typ set_campsite_type_option_cost [roles schema_camper campsite_type_option_cost parse_price] 2023-10-05T17:41:58Z jordi fita mas # Add function to set cost of campsite type option add_campsite_type_option [roles schema_camper campsite_type_option campsite_type] 2023-10-06T09:40:03Z jordi fita mas # Add function to create new campsite type options edit_campsite_type_option [roles schema_camper campsite_type_option] 2023-10-06T09:51:02Z jordi fita mas # Add function to edit campsite type options -extension_postgis [schema_public] 2023-10-06T17:18:52Z jordi fita mas # Add PostGIS extension -company_geography [roles schema_camper company user_profile extension_postgis] 2023-10-06T17:53:34Z jordi fita mas # Add the relation for the GPS coordinates of companies campsite_type_carousel [roles schema_camper campsite_type media user_profile] 2023-10-09T17:18:38Z jordi fita mas # Add the carousel for campsite types campsite_type_carousel_i18n [roles schema_camper campsite_type_carousel language] 2023-10-09T17:45:09Z jordi fita mas # Add relation for campsite type carousel translations add_campsite_type_carousel_slide [roles schema_camper campsite_type_carousel campsite_type] 2023-10-09T17:59:49Z jordi fita mas # Add function to create slides for the campsite type carousel @@ -126,3 +124,7 @@ order_campsite_type_carousel [schema_camper roles campsite_type campsite_type_ca order_home_carousel [schema_camper roles home_carousel] 2023-12-20T18:30:12Z jordi fita mas # Add function to order home carousel order_services_carousel [schema_camper roles services_carousel] 2023-12-20T18:39:18Z jordi fita mas # Add function to order services carousel order_campsite_type_features [schema_camper roles campsite_type_feature] 2023-12-21T16:13:14Z jordi fita mas # Add function to order campsite type features +location [schema_camper roles company] 2023-12-21T17:01:28Z jordi fita mas # Add table to keep location information +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 diff --git a/test/company_geography.sql b/test/company_geography.sql deleted file mode 100644 index 251ccb2..0000000 --- a/test/company_geography.sql +++ /dev/null @@ -1,168 +0,0 @@ --- Test company_geography -set client_min_messages to warning; -create extension if not exists pgtap; -reset client_min_messages; - -begin; - -select plan(30); - -set search_path to camper, public; - -select has_table('company_geography'); -select has_pk('company_geography'); -select table_privs_are('company_geography', 'guest', array['SELECT']); -select table_privs_are('company_geography', 'employee', array['SELECT']); -select table_privs_are('company_geography', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); -select table_privs_are('company_geography', 'authenticator', array[]::text[]); - -select has_column('company_geography', 'company_id'); -select col_is_pk('company_geography', 'company_id'); -select col_is_fk('company_geography', 'company_id'); -select fk_ok('company_geography', 'company_id', 'company', 'company_id'); -select col_type_is('company_geography', 'company_id', 'integer'); -select col_not_null('company_geography', 'company_id'); -select col_hasnt_default('company_geography', 'company_id'); - -select has_column('company_geography', 'geog'); -select col_type_is('company_geography', 'geog', 'geography'); -select col_not_null('company_geography', 'geog'); -select col_hasnt_default('company_geography', 'geog'); - - -set client_min_messages to warning; -truncate company_geography 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') - , (8, 'Company 8', 'XX345', '', '777-777-777', 'c@c', '', '', '', '', '', '', 'DE', 'USD', 'en') -; - -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 company_geography (company_id, geog) -values (2, 'SRID=4326;POINT(-118.4079 33.9434)') - , (4, 'SRID=4326;POINT(2.5559 49.0083)') -; - -prepare geog_data as -select company_id, st_x(geog::geometry), st_y(geog::geometry) -from company_geography -; - -set role guest; -select bag_eq( - 'geog_data', - $$ values (2, -118.4079, 33.9434) - , (4, 2.5559, 49.0083) - $$, - 'Everyone should be able to list all geography across all companies' -); -reset role; - -select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); - -select lives_ok( - $$ delete from company_geography where company_id = 2 $$, - 'Admin from company 2 should be able to delete geography from that company.' -); - -select bag_eq( - 'geog_data', - $$ values (4, 2.5559, 49.0083) - $$, - 'The row should have been deleted.' -); - -select lives_ok( - $$ insert into company_geography(company_id, geog) values (2, 'SRID=4326;POINT(-22.6056 63.9850)') $$, - 'Admin from company 2 should be able to insert a new geography to that company.' -); - -select bag_eq( - 'geog_data', - $$ values (2, -22.6056, 63.9850) - , (4, 2.5559, 49.0083) - $$, - 'The new row should have been added' -); - -select lives_ok( - $$ update company_geography set geog = 'SRID=4326;POINT(139.7330 35.5670)' where company_id = 2 $$, - 'Admin from company 2 should be able to update geography of that company.' -); - -select bag_eq( - 'geog_data', - $$ values (2, 139.7330, 35.5670) - , (4, 2.5559, 49.0083) - $$, - 'The row should have been updated.' -); - - -select throws_ok( - $$ insert into company_geography (company_id, geog) values (8, 'SRID=4326;POINT(2.5559 49.0083)') $$, - '42501', 'new row violates row-level security policy for table "company_geography"', - 'Admin from company 2 should NOT be able to insert new geography to another company 8.' -); - -select lives_ok( - $$ update company_geography set geog = 'SRID=4326;POINT(139.7330 35.5670)' where company_id = 4 $$, - 'Admin from company 2 should not be able to update geography of company 4, but no error if company_id is not changed.' -); - -select bag_eq( - 'geog_data', - $$ values (2, 139.7330, 35.5670) - , (4, 2.5559, 49.0083) - $$, - 'No row should have been changed.' -); - -select throws_ok( - $$ update company_geography set company_id = 4 where company_id = 2 $$, - '42501', 'new row violates row-level security policy for table "company_geography"', - 'Admin from company 2 should NOT be able to move geography to company 4' -); - -select lives_ok( - $$ delete from company_geography where company_id = 4 $$, - 'Admin from company 2 should NOT be able to delete geography from company 4, but not error is thrown' -); - -select bag_eq( - 'geog_data', - $$ values (2, 139.7330, 35.5670) - , (4, 2.5559, 49.0083) - $$, - 'No row should have been changed' -); - -reset role; - - -select * -from finish(); - -rollback; - diff --git a/test/extensions.sql b/test/extensions.sql index 39bfe10..7c31d17 100644 --- a/test/extensions.sql +++ b/test/extensions.sql @@ -14,7 +14,6 @@ select extensions_are(array [ , 'pg_libphonenumber' , 'pgtap' , 'plpgsql' - , 'postgis' , 'uri' , 'vat' ]); diff --git a/test/location.sql b/test/location.sql new file mode 100644 index 0000000..931d6a1 --- /dev/null +++ b/test/location.sql @@ -0,0 +1,178 @@ +-- Test location +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(38); + +set search_path to camper, public; + +select has_table('location'); +select has_pk('location'); +select table_privs_are('location', 'guest', array ['SELECT']); +select table_privs_are('location', 'employee', array ['SELECT']); +select table_privs_are('location', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('location', 'authenticator', array []::text[]); + +select has_column('location', 'company_id'); +select col_is_pk('location', 'company_id'); +select col_is_fk('location', 'company_id'); +select fk_ok('location', 'company_id', 'company', 'company_id'); +select col_type_is('location', 'company_id', 'integer'); +select col_not_null('location', 'company_id'); +select col_hasnt_default('location', 'company_id'); + +select has_column('location', 'directions'); +select col_type_is('location', 'directions', 'xml'); +select col_not_null('location', 'directions'); +select col_hasnt_default('location', 'directions'); + +select has_column('location', 'map_embed'); +select col_type_is('location', 'map_embed', 'xml'); +select col_not_null('location', 'map_embed'); +select col_hasnt_default('location', 'map_embed'); + +select has_column('location', 'opening_dates'); +select col_type_is('location', 'opening_dates', 'xml'); +select col_not_null('location', 'opening_dates'); +select col_hasnt_default('location', 'opening_dates'); + + +set client_min_messages to warning; +truncate location 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') + , (8, 'Company 8', 'XX345', '', '777-777-777', 'c@c', '', '', '', '', '', '', 'DE', 'USD', 'en') +; + +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 location (company_id, directions, map_embed, opening_dates) +values (2, '

up

', '', '

today

') + , (4, '

down

', '', '

yesterday

') +; + +prepare location_data as +select company_id, directions::text, map_embed::text, opening_dates::text +from location +; + +set role guest; +select bag_eq( + 'location_data', + $$ values (2, '

up

', '', '

today

') + , (4, '

down

', '', '

yesterday

') + $$, + 'Everyone should be able to list all location across all companies' +); +reset role; + +select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2'); + +select lives_ok( + $$ delete from location where company_id = 2 $$, + 'Admin from company 2 should be able to delete location from that company.' +); + +select bag_eq( + 'location_data', + $$ values (4, '

down

', '', '

yesterday

') + $$, + 'The row should have been deleted.' +); + +select lives_ok( + $$ insert into location(company_id, directions, map_embed, opening_dates) values (2, '

left

', '', '

tomorrow

') $$, + 'Admin from company 2 should be able to insert a new location to that company.' +); + +select bag_eq( + 'location_data', + $$ values (2, '

left

', '', '

tomorrow

') + , (4, '

down

', '', '

yesterday

') + $$, + 'The new row should have been added' +); + +select lives_ok( + $$ update location set directions = '

up

', map_embed = '', opening_dates = '

today

' where company_id = 2 $$, + 'Admin from company 2 should be able to update location of that company.' +); + +select bag_eq( + 'location_data', + $$ values (2, '

up

', '', '

today

') + , (4, '

down

', '', '

yesterday

') + $$, + 'The row should have been updated.' +); + + +select throws_ok( + $$ insert into location (company_id, directions, map_embed, opening_dates) values (8, '

', '

', '

') $$, + '42501', 'new row violates row-level security policy for table "location"', + 'Admin from company 2 should NOT be able to insert new location to another company.' +); + +select lives_ok( + $$ update location set opening_dates = '

never

' where company_id = 4 $$, + 'Admin from company 2 should not be able to update location of company 4, but no error if company_id is not changed.' +); + +select bag_eq( + 'location_data', + $$ values (2, '

up

', '', '

today

') + , (4, '

down

', '', '

yesterday

') + $$, + 'No row should have been changed.' +); + +select throws_ok( + $$ update location set company_id = 4 where company_id = 2 $$, + '42501', 'new row violates row-level security policy for table "location"', + 'Admin from company 2 should NOT be able to move location to company 4' +); + +select lives_ok( + $$ delete from location where company_id = 4 $$, + 'Admin from company 2 should NOT be able to delete location from company 4, but not error is thrown' +); + +select bag_eq( + 'location_data', + $$ values (2, '

up

', '', '

today

') + , (4, '

down

', '', '

yesterday

') + $$, + 'No row should have been changed' +); + +reset role; + + +select * +from finish(); + +rollback; + diff --git a/test/location_i18n.sql b/test/location_i18n.sql new file mode 100644 index 0000000..f3a9a15 --- /dev/null +++ b/test/location_i18n.sql @@ -0,0 +1,49 @@ +-- Test location_i18n +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(27); + +set search_path to camper, public; + +select has_table('location_i18n'); +select has_pk('location_i18n'); +select col_is_pk('location_i18n', array['company_id', 'lang_tag']); +select table_privs_are('location_i18n', 'guest', array['SELECT']); +select table_privs_are('location_i18n', 'employee', array['SELECT']); +select table_privs_are('location_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']); +select table_privs_are('location_i18n', 'authenticator', array[]::text[]); + +select has_column('location_i18n', 'company_id'); +select col_is_fk('location_i18n', 'company_id'); +select fk_ok('location_i18n', 'company_id', 'location', 'company_id'); +select col_type_is('location_i18n', 'company_id', 'integer'); +select col_not_null('location_i18n', 'company_id'); +select col_hasnt_default('location_i18n', 'company_id'); + +select has_column('location_i18n', 'lang_tag'); +select col_is_fk('location_i18n', 'lang_tag'); +select fk_ok('location_i18n', 'lang_tag', 'language', 'lang_tag'); +select col_type_is('location_i18n', 'lang_tag', 'text'); +select col_not_null('location_i18n', 'lang_tag'); +select col_hasnt_default('location_i18n', 'lang_tag'); + +select has_column('location_i18n', 'directions'); +select col_type_is('location_i18n', 'directions', 'xml'); +select col_not_null('location_i18n', 'directions'); +select col_hasnt_default('location_i18n', 'directions'); + +select has_column('location_i18n', 'opening_dates'); +select col_type_is('location_i18n', 'opening_dates', 'xml'); +select col_not_null('location_i18n', 'opening_dates'); +select col_hasnt_default('location_i18n', 'opening_dates'); + + +select * +from finish(); + +rollback; + diff --git a/test/setup_location.sql b/test/setup_location.sql new file mode 100644 index 0000000..12abb6f --- /dev/null +++ b/test/setup_location.sql @@ -0,0 +1,76 @@ +-- Test setup_location +set client_min_messages to warning; +create extension if not exists pgtap; +reset client_min_messages; + +begin; + +select plan(15); + +set search_path to camper, public; + +select has_function('camper', 'setup_location', array['integer', 'text', 'text', 'text']); +select function_lang_is('camper', 'setup_location', array['integer', 'text', 'text', 'text'], 'sql'); +select function_returns('camper', 'setup_location', array['integer', 'text', 'text', 'text'], 'void'); +select isnt_definer('camper', 'setup_location', array['integer', 'text', 'text', 'text']); +select volatility_is('camper', 'setup_location', array['integer', 'text', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]); + +set client_min_messages to warning; +truncate location 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 (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'ES', 'EUR', 'ca') + , (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'FR', 'USD', 'ca') +; + +prepare location_data as +select company_id, directions::text, map_embed::text, opening_dates::text +from location +; + +select lives_ok( + $$ select setup_location(1, '

Up

', '', '

Today

') $$, + 'Should be able to setup location parameters for the first company' +); + +select lives_ok( + $$ select setup_location(2, '

Left

', '', '

Tomorrow

') $$, + 'Should be able to setup location parameters for the second company' +); + +select bag_eq( + 'location_data', + $$ values (1, '

Up

', '', '

Today

'), + (2, '

Left

', '', '

Tomorrow

') + $$, + 'Should have inserted all location parameters' +); + +select lives_ok( + $$ select setup_location(1, '

Down

', '', '

Never

') $$, + 'Should be able to update location parameters for the first company' +); + +select lives_ok( + $$ select setup_location(2, '

Right

', '', '

Yesterday

') $$, + 'Should be able to update location parameters for the second company' +); + +select bag_eq( + 'location_data', + $$ values (1, '

Down

', '', '

Never

'), + (2, '

Right

', '', '

Yesterday

') + $$, + 'Should have updated all location parameters' +); + +select * +from finish(); + +rollback; diff --git a/test/translate_location.sql b/test/translate_location.sql new file mode 100644 index 0000000..49f5511 --- /dev/null +++ b/test/translate_location.sql @@ -0,0 +1,72 @@ +-- Test translate_location +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_location', array['integer', 'text', 'text', 'text']); +select function_lang_is('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'sql'); +select function_returns('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'void'); +select isnt_definer('camper', 'translate_location', array['integer', 'text', 'text', 'text']); +select volatility_is('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'volatile'); +select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'guest', array[]::text[]); +select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'employee', array[]::text[]); +select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']); +select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]); + + +set client_min_messages to warning; +truncate location_i18n cascade; +truncate location 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 location (company_id, directions, map_embed, opening_dates) +values (2, '

Up

', '', '

Tomorrow

') + , (4, '

Left

', '', '

Today

') +; + +insert into location_i18n (company_id, lang_tag, directions, opening_dates) +values (2, 'ca', '

Uh?

', '

Uh?

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

Uh?

', '

Uh?

') +; + +select lives_ok( + $$ select translate_location(2, 'es', '

Arriba

', '

Mañana

') $$, + 'Should be able to translate the location of the first company to a new language' +); + +select lives_ok( + $$ select translate_location(2, 'ca', '

Dalt

', '

Demà

') $$, + 'Should be able to overwrite a location’s translation' +); + +select lives_ok( + $$ select translate_location(4, 'ca', null, null) $$, + 'Should be able to “translate” a location to empty strings' +); + + +select bag_eq( + $$ select company_id, lang_tag, directions::text, opening_dates::text from location_i18n $$, + $$ values (2, 'ca', '

Dalt

', '

Demà

') + , (2, 'es', '

Arriba

', '

Mañana

') + , (4, 'ca', '', '') + $$, + 'Should have translated all locations' +); + +select * +from finish(); + +rollback; diff --git a/verify/company_geography.sql b/verify/company_geography.sql deleted file mode 100644 index fa997d0..0000000 --- a/verify/company_geography.sql +++ /dev/null @@ -1,16 +0,0 @@ --- Verify camper:company_geography on pg - -begin; - -select company_id - , geog -from camper.company_geography -where false; - -select 1 / count(*) from pg_class where oid = 'camper.company_geography'::regclass and relrowsecurity; -select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.company_geography'::regclass; -select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.company_geography'::regclass; -select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.company_geography'::regclass; -select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.company_geography'::regclass; - -rollback; diff --git a/verify/extension_postgis.sql b/verify/extension_postgis.sql deleted file mode 100644 index d46342a..0000000 --- a/verify/extension_postgis.sql +++ /dev/null @@ -1,10 +0,0 @@ --- Verify camper:extension_postgis on pg - -begin; - -select 1 / count(*) -from pg_extension -where extname = 'postgis' -; - -rollback; diff --git a/verify/location.sql b/verify/location.sql new file mode 100644 index 0000000..4869e54 --- /dev/null +++ b/verify/location.sql @@ -0,0 +1,18 @@ +-- Verify camper:location on pg + +begin; + +select company_id + , directions + , map_embed + , opening_dates +from camper.location +where false; + +select 1 / count(*) from pg_class where oid = 'camper.location'::regclass and relrowsecurity; +select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.location'::regclass; +select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.location'::regclass; +select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.location'::regclass; +select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.location'::regclass; + +rollback; diff --git a/verify/location_i18n.sql b/verify/location_i18n.sql new file mode 100644 index 0000000..3796e54 --- /dev/null +++ b/verify/location_i18n.sql @@ -0,0 +1,12 @@ +-- Verify camper:location_i18n on pg + +begin; + +select company_id + , lang_tag + , directions + , opening_dates +from camper.location_i18n +where false; + +rollback; diff --git a/verify/setup_location.sql b/verify/setup_location.sql new file mode 100644 index 0000000..c2f0bdc --- /dev/null +++ b/verify/setup_location.sql @@ -0,0 +1,7 @@ +-- Verify camper:setup_location on pg + +begin; + +select has_function_privilege('camper.setup_location(integer, text, text, text)', 'execute'); + +rollback; diff --git a/verify/translate_location.sql b/verify/translate_location.sql new file mode 100644 index 0000000..4515a04 --- /dev/null +++ b/verify/translate_location.sql @@ -0,0 +1,7 @@ +-- Verify camper:translate_location on pg + +begin; + +select has_function_privilege('camper.translate_location(integer, text, text, text)', 'execute'); + +rollback; diff --git a/web/static/alpinejs@3.13.3.min.js b/web/static/alpinejs@3.13.3.min.js new file mode 100644 index 0000000..af7df64 --- /dev/null +++ b/web/static/alpinejs@3.13.3.min.js @@ -0,0 +1,5 @@ +(()=>{var tt=!1,rt=!1,V=[],nt=-1;function Vt(e){Sn(e)}function Sn(e){V.includes(e)||V.push(e),An()}function Ee(e){let t=V.indexOf(e);t!==-1&&t>nt&&V.splice(t,1)}function An(){!rt&&!tt&&(tt=!0,queueMicrotask(On))}function On(){tt=!1,rt=!0;for(let e=0;ee.effect(t,{scheduler:r=>{it?Vt(r):r()}}),ot=e.raw}function st(e){k=e}function Wt(e){let t=()=>{};return[n=>{let i=k(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),$(i))},i},()=>{t()}]}function q(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function O(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>O(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)O(n,t,!1),n=n.nextElementSibling}function v(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var Gt=!1;function Jt(){Gt&&v("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),Gt=!0,document.body||v("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` + {{ block "head" . }}{{ end }} @@ -55,6 +56,9 @@
  • {{( pgettext "Services Page" "title" )}}
  • +
  • + {{( pgettext "Location" "title" )}} +
  • {{- end }}
  • + {{- end }} +
  • + {{ range $lang, $input := . -}} + + {{- end }} + {{ template "error-message" . }} + + {{- end }} + {{ with .OpeningDates -}} +
    + {{( pgettext "Opening Dates" "input" )}} + + {{ range $lang, $input := . -}} + + {{- end }} + {{ template "error-message" . }} +
    + {{- end }} + {{ with .MapEmbed -}} + + {{- end }} + +
    + +
    + +{{- end }} diff --git a/web/templates/public/contact.gohtml b/web/templates/public/contact.gohtml deleted file mode 100644 index 7d6391f..0000000 --- a/web/templates/public/contact.gohtml +++ /dev/null @@ -1,62 +0,0 @@ - -{{ define "title" -}} - {{( pgettext "Contact" "title" )}} -{{- end }} - -{{ define "head" -}} - -{{- end }} - -{{ define "content" -}} - {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/app.contactPage*/ -}} - -
    -
    -

    {{( pgettext "Contact" "title" )}}

    - - {{ template "companyAddress" .PublicPage.CompanyAddress }} -

    GPS: 42º 14’ 43,86’’ / 2º 35’ 58,26’’

    -
    - -
    -
    - - {{ if .CompanyGeography -}} - - - {{- end }} -{{- end }} diff --git a/web/templates/public/layout.gohtml b/web/templates/public/layout.gohtml index e11d669..4b696d1 100644 --- a/web/templates/public/layout.gohtml +++ b/web/templates/public/layout.gohtml @@ -50,7 +50,7 @@ {{- end }}
  • {{( pgettext "Services" "title" )}}
  • {{( pgettext "Surroundings" "title" )}}
  • -
  • {{( pgettext "Contact" "title" )}}
  • +
  • {{( pgettext "Location" "title" )}}
  • {{( pgettext "Booking" "title" )}}
  • {{ if .LocalizedAlternates -}}
  • {{ range .LocalizedAlternates -}} @@ -78,7 +78,7 @@
  • {{( pgettext "Campground" "title" )}}
  • {{( pgettext "Services" "title" )}}
  • {{( pgettext "Surroundings" "title" )}}
  • -
  • {{( pgettext "Contact" "title" )}}
  • +
  • {{( pgettext "Location" "title" )}}
  • @@ -96,9 +96,8 @@ {{- end }}
    -

    Obertura 2023

    -

    Camping i Safari tents:
    de 08/04 a 09/10

    -

    Cabanes i Bungalows:
    de 08/04 a 11/12

    +

    {{ (pgettext "Opening" "title" )}}

    + {{ .OpeningDates }}
    diff --git a/web/templates/public/location.gohtml b/web/templates/public/location.gohtml new file mode 100644 index 0000000..97480c4 --- /dev/null +++ b/web/templates/public/location.gohtml @@ -0,0 +1,21 @@ + +{{ define "title" -}} + {{( pgettext "Location" "title" )}} +{{- end }} + +{{ define "content" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/location.locationPage*/ -}} + +

    {{( pgettext "Location" "title" )}}

    +
    +
    + + {{ .Directions }} +
    + + {{ .MapEmbed }} +
    +{{- end }}