Add support for custom CSS/JS in settings for admin and public pages.
This feature was originally authored by @sweetppro in PR #438. However, since the PR ended up in an unclean state with multiple master merges (instead of rebase) from the upstream, there are several commits that are out of order and can can no longer be be squashed for a clean feature merge. This commit aggregates the changes from the original PR and applies the following fixes on top of it. - Add custom admin JS box to appearance UI. - Refactor i18n language strings. - Add handlers and migrations for the new `appearance.admin.custom_js` field. - Fix migration version to `v2.1.0` - Load custom appearance CSS/JS bytes into global constants during boot instead of making a DB call on every request. - Fix and canonicalize URIs from `/api/custom*` to `/public/*.css` and `/admin/*.css`. Add proxy paths to yarn proxy config. - Remove redundant HTTP handlers for different custom appearance files and refactor into a single handler `serveCustomApperance()` - Fix content-type and UTF8 encoding headers for different file types. - Fix incorrect registration of public facing custom CSS/JS handlers in the authenticated admin URI group. - Fix merge conflicts in `Settings.vue`. - Minor HTML and style fixes. - Remove the `AppearanceEditor` component and use the existing `HTMLEditor` component instead. - Add `language` prop to the `HTMLEditor` component. Co-authored-by: SweetPPro <sweetppro@users.noreply.github.com>
This commit is contained in:
parent
920645f90e
commit
fabe06e339
28 changed files with 280 additions and 5 deletions
|
@ -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)
|
||||
|
|
14
cmd/init.go
14
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))
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>static/favicon.png" />
|
||||
<link href="<%= BASE_URL %>custom.css" rel="stylesheet" type="text/css">
|
||||
<script src="<%= BASE_URL %>custom.js" async defer></script>
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -802,6 +802,10 @@ section.analytics {
|
|||
.box {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.html-editor {
|
||||
height: auto;
|
||||
min-height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Logs */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -49,6 +49,10 @@
|
|||
<b-tab-item :label="$t('settings.messengers.name')">
|
||||
<messenger-settings :form="form" :key="key" />
|
||||
</b-tab-item><!-- messengers -->
|
||||
|
||||
<b-tab-item :label="$t('settings.appearance.name')">
|
||||
<appearance-settings :form="form" :key="key" />
|
||||
</b-tab-item><!-- appearance -->
|
||||
</b-tabs>
|
||||
|
||||
</section>
|
||||
|
@ -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() {
|
||||
|
|
66
frontend/src/views/settings/appearance.vue
Normal file
66
frontend/src/views/settings/appearance.vue
Normal file
|
@ -0,0 +1,66 @@
|
|||
<template>
|
||||
<div class="items">
|
||||
<b-tabs :animated="false">
|
||||
<b-tab-item :label="$t('settings.appearance.adminName')" label-position="on-border">
|
||||
<div class="block">
|
||||
{{ $t('settings.appearance.adminHelp') }}
|
||||
</div>
|
||||
|
||||
<b-field :label="$t('settings.appearance.customCSS')" label-position="on-border">
|
||||
<html-editor v-model="data['appearance.admin.custom_css']" name="body"
|
||||
language="css" />
|
||||
</b-field>
|
||||
|
||||
<b-field :label="$t('settings.appearance.customJS')" label-position="on-border">
|
||||
<html-editor v-model="data['appearance.admin.custom_js']" name="body"
|
||||
language="css" />
|
||||
</b-field>
|
||||
</b-tab-item><!-- admin -->
|
||||
|
||||
<b-tab-item :label="$t('settings.appearance.publicName')" label-position="on-border">
|
||||
<div class="block">
|
||||
{{ $t('settings.appearance.publicHelp') }}
|
||||
</div>
|
||||
|
||||
<b-field :label="$t('settings.appearance.customCSS')" label-position="on-border">
|
||||
<html-editor v-model="data['appearance.public.custom_css']" name="body"
|
||||
language="css" />
|
||||
</b-field>
|
||||
|
||||
<b-field :label="$t('settings.appearance.customJS')" label-position="on-border">
|
||||
<html-editor v-model="data['appearance.public.custom_js']" name="body"
|
||||
language="js" />
|
||||
</b-field>
|
||||
</b-tab-item><!-- public -->
|
||||
</b-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import { mapState } from 'vuex';
|
||||
import HTMLEditor from '../../components/HTMLEditor.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
'html-editor': HTMLEditor,
|
||||
},
|
||||
|
||||
props: {
|
||||
form: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
data: this.form,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['settings']),
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
5
frontend/vue.config.js
vendored
5
frontend/vue.config.js
vendored
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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ć",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
23
internal/migrations/v2.1.0.go
Normal file
23
internal/migrations/v2.1.0.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
<meta name="description" content="{{ .Data.Description }}" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
|
||||
<link href="/public/static/style.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/public/custom.css" rel="stylesheet" type="text/css">
|
||||
<script src="/public/custom.js" async defer></script>
|
||||
|
||||
{{ if ne .FaviconURL "" }}
|
||||
<link rel="shortcut icon" href="{{ .FaviconURL }}" type="image/x-icon" />
|
||||
|
|
Loading…
Reference in a new issue