diff --git a/cmd/admin.go b/cmd/admin.go
index a0fa4fd..289ef20 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -14,14 +14,15 @@ import (
)
type configScript struct {
- RootURL string `json:"rootURL"`
- FromEmail string `json:"fromEmail"`
- Messengers []string `json:"messengers"`
- MediaProvider string `json:"mediaProvider"`
- NeedsRestart bool `json:"needsRestart"`
- Update *AppUpdate `json:"update"`
- Langs []i18nLang `json:"langs"`
- Lang json.RawMessage `json:"lang"`
+ RootURL string `json:"rootURL"`
+ FromEmail string `json:"fromEmail"`
+ Messengers []string `json:"messengers"`
+ MediaProvider string `json:"mediaProvider"`
+ NeedsRestart bool `json:"needsRestart"`
+ Update *AppUpdate `json:"update"`
+ Langs []i18nLang `json:"langs"`
+ EnablePublicSubPage bool `json:"enablePublicSubscriptionPage"`
+ Lang json.RawMessage `json:"lang"`
}
// handleGetConfigScript returns general configuration as a Javascript
@@ -30,9 +31,10 @@ func handleGetConfigScript(c echo.Context) error {
var (
app = c.Get("app").(*App)
out = configScript{
- RootURL: app.constants.RootURL,
- FromEmail: app.constants.FromEmail,
- MediaProvider: app.constants.MediaProvider,
+ RootURL: app.constants.RootURL,
+ FromEmail: app.constants.FromEmail,
+ MediaProvider: app.constants.MediaProvider,
+ EnablePublicSubPage: app.constants.EnablePublicSubPage,
}
)
diff --git a/cmd/handlers.go b/cmd/handlers.go
index 08ef20a..a399460 100644
--- a/cmd/handlers.go
+++ b/cmd/handlers.go
@@ -126,6 +126,7 @@ func registerHTTPHandlers(e *echo.Echo) {
g.GET("/settings/logs", handleIndexPage)
// Public subscriber facing views.
+ e.GET("/subscription/form", handleSubscriptionFormPage)
e.POST("/subscription/form", handleSubscriptionForm)
e.GET("/subscription/:campUUID/:subUUID", validateUUID(subscriberExists(handleSubscriptionPage),
"campUUID", "subUUID"))
diff --git a/cmd/init.go b/cmd/init.go
index 598c75c..fab4fee 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -40,14 +40,15 @@ const (
// constants contains static, constant config values required by the app.
type constants struct {
- RootURL string `koanf:"root_url"`
- LogoURL string `koanf:"logo_url"`
- FaviconURL string `koanf:"favicon_url"`
- FromEmail string `koanf:"from_email"`
- NotifyEmails []string `koanf:"notify_emails"`
- Lang string `koanf:"lang"`
- DBBatchSize int `koanf:"batch_size"`
- Privacy struct {
+ RootURL string `koanf:"root_url"`
+ LogoURL string `koanf:"logo_url"`
+ FaviconURL string `koanf:"favicon_url"`
+ FromEmail string `koanf:"from_email"`
+ NotifyEmails []string `koanf:"notify_emails"`
+ EnablePublicSubPage bool `koanf:"enable_public_subscription_page"`
+ Lang string `koanf:"lang"`
+ DBBatchSize int `koanf:"batch_size"`
+ Privacy struct {
IndividualTracking bool `koanf:"individual_tracking"`
AllowBlocklist bool `koanf:"allow_blocklist"`
AllowExport bool `koanf:"allow_export"`
diff --git a/cmd/lists.go b/cmd/lists.go
index 19e904c..2bda82c 100644
--- a/cmd/lists.go
+++ b/cmd/lists.go
@@ -50,7 +50,7 @@ func handleGetLists(c echo.Context) error {
order = sortAsc
}
- if err := db.Select(&out.Results, fmt.Sprintf(app.queries.GetLists, orderBy, order), listID, pg.Offset, pg.Limit); err != nil {
+ if err := db.Select(&out.Results, fmt.Sprintf(app.queries.QueryLists, orderBy, order), listID, pg.Offset, pg.Limit); err != nil {
app.log.Printf("error fetching lists: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.Ts("globals.messages.errorFetching",
diff --git a/cmd/public.go b/cmd/public.go
index 8062a96..fdecbb4 100644
--- a/cmd/public.go
+++ b/cmd/public.go
@@ -68,6 +68,11 @@ type msgTpl struct {
Message string
}
+type subFormTpl struct {
+ publicTpl
+ Lists []models.List
+}
+
type subForm struct {
subimporter.SubReq
SubListUUIDs []string `form:"l"`
@@ -251,6 +256,40 @@ func handleOptinPage(c echo.Context) error {
return c.Render(http.StatusOK, "optin", out)
}
+// handleSubscriptionFormPage handles subscription requests coming from public
+// HTML subscription forms.
+func handleSubscriptionFormPage(c echo.Context) error {
+ var (
+ app = c.Get("app").(*App)
+ )
+
+ if !app.constants.EnablePublicSubPage {
+ return c.Render(http.StatusNotFound, tplMessage,
+ makeMsgTpl(app.i18n.T("public.errorTitle"), "",
+ app.i18n.Ts("public.invalidFeature")))
+ }
+
+ // Get all public lists.
+ var lists []models.List
+ if err := app.queries.GetLists.Select(&lists, models.ListTypePublic); err != nil {
+ app.log.Printf("error fetching public lists for form: %s", pqErrMsg(err))
+ return c.Render(http.StatusInternalServerError, tplMessage,
+ makeMsgTpl(app.i18n.T("public.errorTitle"), "",
+ app.i18n.Ts("public.errorFetchingLists")))
+ }
+
+ if len(lists) == 0 {
+ return c.Render(http.StatusInternalServerError, tplMessage,
+ makeMsgTpl(app.i18n.T("public.errorTitle"), "",
+ app.i18n.Ts("public.noListsAvailable")))
+ }
+
+ out := subFormTpl{}
+ out.Title = app.i18n.T("public.sub")
+ out.Lists = lists
+ return c.Render(http.StatusOK, "subscription-form", out)
+}
+
// handleSubscriptionForm handles subscription requests coming from public
// HTML subscription forms.
func handleSubscriptionForm(c echo.Context) error {
@@ -267,7 +306,7 @@ func handleSubscriptionForm(c echo.Context) error {
if len(req.SubListUUIDs) == 0 {
return c.Render(http.StatusBadRequest, tplMessage,
makeMsgTpl(app.i18n.T("public.errorTitle"), "",
- app.i18n.T("globals.messages.invalidUUID")))
+ app.i18n.T("public.noListsSelected")))
}
// If there's no name, use the name bit from the e-mail.
@@ -291,7 +330,7 @@ func handleSubscriptionForm(c echo.Context) error {
}
return c.Render(http.StatusOK, tplMessage,
- makeMsgTpl(app.i18n.T("public.subConfirmedTitle"), "",
+ makeMsgTpl(app.i18n.T("public.subTitle"), "",
app.i18n.Ts("public.subConfirmed")))
}
diff --git a/cmd/queries.go b/cmd/queries.go
index cfbe478..e6f692f 100644
--- a/cmd/queries.go
+++ b/cmd/queries.go
@@ -44,7 +44,8 @@ type Queries struct {
UnsubscribeSubscribersFromListsByQuery string `query:"unsubscribe-subscribers-from-lists-by-query"`
CreateList *sqlx.Stmt `query:"create-list"`
- GetLists string `query:"get-lists"`
+ QueryLists string `query:"query-lists"`
+ GetLists *sqlx.Stmt `query:"get-lists"`
GetListsByOptin *sqlx.Stmt `query:"get-lists-by-optin"`
UpdateList *sqlx.Stmt `query:"update-list"`
UpdateListsDate *sqlx.Stmt `query:"update-lists-date"`
diff --git a/cmd/settings.go b/cmd/settings.go
index 69b9f5e..5b2af27 100644
--- a/cmd/settings.go
+++ b/cmd/settings.go
@@ -14,12 +14,13 @@ import (
)
type settings struct {
- AppRootURL string `json:"app.root_url"`
- AppLogoURL string `json:"app.logo_url"`
- AppFaviconURL string `json:"app.favicon_url"`
- AppFromEmail string `json:"app.from_email"`
- AppNotifyEmails []string `json:"app.notify_emails"`
- AppLang string `json:"app.lang"`
+ AppRootURL string `json:"app.root_url"`
+ AppLogoURL string `json:"app.logo_url"`
+ AppFaviconURL string `json:"app.favicon_url"`
+ AppFromEmail string `json:"app.from_email"`
+ AppNotifyEmails []string `json:"app.notify_emails"`
+ EnablePublicSubPage bool `json:"app.enable_public_subscription_page"`
+ AppLang string `json:"app.lang"`
AppBatchSize int `json:"app.batch_size"`
AppConcurrency int `json:"app.concurrency"`
diff --git a/frontend/src/views/Forms.vue b/frontend/src/views/Forms.vue
index e943988..9fb494f 100644
--- a/frontend/src/views/Forms.vue
+++ b/frontend/src/views/Forms.vue
@@ -15,6 +15,16 @@
:native-value="l.uuid">{{ l.name }}
+
+
+
+
+ {{ $t('forms.publicSubPage') }}
+
+
<form method="post" action="{{ serverConfig.rootURL }}/subscription/form" class="listmonk-form">
+ <form method="post" action="{{ serverConfig.rootURL }}/subscription/form" class="listmonk-form">
<div>
<h3>Subscribe</h3>
- <p><input type="text" name="email" placeholder="E-mail" /></p>
- <p><input type="text" name="name" placeholder="Name (optional)" /></p>
+ <p><input type="text" name="email" placeholder="{{ $t('subscribers.email') }}" /></p>
+ <p><input type="text" name="name" placeholder="{{ $t('public.subName') }}" /></p>
<p>
- <input id="{{ id }}" type="checkbox" name="l" value="{{ l.uuid }}" />
+ <input id="{{ id }}" type="checkbox" name="l" checked value="{{ l.uuid }}" />
<label for="{{ id }}">{{ l.name }}</label>
</p>
- <p><input type="submit" value="Subscribe" /></p>
+
+ <p><input type="submit" value="{{ $t('public.sub') }}" /></p>
</div>
</form>
{{ L.T "public.unsubHelp" }}