diff --git a/cmd/handlers.go b/cmd/handlers.go
index 2cd7fa9..63c6ba1 100644
--- a/cmd/handlers.go
+++ b/cmd/handlers.go
@@ -55,6 +55,8 @@ func initHTTPHandlers(e *echo.Echo, app *App) {
return c.Render(http.StatusOK, "home", publicTpl{Title: "listmonk"})
})
g.GET(path.Join(adminRoot, ""), handleAdminPage)
+ g.GET(path.Join(adminRoot, "/custom.css"), serveCustomApperance("admin.custom_css"))
+ g.GET(path.Join(adminRoot, "/custom.js"), serveCustomApperance("admin.custom_js"))
g.GET(path.Join(adminRoot, "/*"), handleAdminPage)
// API endpoints.
@@ -142,6 +144,7 @@ func initHTTPHandlers(e *echo.Echo, app *App) {
e.POST("/webhooks/service/:service", handleBounceWebhook)
}
+ // /public/static/* file server is registered in initHTTPServer().
// Public subscriber facing views.
e.GET("/subscription/form", handleSubscriptionFormPage)
e.POST("/subscription/form", handleSubscriptionForm)
@@ -161,6 +164,10 @@ func initHTTPHandlers(e *echo.Echo, app *App) {
"campUUID", "subUUID")))
e.GET("/campaign/:campUUID/:subUUID/px.png", noIndex(validateUUID(handleRegisterCampaignView,
"campUUID", "subUUID")))
+
+ e.GET("/public/custom.css", serveCustomApperance("public.custom_css"))
+ e.GET("/public/custom.js", serveCustomApperance("public.custom_js"))
+
// Public health API endpoint.
e.GET("/health", handleHealthCheck)
}
@@ -182,6 +189,39 @@ func handleHealthCheck(c echo.Context) error {
return c.JSON(http.StatusOK, okResp{true})
}
+// serveCustomApperance serves the given custom CSS/JS apperance blob
+// meant for customizing public and admin pages from the admin settings UI.
+func serveCustomApperance(name string) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ var (
+ app = c.Get("app").(*App)
+
+ out []byte
+ hdr string
+ )
+
+ switch name {
+ case "admin.custom_css":
+ out = app.constants.Appearance.AdminCSS
+ hdr = "text/css; charset=utf-8"
+
+ case "admin.custom_js":
+ out = app.constants.Appearance.AdminJS
+ hdr = "application/javascript; charset=utf-8"
+
+ case "public.custom_css":
+ out = app.constants.Appearance.PublicCSS
+ hdr = "text/css; charset=utf-8"
+
+ case "public.custom_js":
+ out = app.constants.Appearance.PublicJS
+ hdr = "application/javascript; charset=utf-8"
+ }
+
+ return c.Blob(http.StatusOK, hdr, out)
+ }
+}
+
// basicAuth middleware does an HTTP BasicAuth authentication for admin handlers.
func basicAuth(username, password string, c echo.Context) (bool, error) {
app := c.Get("app").(*App)
diff --git a/cmd/init.go b/cmd/init.go
index 8cd6d40..179d1da 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -68,6 +68,13 @@ type constants struct {
AdminUsername []byte `koanf:"admin_username"`
AdminPassword []byte `koanf:"admin_password"`
+ Appearance struct {
+ AdminCSS []byte `koanf:"admin.custom_css"`
+ AdminJS []byte `koanf:"admin.custom_js"`
+ PublicCSS []byte `koanf:"public.custom_css"`
+ PublicJS []byte `koanf:"public.custom_js"`
+ }
+
UnsubURL string
LinkTrackURL string
ViewTrackURL string
@@ -293,7 +300,10 @@ func initConstants() *constants {
lo.Fatalf("error loading app config: %v", err)
}
if err := ko.Unmarshal("privacy", &c.Privacy); err != nil {
- lo.Fatalf("error loading app config: %v", err)
+ lo.Fatalf("error loading app.privacy config: %v", err)
+ }
+ if err := ko.UnmarshalWithConf("appearance", &c.Appearance, koanf.UnmarshalConf{FlatPaths: true}); err != nil {
+ lo.Fatalf("error loading app.appearance config: %v", err)
}
c.RootURL = strings.TrimRight(c.RootURL, "/")
@@ -622,7 +632,7 @@ func initHTTPServer(app *App) *echo.Echo {
fSrv := app.fs.FileServer()
// Public (subscriber) facing static files.
- srv.GET("/public/*", echo.WrapHandler(fSrv))
+ srv.GET("/public/static/*", echo.WrapHandler(fSrv))
// Admin (frontend) facing static files.
srv.GET("/admin/static/*", echo.WrapHandler(fSrv))
diff --git a/cmd/settings.go b/cmd/settings.go
index 323a09f..2ae9fbf 100644
--- a/cmd/settings.go
+++ b/cmd/settings.go
@@ -105,6 +105,11 @@ type settings struct {
TLSSkipVerify bool `json:"tls_skip_verify"`
ScanInterval string `json:"scan_interval"`
} `json:"bounce.mailboxes"`
+
+ AdminCustomCSS string `json:"appearance.admin.custom_css"`
+ AdminCustomJS string `json:"appearance.admin.custom_js"`
+ PublicCustomCSS string `json:"appearance.public.custom_css"`
+ PublicCustomJS string `json:"appearance.public.custom_js"`
}
var (
diff --git a/cmd/upgrade.go b/cmd/upgrade.go
index a27bc91..46b590a 100644
--- a/cmd/upgrade.go
+++ b/cmd/upgrade.go
@@ -31,6 +31,7 @@ var migList = []migFunc{
{"v0.9.0", migrations.V0_9_0},
{"v1.0.0", migrations.V1_0_0},
{"v2.0.0", migrations.V2_0_0},
+ {"v2.1.0", migrations.V2_1_0},
}
// upgrade upgrades the database to the current version by running SQL migration files
diff --git a/frontend/public/index.html b/frontend/public/index.html
index b0b0bb0..f4ea307 100644
--- a/frontend/public/index.html
+++ b/frontend/public/index.html
@@ -5,6 +5,8 @@
+
+
<%= htmlWebpackPlugin.options.title %>
diff --git a/frontend/src/assets/style.scss b/frontend/src/assets/style.scss
index 0e0fc12..720b2b7 100644
--- a/frontend/src/assets/style.scss
+++ b/frontend/src/assets/style.scss
@@ -802,6 +802,10 @@ section.analytics {
.box {
margin-bottom: 30px;
}
+ .html-editor {
+ height: auto;
+ min-height: 350px;
+ }
}
/* Logs */
diff --git a/frontend/src/components/HTMLEditor.vue b/frontend/src/components/HTMLEditor.vue
index 00d28f8..7ab5077 100644
--- a/frontend/src/components/HTMLEditor.vue
+++ b/frontend/src/components/HTMLEditor.vue
@@ -9,6 +9,10 @@ import { colors } from '../constants';
export default {
props: {
value: String,
+ language: {
+ type: String,
+ default: 'html',
+ },
disabled: Boolean,
},
@@ -38,7 +42,7 @@ export default {
this.$refs.htmlEditor.appendChild(el);
this.flask = new CodeFlask(el.shadowRoot.getElementById('area'), {
- language: 'html',
+ language: this.$props.language,
lineNumbers: false,
styleParent: el.shadowRoot,
readonly: this.disabled,
diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue
index 8d7891d..0f01d13 100644
--- a/frontend/src/views/Settings.vue
+++ b/frontend/src/views/Settings.vue
@@ -49,6 +49,10 @@
+
+
+
+
@@ -66,6 +70,7 @@ import MediaSettings from './settings/media.vue';
import SmtpSettings from './settings/smtp.vue';
import BounceSettings from './settings/bounces.vue';
import MessengerSettings from './settings/messengers.vue';
+import AppearanceSettings from './settings/appearance.vue';
const dummyPassword = ' '.repeat(8);
@@ -78,6 +83,7 @@ export default Vue.extend({
SmtpSettings,
BounceSettings,
MessengerSettings,
+ AppearanceSettings,
},
data() {
diff --git a/frontend/src/views/settings/appearance.vue b/frontend/src/views/settings/appearance.vue
new file mode 100644
index 0000000..aa2e078
--- /dev/null
+++ b/frontend/src/views/settings/appearance.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+ {{ $t('settings.appearance.adminHelp') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('settings.appearance.publicHelp') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/vue.config.js b/frontend/vue.config.js
index 1d40c67..a5950bb 100644
--- a/frontend/vue.config.js
+++ b/frontend/vue.config.js
@@ -29,7 +29,10 @@ module.exports = {
'^/$': {
target: process.env.LISTMONK_API_URL || 'http://127.0.0.1:9000'
},
- '^/(api|webhooks|subscription|public)': {
+ '^/(api|webhooks|subscription|public|health)': {
+ target: process.env.LISTMONK_API_URL || 'http://127.0.0.1:9000'
+ },
+ '^/(admin\/custom\.(css|js))': {
target: process.env.LISTMONK_API_URL || 'http://127.0.0.1:9000'
}
}
diff --git a/i18n/cs-cz.json b/i18n/cs-cz.json
index b54fb21..26fd41c 100644
--- a/i18n/cs-cz.json
+++ b/i18n/cs-cz.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Odběr jste zrušili úspěšně.",
"public.unsubbedTitle": "Zrušen odběr",
"public.unsubscribeTitle": "Zrušit odběr ze seznamu adresátů",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Akce",
"settings.bounces.blocklist": "Seznam blokovaných",
"settings.bounces.count": "Počet případů nedoručitelnosti",
diff --git a/i18n/de.json b/i18n/de.json
index f164464..663fc25 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Du wurdest erfolgreich abgemeldet",
"public.unsubbedTitle": "Abgemeldet",
"public.unsubscribeTitle": "Von E-Mail Liste abmelden.",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Aktion",
"settings.bounces.blocklist": "Sperrliste",
"settings.bounces.count": "Bounce Anzahl",
diff --git a/i18n/en.json b/i18n/en.json
index 99d9c49..c578918 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "You have unsubscribed successfully.",
"public.unsubbedTitle": "Unsubscribed",
"public.unsubscribeTitle": "Unsubscribe from mailing list",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Blocklist",
"settings.bounces.count": "Bounce count",
diff --git a/i18n/es.json b/i18n/es.json
index 3abcf8b..865922d 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Ud. se ha des-subscrito de forma satisfactoria",
"public.unsubbedTitle": "Des-subscrito.",
"public.unsubscribeTitle": "Des-subscribirse de una lista de correo",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Acción",
"settings.bounces.blocklist": "Lista de bloqueo",
"settings.bounces.count": "Conteo de rebotes",
diff --git a/i18n/fr.json b/i18n/fr.json
index e99a583..9d370db 100644
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Vous vous êtes désabonné·e avec succès.",
"public.unsubbedTitle": "Désabonné·e",
"public.unsubscribeTitle": "Se désabonner de la liste de diffusion",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Liste de bloquage",
"settings.bounces.count": "Comptage des rebonds",
diff --git a/i18n/hu.json b/i18n/hu.json
index 9e76551..fbf258c 100644
--- a/i18n/hu.json
+++ b/i18n/hu.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Sikeresen leiratkozott.",
"public.unsubbedTitle": "Leiratkozott",
"public.unsubscribeTitle": "Leiratkozás a levelezőlistáról",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Tiltólista",
"settings.bounces.count": "Visszapattanások száma",
diff --git a/i18n/it.json b/i18n/it.json
index d55d4f3..4c96d37 100644
--- a/i18n/it.json
+++ b/i18n/it.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "La cancellazione è avvenuta con successo.",
"public.unsubbedTitle": "Iscrizione annullata",
"public.unsubscribeTitle": "Cancella l'iscrizione dalla newsletter",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Blocklist",
"settings.bounces.count": "Bounce count",
diff --git a/i18n/ml.json b/i18n/ml.json
index f72e863..d468ced 100644
--- a/i18n/ml.json
+++ b/i18n/ml.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "നിങ്ങൾ വരിക്കാരനല്ലാതായി",
"public.unsubbedTitle": "വരിക്കാരനല്ലാതാകുക",
"public.unsubscribeTitle": "മെയിലിങ് ലിസ്റ്റിന്റെ വരിക്കാരനല്ലാതാകുക",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Blocklist",
"settings.bounces.count": "Bounce count",
diff --git a/i18n/nl.json b/i18n/nl.json
index 1b20a9a..961ca9d 100644
--- a/i18n/nl.json
+++ b/i18n/nl.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Je bent met succes uitgeschreven.",
"public.unsubbedTitle": "Uitgeschreven",
"public.unsubscribeTitle": "Uitschrijven van mailinglijst",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Actie",
"settings.bounces.blocklist": "Geblokkeerd",
"settings.bounces.count": "Aantal bounces",
diff --git a/i18n/pl.json b/i18n/pl.json
index 401cd7e..3305a4d 100644
--- a/i18n/pl.json
+++ b/i18n/pl.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Pomyślnie odsubskrybowano",
"public.unsubbedTitle": "Odsubskrybowano",
"public.unsubscribeTitle": "Wypisz się z listy mailingowej",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Akcja",
"settings.bounces.blocklist": "Lista zablokowanych",
"settings.bounces.count": "Liczba odbić",
diff --git a/i18n/pt-BR.json b/i18n/pt-BR.json
index e75bfc5..0351313 100644
--- a/i18n/pt-BR.json
+++ b/i18n/pt-BR.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Você cancelou a inscrição com sucesso.",
"public.unsubbedTitle": "Inscrição cancelada",
"public.unsubscribeTitle": "Cancelar inscrição na lista de e-mails",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Blocklist",
"settings.bounces.count": "Bounce count",
diff --git a/i18n/pt.json b/i18n/pt.json
index e11cb33..3137edb 100644
--- a/i18n/pt.json
+++ b/i18n/pt.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "A sua subscrição foi cancelada com sucesso.",
"public.unsubbedTitle": "Subscrição cancelada",
"public.unsubscribeTitle": "Cancelar subscrição da lista de emails",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Blocklist",
"settings.bounces.count": "Bounce count",
diff --git a/i18n/ro.json b/i18n/ro.json
index a629fc9..e3e319d 100644
--- a/i18n/ro.json
+++ b/i18n/ro.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Te-ai dezabonat cu succes.",
"public.unsubbedTitle": "Dezabonat",
"public.unsubscribeTitle": "Dezabonează-te de la lista de discuții",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Acțiune",
"settings.bounces.blocklist": "Lista de blocare",
"settings.bounces.count": "Numarul de respingeri",
diff --git a/i18n/ru.json b/i18n/ru.json
index 1c40fc3..9f6d473 100644
--- a/i18n/ru.json
+++ b/i18n/ru.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Вы были отписаны.",
"public.unsubbedTitle": "Отписано",
"public.unsubscribeTitle": "Отписаться от списков рассылки",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Blocklist",
"settings.bounces.count": "Bounce count",
diff --git a/i18n/tr.json b/i18n/tr.json
index e9adbf0..9770280 100644
--- a/i18n/tr.json
+++ b/i18n/tr.json
@@ -295,6 +295,13 @@
"public.unsubbedInfo": "Başarı ile üyeliğinizi bitirdiniz.",
"public.unsubbedTitle": "Üyelik bitirildi.",
"public.unsubscribeTitle": "e-posta listesi üyeliğini bitir",
+ "settings.appearance.adminHelp": "Custom CSS to apply to the admin UI.",
+ "settings.appearance.adminName": "Admin",
+ "settings.appearance.customCSS": "Custom CSS",
+ "settings.appearance.customJS": "Custom JavaScript",
+ "settings.appearance.name": "Appearance",
+ "settings.appearance.publicHelp": "Custom CSS and JavaScript to apply to the public pages.",
+ "settings.appearance.publicName": "Public",
"settings.bounces.action": "Action",
"settings.bounces.blocklist": "Blocklist",
"settings.bounces.count": "Bounce count",
diff --git a/internal/migrations/v2.1.0.go b/internal/migrations/v2.1.0.go
new file mode 100644
index 0000000..a15f03b
--- /dev/null
+++ b/internal/migrations/v2.1.0.go
@@ -0,0 +1,23 @@
+package migrations
+
+import (
+ "github.com/jmoiron/sqlx"
+ "github.com/knadh/koanf"
+ "github.com/knadh/stuffbin"
+)
+
+// V2_1_0 performs the DB migrations for v.2.1.0.
+func V2_1_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
+ if _, err := db.Exec(`
+ INSERT INTO settings (key, value) VALUES
+ ('appearance.admin.custom_css', '""'),
+ ('appearance.admin.custom_js', '""'),
+ ('appearance.public.custom_css', '""'),
+ ('appearance.public.custom_js', '""')
+ ON CONFLICT DO NOTHING;
+ `); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/schema.sql b/schema.sql
index 0029e06..b64a1db 100644
--- a/schema.sql
+++ b/schema.sql
@@ -218,7 +218,11 @@ INSERT INTO settings (key, value) VALUES
('bounce.sendgrid_enabled', 'false'),
('bounce.sendgrid_key', '""'),
('bounce.mailboxes',
- '[{"enabled":false, "type": "pop", "host":"pop.yoursite.com","port":995,"auth_protocol":"userpass","username":"username","password":"password","return_path": "bounce@listmonk.yoursite.com","scan_interval":"15m","tls_enabled":true,"tls_skip_verify":false}]');
+ '[{"enabled":false, "type": "pop", "host":"pop.yoursite.com","port":995,"auth_protocol":"userpass","username":"username","password":"password","return_path": "bounce@listmonk.yoursite.com","scan_interval":"15m","tls_enabled":true,"tls_skip_verify":false}]'),
+ ('appearance.admin.custom_css', '""'),
+ ('appearance.admin.custom_js', '""'),
+ ('appearance.public.custom_css', '""'),
+ ('appearance.public.custom_js', '""');
-- bounces
DROP TABLE IF EXISTS bounces CASCADE;
diff --git a/static/public/templates/index.html b/static/public/templates/index.html
index 507476c..d310d92 100644
--- a/static/public/templates/index.html
+++ b/static/public/templates/index.html
@@ -7,6 +7,8 @@
+
+
{{ if ne .FaviconURL "" }}