Make the slogan user-editable and translatable

Because God forbid we have any performance; everything —**everything**—
must be user editable.
This commit is contained in:
jordi fita mas 2024-01-23 11:52:39 +01:00
parent 2db322a55c
commit e34f253620
26 changed files with 783 additions and 77 deletions

View File

@ -75,6 +75,11 @@ select add_media(52, 'wooden_lodges_carouselB.avif', 'image/avif', decode('m4_es
select add_media(52, 'wooden_lodges_carouselC.avif', 'image/avif', decode('m4_esyscmd([[base64 -w0 demo/wooden_lodges_carouselC.avif]])', 'base64'));
select setup_home(52, 'Vine a gaudir!');
select translate_home(52, 'en', 'Come and enjoy!');
select translate_home(52, 'es', '¡Ven a disfrutar!');
select translate_home(52, 'fr', 'Venez et profitez-en !');
select add_cover_carousel_slide(62, 'Qualitat Calma Natura');
select add_cover_carousel_slide(63, 'El plaer dacampar en plena natura…');
select add_cover_carousel_slide(64, '…amb serveis de 1r. classe');

52
deploy/home.sql Normal file
View File

@ -0,0 +1,52 @@
-- Deploy camper:home to pg
-- requires: roles
-- requires: schema_camper
-- requires: company
-- requires: user_profile
begin;
set search_path to camper, public;
create table home (
company_id integer not null primary key references company,
slogan text not null
);
grant select on table home to guest;
grant select on table home to employee;
grant select, insert, update, delete on table home to admin;
alter table home enable row level security;
create policy guest_ok
on home
for select
using (true)
;
create policy insert_to_company
on home
for insert
with check (
company_id in (select company_id from user_profile)
)
;
create policy update_company
on home
for update
using (
company_id in (select company_id from user_profile)
)
;
create policy delete_from_company
on home
for delete
using (
company_id in (select company_id from user_profile)
)
;
commit;

21
deploy/home_i18n.sql Normal file
View File

@ -0,0 +1,21 @@
-- Deploy camper:home_i18n to pg
-- requires: roles
-- requires: schema_camper
-- requires: home
begin;
set search_path to camper, public;
create table home_i18n (
company_id integer not null references home,
lang_tag text not null references language,
slogan text not null,
primary key (company_id, lang_tag)
);
grant select on table home_i18n to guest;
grant select on table home_i18n to employee;
grant select, insert, update, delete on table home_i18n to admin;
commit;

24
deploy/setup_home.sql Normal file
View File

@ -0,0 +1,24 @@
-- Deploy camper:setup_home to pg
-- requires: roles
-- requires: schema_camper
-- requires: home
begin;
set search_path to camper, public;
create or replace function setup_home(company integer, slogan text) returns void as
$$
insert into home (company_id, slogan)
values (company, coalesce(slogan, ''))
on conflict (company_id) do update
set slogan = excluded.slogan
;
$$
language sql
;
revoke execute on function setup_home(integer, text) from public;
grant execute on function setup_home(integer, text) to admin;
commit;

25
deploy/translate_home.sql Normal file
View File

@ -0,0 +1,25 @@
-- Deploy camper:translate_home to pg
-- requires: roles
-- requires: schema_camper
-- requires: home_i18n
begin;
set search_path to camper, public;
create or replace function translate_home(company_id integer, lang_tag text, slogan text) returns void as
$$
insert into home_i18n (company_id, lang_tag, slogan)
values (company_id, lang_tag, coalesce(slogan, ''))
on conflict (company_id, lang_tag)
do update
set slogan = excluded.slogan
;
$$
language sql
;
revoke execute on function translate_home(integer, text, text) from public;
grant execute on function translate_home(integer, text, text) to admin;
commit;

View File

@ -189,3 +189,13 @@ func (c *Conn) RemoveSurroundingsHighlight(ctx context.Context, id int) error {
_, err := c.Exec(ctx, "select remove_surroundings_highlight($1)", id)
return err
}
func (tx *Tx) SetupHome(ctx context.Context, companyID int, slogan string) error {
_, err := tx.Exec(ctx, "select setup_home($1, $2)", companyID, slogan)
return err
}
func (tx *Tx) TranslateHome(ctx context.Context, companyID int, langTag language.Tag, slogan string) error {
_, err := tx.Exec(ctx, "select translate_home($1, $2, $3)", companyID, langTag, slogan)
return err
}

View File

@ -6,12 +6,17 @@
package home
import (
"context"
"net/http"
"github.com/jackc/pgx/v4"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/carousel"
"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"
)
@ -43,8 +48,10 @@ func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *dat
switch r.Method {
case http.MethodGet:
serveHomeIndex(w, r, user, company, conn)
case http.MethodPut:
updateHome(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
}
case "cover":
h.cover.Handler(user, company, conn).ServeHTTP(w, r)
@ -57,6 +64,14 @@ func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *dat
}
func serveHomeIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
f := newHomeForm(company)
if err := f.FillFromDatabase(r.Context(), company, conn); err != nil {
panic(err)
}
serveHomeIndexWithForm(w, r, user, company, conn, f)
}
func serveHomeIndexWithForm(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *homeForm) {
cover, err := carousel.CollectSlideEntries(r.Context(), company, conn, coverName)
if err != nil {
panic(err)
@ -66,6 +81,7 @@ func serveHomeIndex(w http.ResponseWriter, r *http.Request, user *auth.User, com
panic(err)
}
page := &homeIndex{
Form: f,
Cover: cover,
Slides: slides,
}
@ -73,6 +89,7 @@ func serveHomeIndex(w http.ResponseWriter, r *http.Request, user *auth.User, com
}
type homeIndex struct {
Form *homeForm
Cover []*carousel.SlideEntry
Slides []*carousel.SlideEntry
}
@ -80,3 +97,73 @@ type homeIndex struct {
func (page *homeIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderAdmin(w, r, user, company, "home/index.gohtml", page)
}
func updateHome(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
f := newHomeForm(company)
if ok, err := form.Handle(f, w, r, user); err != nil {
return
} else if !ok {
serveHomeIndexWithForm(w, r, user, company, conn, f)
return
}
tx := conn.MustBegin(r.Context())
defer tx.Rollback(r.Context())
if err := tx.SetupHome(r.Context(), company.ID, f.Slogan[f.DefaultLang].Val); err != nil {
panic(err)
}
for lang := range company.Locales {
l := lang.String()
if l == f.DefaultLang {
continue
}
if err := tx.TranslateHome(r.Context(), company.ID, lang, f.Slogan[l].Val); err != nil {
panic(err)
}
}
tx.MustCommit(r.Context())
httplib.Redirect(w, r, "/admin/home", http.StatusSeeOther)
}
type homeForm struct {
DefaultLang string
Slogan form.I18nInput
}
func newHomeForm(company *auth.Company) *homeForm {
return &homeForm{
DefaultLang: company.DefaultLanguage.String(),
Slogan: form.NewI18nInput(company.Locales, "slogan"),
}
}
func (f *homeForm) FillFromDatabase(ctx context.Context, company *auth.Company, conn *database.Conn) error {
var slogans database.RecordArray
err := conn.QueryRow(ctx, `
select home.slogan
, array_agg((lang_tag, i18n.slogan))
from home
left join home_i18n as i18n using (company_id)
where company_id = $1
group by home.slogan
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, company.ID).Scan(&f.Slogan[f.DefaultLang].Val, &slogans)
if err != nil {
return err
}
if err := f.Slogan.FillArray(slogans); err != nil {
return err
}
return nil
}
func (f *homeForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.Slogan.FillValue(r)
return nil
}
func (f *homeForm) Valid(l *locale.Locale) bool {
v := form.NewValidator(l)
return v.AllOK
}

View File

@ -41,6 +41,7 @@ type homePage struct {
Cover []*carousel.Slide
CampsiteTypes []*campsiteType
Carousel []*carousel.Slide
Slogan string
}
func newHomePage() *homePage {
@ -52,6 +53,7 @@ func (p *homePage) MustRender(w http.ResponseWriter, r *http.Request, user *auth
p.CampsiteTypes = mustCollectCampsiteTypes(r.Context(), company, conn, user.Locale)
p.Cover = carousel.MustCollectSlides(r.Context(), company, conn, user.Locale, coverName)
p.Carousel = carousel.MustCollectSlides(r.Context(), company, conn, user.Locale, carouselName)
p.Slogan = conn.MustGetText(r.Context(), "select coalesce(i18n.slogan, home.slogan) from home left join camper.home_i18n as i18n on i18n.company_id = home.company_id and i18n.lang_tag = $1 where home.company_id = $2", user.Locale.Language, company.ID)
template.MustRenderPublic(w, r, user, company, "home.gohtml", p)
}

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-01-22 20:50+0100\n"
"POT-Creation-Date: 2024-01-23 11:46+0100\n"
"PO-Revision-Date: 2023-07-22 23:45+0200\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -80,10 +80,6 @@ msgstr "El plaer dacampar en plena natura…"
msgid "Our services"
msgstr "Els nostres serveis"
#: web/templates/public/home.gohtml:42
msgid "Come and enjoy!"
msgstr "Vine a gaudir!"
#: web/templates/public/home.gohtml:44
#: web/templates/public/surroundings.gohtml:7
#: web/templates/public/surroundings.gohtml:12
@ -479,6 +475,7 @@ msgstr "Contingut"
#: web/templates/admin/season/form.gohtml:73
#: web/templates/admin/services/form.gohtml:80
#: web/templates/admin/surroundings/form.gohtml:69
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/media/form.gohtml:39
msgctxt "action"
msgid "Update"
@ -632,7 +629,7 @@ msgstr "Carrusel del tipus dallotjament"
#: web/templates/admin/campsite/carousel/index.gohtml:17
#: web/templates/admin/services/index.gohtml:15
#: web/templates/admin/home/index.gohtml:60
#: web/templates/admin/home/index.gohtml:84
msgctxt "action"
msgid "Add slide"
msgstr "Afegeix diapositiva"
@ -640,16 +637,16 @@ msgstr "Afegeix diapositiva"
#: web/templates/admin/campsite/carousel/index.gohtml:30
#: web/templates/admin/services/index.gohtml:28
#: web/templates/admin/surroundings/index.gohtml:28
#: web/templates/admin/home/index.gohtml:28
#: web/templates/admin/home/index.gohtml:73
#: web/templates/admin/home/index.gohtml:52
#: web/templates/admin/home/index.gohtml:97
msgctxt "header"
msgid "Image"
msgstr "Imatge"
#: web/templates/admin/campsite/carousel/index.gohtml:31
#: web/templates/admin/services/index.gohtml:29
#: web/templates/admin/home/index.gohtml:29
#: web/templates/admin/home/index.gohtml:74
#: web/templates/admin/home/index.gohtml:53
#: web/templates/admin/home/index.gohtml:98
msgctxt "header"
msgid "Caption"
msgstr "Llegenda"
@ -660,15 +657,15 @@ msgstr "Llegenda"
#: web/templates/admin/services/index.gohtml:75
#: web/templates/admin/user/index.gohtml:23
#: web/templates/admin/surroundings/index.gohtml:30
#: web/templates/admin/home/index.gohtml:30
#: web/templates/admin/home/index.gohtml:75
#: web/templates/admin/home/index.gohtml:54
#: web/templates/admin/home/index.gohtml:99
msgctxt "header"
msgid "Actions"
msgstr "Accions"
#: web/templates/admin/campsite/carousel/index.gohtml:36
#: web/templates/admin/services/index.gohtml:34
#: web/templates/admin/home/index.gohtml:79
#: web/templates/admin/home/index.gohtml:103
msgid "Are you sure you wish to delete this slide?"
msgstr "Esteu segur de voler esborrar aquesta diapositiva?"
@ -678,15 +675,15 @@ msgstr "Esteu segur de voler esborrar aquesta diapositiva?"
#: web/templates/admin/services/index.gohtml:91
#: web/templates/admin/user/index.gohtml:37
#: web/templates/admin/surroundings/index.gohtml:47
#: web/templates/admin/home/index.gohtml:47
#: web/templates/admin/home/index.gohtml:92
#: web/templates/admin/home/index.gohtml:71
#: web/templates/admin/home/index.gohtml:116
msgctxt "action"
msgid "Delete"
msgstr "Esborra"
#: web/templates/admin/campsite/carousel/index.gohtml:59
#: web/templates/admin/services/index.gohtml:56
#: web/templates/admin/home/index.gohtml:101
#: web/templates/admin/home/index.gohtml:125
msgid "No slides added yet."
msgstr "No sha afegit cap diapositiva encara."
@ -1003,7 +1000,7 @@ msgid "Services Page"
msgstr "Pàgina de serveis"
#: web/templates/admin/services/index.gohtml:14
#: web/templates/admin/home/index.gohtml:59
#: web/templates/admin/home/index.gohtml:83
msgctxt "title"
msgid "Carousel"
msgstr "Carrusel"
@ -1279,21 +1276,31 @@ msgstr "Fil dAriadna"
msgid "Camper Version: %s"
msgstr "Camper versió: %s"
#: web/templates/admin/home/index.gohtml:14
#: web/templates/admin/home/index.gohtml:15
msgctxt "title"
msgid "Slogan"
msgstr "Eslògan"
#: web/templates/admin/home/index.gohtml:21
msgctxt "input"
msgid "Slogan"
msgstr "Eslògan"
#: web/templates/admin/home/index.gohtml:38
msgctxt "title"
msgid "Cover"
msgstr "Portada"
#: web/templates/admin/home/index.gohtml:15
#: web/templates/admin/home/index.gohtml:39
msgctxt "action"
msgid "Add cover image"
msgstr "Afegeix imatge de portada"
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/home/index.gohtml:58
msgid "Are you sure you wish to delete this cover image?"
msgstr "Esteu segur de voler esborrar aquesta imatge de portada?"
#: web/templates/admin/home/index.gohtml:56
#: web/templates/admin/home/index.gohtml:80
msgid "No images added to the cover yet."
msgstr "No sha afegit cap imatge de portada encara."
@ -1443,22 +1450,22 @@ msgstr "No podeu deixar el nom en blanc."
msgid "Name must have at least one letter."
msgstr "El nom ha de tenir com a mínim una lletra."
#: pkg/carousel/admin.go:275 pkg/campsite/types/carousel.go:223
#: pkg/carousel/admin.go:276 pkg/campsite/types/carousel.go:225
msgctxt "input"
msgid "Slide image"
msgstr "Imatge de la diapositiva"
#: pkg/carousel/admin.go:276 pkg/campsite/types/carousel.go:224
#: pkg/carousel/admin.go:277 pkg/campsite/types/carousel.go:226
msgctxt "action"
msgid "Set slide image"
msgstr "Estableix la imatge de la diapositiva"
#: pkg/carousel/admin.go:345 pkg/campsite/types/carousel.go:297
#: pkg/carousel/admin.go:346 pkg/campsite/types/carousel.go:299
#: pkg/surroundings/admin.go:316
msgid "Slide image can not be empty."
msgstr "No podeu deixar la imatge de la diapositiva en blanc."
#: pkg/carousel/admin.go:346 pkg/campsite/types/carousel.go:298
#: pkg/carousel/admin.go:347 pkg/campsite/types/carousel.go:300
#: pkg/surroundings/admin.go:317
msgid "Slide image must be an image media type."
msgstr "La imatge de la diapositiva ha de ser un mèdia de tipus imatge."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-01-22 20:50+0100\n"
"POT-Creation-Date: 2024-01-23 11:46+0100\n"
"PO-Revision-Date: 2023-07-22 23:46+0200\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -80,10 +80,6 @@ msgstr "El placer de acampar en plena naturaleza…"
msgid "Our services"
msgstr "Nuestros servicios"
#: web/templates/public/home.gohtml:42
msgid "Come and enjoy!"
msgstr "¡Ven a disfrutar!"
#: web/templates/public/home.gohtml:44
#: web/templates/public/surroundings.gohtml:7
#: web/templates/public/surroundings.gohtml:12
@ -479,6 +475,7 @@ msgstr "Contenido"
#: web/templates/admin/season/form.gohtml:73
#: web/templates/admin/services/form.gohtml:80
#: web/templates/admin/surroundings/form.gohtml:69
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/media/form.gohtml:39
msgctxt "action"
msgid "Update"
@ -632,7 +629,7 @@ msgstr "Carrusel del tipo de alojamiento"
#: web/templates/admin/campsite/carousel/index.gohtml:17
#: web/templates/admin/services/index.gohtml:15
#: web/templates/admin/home/index.gohtml:60
#: web/templates/admin/home/index.gohtml:84
msgctxt "action"
msgid "Add slide"
msgstr "Añadir diapositiva"
@ -640,16 +637,16 @@ msgstr "Añadir diapositiva"
#: web/templates/admin/campsite/carousel/index.gohtml:30
#: web/templates/admin/services/index.gohtml:28
#: web/templates/admin/surroundings/index.gohtml:28
#: web/templates/admin/home/index.gohtml:28
#: web/templates/admin/home/index.gohtml:73
#: web/templates/admin/home/index.gohtml:52
#: web/templates/admin/home/index.gohtml:97
msgctxt "header"
msgid "Image"
msgstr "Imagen"
#: web/templates/admin/campsite/carousel/index.gohtml:31
#: web/templates/admin/services/index.gohtml:29
#: web/templates/admin/home/index.gohtml:29
#: web/templates/admin/home/index.gohtml:74
#: web/templates/admin/home/index.gohtml:53
#: web/templates/admin/home/index.gohtml:98
msgctxt "header"
msgid "Caption"
msgstr "Leyenda"
@ -660,15 +657,15 @@ msgstr "Leyenda"
#: web/templates/admin/services/index.gohtml:75
#: web/templates/admin/user/index.gohtml:23
#: web/templates/admin/surroundings/index.gohtml:30
#: web/templates/admin/home/index.gohtml:30
#: web/templates/admin/home/index.gohtml:75
#: web/templates/admin/home/index.gohtml:54
#: web/templates/admin/home/index.gohtml:99
msgctxt "header"
msgid "Actions"
msgstr "Acciones"
#: web/templates/admin/campsite/carousel/index.gohtml:36
#: web/templates/admin/services/index.gohtml:34
#: web/templates/admin/home/index.gohtml:79
#: web/templates/admin/home/index.gohtml:103
msgid "Are you sure you wish to delete this slide?"
msgstr "¿Estáis seguro de querer borrar esta diapositiva?"
@ -678,15 +675,15 @@ msgstr "¿Estáis seguro de querer borrar esta diapositiva?"
#: web/templates/admin/services/index.gohtml:91
#: web/templates/admin/user/index.gohtml:37
#: web/templates/admin/surroundings/index.gohtml:47
#: web/templates/admin/home/index.gohtml:47
#: web/templates/admin/home/index.gohtml:92
#: web/templates/admin/home/index.gohtml:71
#: web/templates/admin/home/index.gohtml:116
msgctxt "action"
msgid "Delete"
msgstr "Borrar"
#: web/templates/admin/campsite/carousel/index.gohtml:59
#: web/templates/admin/services/index.gohtml:56
#: web/templates/admin/home/index.gohtml:101
#: web/templates/admin/home/index.gohtml:125
msgid "No slides added yet."
msgstr "No se ha añadido ninguna diapositiva todavía."
@ -1003,7 +1000,7 @@ msgid "Services Page"
msgstr "Página de servicios"
#: web/templates/admin/services/index.gohtml:14
#: web/templates/admin/home/index.gohtml:59
#: web/templates/admin/home/index.gohtml:83
msgctxt "title"
msgid "Carousel"
msgstr "Carrusel"
@ -1279,21 +1276,31 @@ msgstr "Migas de pan"
msgid "Camper Version: %s"
msgstr "Camper versión: %s"
#: web/templates/admin/home/index.gohtml:14
#: web/templates/admin/home/index.gohtml:15
msgctxt "title"
msgid "Slogan"
msgstr "Eslogan"
#: web/templates/admin/home/index.gohtml:21
msgctxt "input"
msgid "Slogan"
msgstr "Eslogan"
#: web/templates/admin/home/index.gohtml:38
msgctxt "title"
msgid "Cover"
msgstr "Portada"
#: web/templates/admin/home/index.gohtml:15
#: web/templates/admin/home/index.gohtml:39
msgctxt "action"
msgid "Add cover image"
msgstr "Añadir imagen de portada"
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/home/index.gohtml:58
msgid "Are you sure you wish to delete this cover image?"
msgstr "¿Estáis seguro de querer borrar esta imagen de portada?"
#: web/templates/admin/home/index.gohtml:56
#: web/templates/admin/home/index.gohtml:80
msgid "No images added to the cover yet."
msgstr "No se ha añadido ninguna imagen de portada todavía."
@ -1443,22 +1450,22 @@ msgstr "No podéis dejar el nombre en blanco."
msgid "Name must have at least one letter."
msgstr "El nombre tiene que tener como mínimo una letra."
#: pkg/carousel/admin.go:275 pkg/campsite/types/carousel.go:223
#: pkg/carousel/admin.go:276 pkg/campsite/types/carousel.go:225
msgctxt "input"
msgid "Slide image"
msgstr "Imagen de la diapositiva"
#: pkg/carousel/admin.go:276 pkg/campsite/types/carousel.go:224
#: pkg/carousel/admin.go:277 pkg/campsite/types/carousel.go:226
msgctxt "action"
msgid "Set slide image"
msgstr "Establecer la imagen de la diapositiva"
#: pkg/carousel/admin.go:345 pkg/campsite/types/carousel.go:297
#: pkg/carousel/admin.go:346 pkg/campsite/types/carousel.go:299
#: pkg/surroundings/admin.go:316
msgid "Slide image can not be empty."
msgstr "No podéis dejar la imagen de la diapositiva en blanco."
#: pkg/carousel/admin.go:346 pkg/campsite/types/carousel.go:298
#: pkg/carousel/admin.go:347 pkg/campsite/types/carousel.go:300
#: pkg/surroundings/admin.go:317
msgid "Slide image must be an image media type."
msgstr "La imagen de la diapositiva tiene que ser un medio de tipo imagen."

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-01-22 20:50+0100\n"
"POT-Creation-Date: 2024-01-23 11:46+0100\n"
"PO-Revision-Date: 2023-12-20 10:13+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n"
@ -81,10 +81,6 @@ msgstr "Le plaisir de camper en pleine nature…"
msgid "Our services"
msgstr "Nos services"
#: web/templates/public/home.gohtml:42
msgid "Come and enjoy!"
msgstr "Venez et profitez-en !"
#: web/templates/public/home.gohtml:44
#: web/templates/public/surroundings.gohtml:7
#: web/templates/public/surroundings.gohtml:12
@ -480,6 +476,7 @@ msgstr "Contenu"
#: web/templates/admin/season/form.gohtml:73
#: web/templates/admin/services/form.gohtml:80
#: web/templates/admin/surroundings/form.gohtml:69
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/media/form.gohtml:39
msgctxt "action"
msgid "Update"
@ -633,7 +630,7 @@ msgstr "Type de camping Carrousel"
#: web/templates/admin/campsite/carousel/index.gohtml:17
#: web/templates/admin/services/index.gohtml:15
#: web/templates/admin/home/index.gohtml:60
#: web/templates/admin/home/index.gohtml:84
msgctxt "action"
msgid "Add slide"
msgstr "Ajouter la diapositive"
@ -641,16 +638,16 @@ msgstr "Ajouter la diapositive"
#: web/templates/admin/campsite/carousel/index.gohtml:30
#: web/templates/admin/services/index.gohtml:28
#: web/templates/admin/surroundings/index.gohtml:28
#: web/templates/admin/home/index.gohtml:28
#: web/templates/admin/home/index.gohtml:73
#: web/templates/admin/home/index.gohtml:52
#: web/templates/admin/home/index.gohtml:97
msgctxt "header"
msgid "Image"
msgstr "Image"
#: web/templates/admin/campsite/carousel/index.gohtml:31
#: web/templates/admin/services/index.gohtml:29
#: web/templates/admin/home/index.gohtml:29
#: web/templates/admin/home/index.gohtml:74
#: web/templates/admin/home/index.gohtml:53
#: web/templates/admin/home/index.gohtml:98
msgctxt "header"
msgid "Caption"
msgstr "Légende"
@ -661,15 +658,15 @@ msgstr "Légende"
#: web/templates/admin/services/index.gohtml:75
#: web/templates/admin/user/index.gohtml:23
#: web/templates/admin/surroundings/index.gohtml:30
#: web/templates/admin/home/index.gohtml:30
#: web/templates/admin/home/index.gohtml:75
#: web/templates/admin/home/index.gohtml:54
#: web/templates/admin/home/index.gohtml:99
msgctxt "header"
msgid "Actions"
msgstr "Actions"
#: web/templates/admin/campsite/carousel/index.gohtml:36
#: web/templates/admin/services/index.gohtml:34
#: web/templates/admin/home/index.gohtml:79
#: web/templates/admin/home/index.gohtml:103
msgid "Are you sure you wish to delete this slide?"
msgstr "Êtes-vous sûr de vouloir supprimer cette diapositive ?"
@ -679,15 +676,15 @@ msgstr "Êtes-vous sûr de vouloir supprimer cette diapositive ?"
#: web/templates/admin/services/index.gohtml:91
#: web/templates/admin/user/index.gohtml:37
#: web/templates/admin/surroundings/index.gohtml:47
#: web/templates/admin/home/index.gohtml:47
#: web/templates/admin/home/index.gohtml:92
#: web/templates/admin/home/index.gohtml:71
#: web/templates/admin/home/index.gohtml:116
msgctxt "action"
msgid "Delete"
msgstr "Supprimer"
#: web/templates/admin/campsite/carousel/index.gohtml:59
#: web/templates/admin/services/index.gohtml:56
#: web/templates/admin/home/index.gohtml:101
#: web/templates/admin/home/index.gohtml:125
msgid "No slides added yet."
msgstr "Aucune diapositive na encore été ajoutée."
@ -1004,7 +1001,7 @@ msgid "Services Page"
msgstr "La page des services"
#: web/templates/admin/services/index.gohtml:14
#: web/templates/admin/home/index.gohtml:59
#: web/templates/admin/home/index.gohtml:83
msgctxt "title"
msgid "Carousel"
msgstr "Carrousel"
@ -1280,21 +1277,31 @@ msgstr "Fil dAriane"
msgid "Camper Version: %s"
msgstr "Camper version: %s"
#: web/templates/admin/home/index.gohtml:14
#: web/templates/admin/home/index.gohtml:15
msgctxt "title"
msgid "Slogan"
msgstr "Slogan"
#: web/templates/admin/home/index.gohtml:21
msgctxt "input"
msgid "Slogan"
msgstr "Slogan"
#: web/templates/admin/home/index.gohtml:38
msgctxt "title"
msgid "Cover"
msgstr "Couverture"
#: web/templates/admin/home/index.gohtml:15
#: web/templates/admin/home/index.gohtml:39
msgctxt "action"
msgid "Add cover image"
msgstr "Ajouter une image de couverture"
#: web/templates/admin/home/index.gohtml:34
#: web/templates/admin/home/index.gohtml:58
msgid "Are you sure you wish to delete this cover image?"
msgstr "Êtes-vous sûr de vouloir supprimer cette image de couverture ?"
#: web/templates/admin/home/index.gohtml:56
#: web/templates/admin/home/index.gohtml:80
msgid "No images added to the cover yet."
msgstr "Aucune image de couverture na encore été ajoutée."
@ -1444,22 +1451,22 @@ msgstr "Le nom ne peut pas être laissé vide."
msgid "Name must have at least one letter."
msgstr "Le nom doit comporter au moins une lettre."
#: pkg/carousel/admin.go:275 pkg/campsite/types/carousel.go:223
#: pkg/carousel/admin.go:276 pkg/campsite/types/carousel.go:225
msgctxt "input"
msgid "Slide image"
msgstr "Image du diaporama"
#: pkg/carousel/admin.go:276 pkg/campsite/types/carousel.go:224
#: pkg/carousel/admin.go:277 pkg/campsite/types/carousel.go:226
msgctxt "action"
msgid "Set slide image"
msgstr "Définir limage de la diapositive"
#: pkg/carousel/admin.go:345 pkg/campsite/types/carousel.go:297
#: pkg/carousel/admin.go:346 pkg/campsite/types/carousel.go:299
#: pkg/surroundings/admin.go:316
msgid "Slide image can not be empty."
msgstr "Limage de la diapositive ne peut pas être vide."
#: pkg/carousel/admin.go:346 pkg/campsite/types/carousel.go:298
#: pkg/carousel/admin.go:347 pkg/campsite/types/carousel.go:300
#: pkg/surroundings/admin.go:317
msgid "Slide image must be an image media type."
msgstr "Limage de la diapositive doit être de type média dimage."

7
revert/home.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:home from pg
begin;
drop table if exists camper.home;
commit;

7
revert/home_i18n.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:home_i18n from pg
begin;
drop table if exists camper.home_i18n;
commit;

7
revert/setup_home.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:setup_home from pg
begin;
drop function if exists camper.setup_home(integer, text);
commit;

View File

@ -0,0 +1,7 @@
-- Revert camper:translate_home from pg
begin;
drop function if exists camper.translate_home(integer, text, text);
commit;

View File

@ -168,3 +168,7 @@ edit_campsite_type [edit_campsite_type@v1 campsite_type__check_in_out] 2024-01-2
campsite_type_i18n__check_in_out [campsite_type_i18n] 2024-01-22T18:07:21Z jordi fita mas <jordi@tandem.blog> # Add check_in and check_out fields to campsite_type_i18n
translate_campsite_type [translate_campsite_type@v1 campsite_type_i18n__check_in_out] 2024-01-22T18:14:26Z jordi fita mas <jordi@tandem.blog> # Add check_in and check_out parameters to translate_campsite_type function
remove_campsite_type_option [roles schema_camper campsite_type_option campsite_type_option_i18n campsite_type_option_cost] 2024-01-22T19:25:03Z jordi fita mas <jordi@tandem.blog> # Add function to remove campsite type options
home [roles schema_camper company user_profile] 2024-01-23T10:02:08Z jordi fita mas <jordi@tandem.blog> # Add table to hold texts for home page
setup_home [roles schema_camper home] 2024-01-23T10:14:14Z jordi fita mas <jordi@tandem.blog> # Add function to set up home page
home_i18n [roles schema_camper home] 2024-01-23T10:19:47Z jordi fita mas <jordi@tandem.blog> # Add table to hold translated texts for home page
translate_home [roles schema_camper home_i18n] 2024-01-23T10:24:02Z jordi fita mas <jordi@tandem.blog> # Add function to translate home texts

167
test/home.sql Normal file
View File

@ -0,0 +1,167 @@
-- Test home
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('home');
select has_pk('home');
select table_privs_are('home', 'guest', array['SELECT']);
select table_privs_are('home', 'employee', array['SELECT']);
select table_privs_are('home', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('home', 'authenticator', array[]::text[]);
select has_column('home', 'company_id');
select col_is_pk('home', 'company_id');
select col_is_fk('home', 'company_id');
select fk_ok('home', 'company_id', 'company', 'company_id');
select col_type_is('home', 'company_id', 'integer');
select col_not_null('home', 'company_id');
select col_hasnt_default('home', 'company_id');
select has_column('home', 'slogan');
select col_type_is('home', 'slogan', 'text');
select col_not_null('home', 'slogan');
select col_hasnt_default('home', 'slogan');
set client_min_messages to warning;
truncate home 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, tourist_tax, country_code, currency_code, default_lang_tag)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca')
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 'FR', 'USD', 'ca')
, (6, 'Company 5', 'XX345', '', '777-777-777', 'c@c', '', '', '', '', '', '', 60, '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 home (company_id, slogan)
values (2, 'Slogan 2')
, (4, 'Slogan 4')
;
prepare home_data as
select company_id, slogan
from home
order by company_id, slogan;
set role guest;
select bag_eq(
'home_data',
$$ values (2, 'Slogan 2')
, (4, 'Slogan 4')
$$,
'Everyone should be able to list all home texts across all companies'
);
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
select lives_ok(
$$ delete from home where company_id = 2 $$,
'Admin from company 2 should be able to delete home texts from that company.'
);
select bag_eq(
'home_data',
$$ values (4, 'Slogan 4')
$$,
'The row should have been deleted.'
);
select lives_ok(
$$ insert into home(company_id, slogan) values (2, 'Another Slogan') $$,
'Admin from company 2 should be able to insert a new home texts to that company.'
);
select bag_eq(
'home_data',
$$ values (2, 'Another Slogan')
, (4, 'Slogan 4')
$$,
'The new row should have been added'
);
select lives_ok(
$$ update home set slogan = 'Slogan 2' where company_id = 2 $$,
'Admin from company 2 should be able to update home texts of that company.'
);
select bag_eq(
'home_data',
$$ values (2, 'Slogan 2')
, (4, 'Slogan 4')
$$,
'The row should have been updated.'
);
select throws_ok(
$$ insert into home (company_id, slogan) values (6, 'Slogan 6') $$,
'42501', 'new row violates row-level security policy for table "home"',
'Admin from company 2 should NOT be able to insert new home texts to company 6.'
);
select lives_ok(
$$ update home set slogan = 'Nope' where company_id = 4 $$,
'Admin from company 2 should not be able to update new home texts of company 4, but no error if company_id is not changed.'
);
select bag_eq(
'home_data',
$$ values (2, 'Slogan 2')
, (4, 'Slogan 4')
$$,
'No row should have been changed.'
);
select throws_ok(
$$ update home set company_id = 6 where company_id = 2 $$,
'42501', 'new row violates row-level security policy for table "home"',
'Admin from company 2 should NOT be able to move home texts to company 6'
);
select lives_ok(
$$ delete from home where company_id = 4 $$,
'Admin from company 2 should NOT be able to delete home texts from company 4, but not error is thrown'
);
select bag_eq(
'home_data',
$$ values (2, 'Slogan 2')
, (4, 'Slogan 4')
$$,
'No row should have been changed'
);
reset role;
select *
from finish();
rollback;

44
test/home_i18n.sql Normal file
View File

@ -0,0 +1,44 @@
-- Test home_i18n
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(23);
set search_path to camper, public;
select has_table('home_i18n');
select has_pk('home_i18n');
select col_is_pk('home_i18n', array['company_id', 'lang_tag']);
select table_privs_are('home_i18n', 'guest', array['SELECT']);
select table_privs_are('home_i18n', 'employee', array['SELECT']);
select table_privs_are('home_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('home_i18n', 'authenticator', array[]::text[]);
select has_column('home_i18n', 'company_id');
select col_is_fk('home_i18n', 'company_id');
select fk_ok('home_i18n', 'company_id', 'home', 'company_id');
select col_type_is('home_i18n', 'company_id', 'integer');
select col_not_null('home_i18n', 'company_id');
select col_hasnt_default('home_i18n', 'company_id');
select has_column('home_i18n', 'lang_tag');
select col_is_fk('home_i18n', 'lang_tag');
select fk_ok('home_i18n', 'lang_tag', 'language', 'lang_tag');
select col_type_is('home_i18n', 'lang_tag', 'text');
select col_not_null('home_i18n', 'lang_tag');
select col_hasnt_default('home_i18n', 'lang_tag');
select has_column('home_i18n', 'slogan');
select col_type_is('home_i18n', 'slogan', 'text');
select col_not_null('home_i18n', 'slogan');
select col_hasnt_default('home_i18n', 'slogan');
select *
from finish();
rollback;

78
test/setup_home.sql Normal file
View File

@ -0,0 +1,78 @@
-- Test setup_home
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_home', array['integer', 'text']);
select function_lang_is('camper', 'setup_home', array['integer', 'text'], 'sql');
select function_returns('camper', 'setup_home', array['integer', 'text'], 'void');
select isnt_definer('camper', 'setup_home', array['integer', 'text']);
select volatility_is('camper', 'setup_home', array['integer', 'text'], 'volatile');
select function_privs_are('camper', 'setup_home', array ['integer', 'text'], 'guest', array[]::text[]);
select function_privs_are('camper', 'setup_home', array ['integer', 'text'], 'employee', array[]::text[]);
select function_privs_are('camper', 'setup_home', array ['integer', 'text'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'setup_home', array ['integer', 'text'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate home 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, tourist_tax, country_code, currency_code, default_lang_tag)
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca')
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 'FR', 'USD', 'ca')
;
prepare home_data as
select company_id, slogan
from home
;
select lives_ok(
$$ select setup_home(1, 'Enjoy!') $$,
'Should be able to setup home for the first company'
);
select lives_ok(
$$ select setup_home(2, 'Go Away!') $$,
'Should be able to setup home for the second company'
);
select bag_eq(
'home_data',
$$ values (1, 'Enjoy!')
, (2, 'Go Away!')
$$,
'Should have inserted all home texts'
);
select lives_ok(
$$ select setup_home(1, 'Come Back!') $$,
'Should be able to update home for the first company'
);
select lives_ok(
$$ select setup_home(2, 'Quoi?') $$,
'Should be able to update home for the second company'
);
select bag_eq(
'home_data',
$$ values (1, 'Come Back!')
, (2, 'Quoi?')
$$,
'Should have updated all home texts'
);
select *
from finish();
rollback;

73
test/translate_home.sql Normal file
View File

@ -0,0 +1,73 @@
-- Test translate_home
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_home', array['integer', 'text', 'text']);
select function_lang_is('camper', 'translate_home', array['integer', 'text', 'text'], 'sql');
select function_returns('camper', 'translate_home', array['integer', 'text', 'text'], 'void');
select isnt_definer('camper', 'translate_home', array['integer', 'text', 'text']);
select volatility_is('camper', 'translate_home', array['integer', 'text', 'text'], 'volatile');
select function_privs_are('camper', 'translate_home', array['integer', 'text', 'text'], 'guest', array[]::text[]);
select function_privs_are('camper', 'translate_home', array['integer', 'text', 'text'], 'employee', array[]::text[]);
select function_privs_are('camper', 'translate_home', array['integer', 'text', 'text'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'translate_home', array['integer', 'text', 'text'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate home_i18n cascade;
truncate home 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, tourist_tax, country_code, currency_code, default_lang_tag)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 'ES', 'EUR', 'ca')
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 'FR', 'USD', 'ca')
;
insert into home (company_id, slogan)
values (2, 'Enjoy')
, (4, 'Away')
;
insert into home_i18n (company_id, lang_tag, slogan)
values (2, 'ca', 'Hola')
, (4, 'ca', 'Hola')
;
select lives_ok(
$$ select translate_home(2, 'es', 'Disfruta') $$,
'Should be able to translate the home of the first company to a new language'
);
select lives_ok(
$$ select translate_home(2, 'ca', 'Gaudeix') $$,
'Should be able to overwrite a homes translation'
);
select lives_ok(
$$ select translate_home(4, 'ca', null) $$,
'Should be able to “translate” a home to empty strings'
);
select bag_eq(
$$ select company_id, lang_tag, slogan from home_i18n $$,
$$ values (2, 'ca', 'Gaudeix')
, (2, 'es', 'Disfruta')
, (4, 'ca', '')
$$,
'Should have translated all home texts'
);
select *
from finish();
rollback;

16
verify/home.sql Normal file
View File

@ -0,0 +1,16 @@
-- Verify camper:home on pg
begin;
select company_id
, slogan
from camper.home
where false;
select 1 / count(*) from pg_class where oid = 'camper.home'::regclass and relrowsecurity;
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.home'::regclass;
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.home'::regclass;
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.home'::regclass;
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.home'::regclass;
rollback;

11
verify/home_i18n.sql Normal file
View File

@ -0,0 +1,11 @@
-- Verify camper:home_i18n on pg
begin;
select company_id
, lang_tag
, slogan
from camper.home_i18n
where false;
rollback;

7
verify/setup_home.sql Normal file
View File

@ -0,0 +1,7 @@
-- Verify camper:setup_home on pg
begin;
select has_function_privilege('camper.setup_home(integer, text)', 'execute');
rollback;

View File

@ -0,0 +1,7 @@
-- Verify camper:translate_home on pg
begin;
select has_function_privilege('camper.translate_home(integer, text, text)', 'execute');
rollback;

View File

@ -11,6 +11,30 @@
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/home.homeIndex*/ -}}
{{ with .Form }}
<h2>{{( pgettext "Slogan" "title")}}</h2>
<form data-hx-put="/admin/home">
{{ CSRFInput }}
<fieldset {{ template "init-lang" . }}>
{{ with .Slogan -}}
<fieldset>
<legend>{{( pgettext "Slogan" "input")}}</legend>
{{ template "lang-selector" . }}
{{ range $lang, $input := . -}}
<label x-cloak x-show="lang === '{{ $lang }}'"><span>{{ $lang }}</span><br>
<input type="text" name="{{ $input.Name }}" value="{{ $input.Val }}"
{{ template "error-attrs" . }}><br>
</label>
{{- end }}
{{ template "error-message" . }}
</fieldset>
{{- end }}
</fieldset>
<footer>
<button type="submit">{{( pgettext "Update" "action" )}}</button>
</footer>
</form>
{{- end }}
<h2>{{( pgettext "Cover" "title" )}}</h2>
<a href="/admin/home/cover/new">{{( pgettext "Add cover image" "action" )}}</a>
{{ if .Cover -}}

View File

@ -39,7 +39,7 @@
<section class="services">
<h2><a href="/{{ currentLocale }}/services">{{( gettext "Our services")}} <span>→</span></a></h2>
</section>
<p class="enjoy">{{( gettext "Come and enjoy!")}}</p>
<p class="enjoy">{{ .Slogan }}</p>
<section class="surroundings">
<h2 class="sr-only">{{( pgettext "Surroundings" "title" )}}</h2>
<div class="carousel">