diff --git a/init.go b/init.go
index 699f30c..a7f69ed 100644
--- a/init.go
+++ b/init.go
@@ -186,7 +186,7 @@ func initImporter(app *App) *subimporter.Importer {
}
// initMessengers initializes various messaging backends.
-func initMessengers(r *manager.Manager) messenger.Messenger {
+func initMessengers(m *manager.Manager) messenger.Messenger {
// Load SMTP configurations for the default e-mail Messenger.
var (
mapKeys = ko.MapKeys("smtp")
@@ -215,7 +215,7 @@ func initMessengers(r *manager.Manager) messenger.Messenger {
if err != nil {
lo.Fatalf("error loading e-mail messenger: %v", err)
}
- if err := r.AddMessenger(msgr); err != nil {
+ if err := m.AddMessenger(msgr); err != nil {
lo.Printf("error registering messenger %s", err)
}
diff --git a/internal/manager/manager.go b/internal/manager/manager.go
index 4c816fc..f7b14db 100644
--- a/internal/manager/manager.go
+++ b/internal/manager/manager.go
@@ -122,7 +122,6 @@ func (m *Manager) AddMessenger(msg messenger.Messenger) error {
return fmt.Errorf("messenger '%s' is already loaded", id)
}
m.messengers[id] = msg
-
return nil
}
@@ -132,7 +131,6 @@ func (m *Manager) GetMessengerNames() []string {
for n := range m.messengers {
names = append(names, n)
}
-
return names
}
@@ -196,9 +194,7 @@ func (m *Manager) Run(tick time.Duration) {
delete(m.msgErrorCounts, e.camp.ID)
// Notify admins.
- m.sendNotif(e.camp,
- models.CampaignStatusPaused,
- "Too many errors")
+ m.sendNotif(e.camp, models.CampaignStatusPaused, "Too many errors")
}
}
}
@@ -256,6 +252,34 @@ func (m *Manager) SpawnWorkers() {
}
}
+// TemplateFuncs returns the template functions to be applied into
+// compiled campaign templates.
+func (m *Manager) TemplateFuncs(c *models.Campaign) template.FuncMap {
+ return template.FuncMap{
+ "TrackLink": func(url string, msg *Message) string {
+ return m.trackLink(url, msg.Campaign.UUID, msg.Subscriber.UUID)
+ },
+ "TrackView": func(msg *Message) template.HTML {
+ return template.HTML(fmt.Sprintf(``,
+ fmt.Sprintf(m.cfg.ViewTrackURL, msg.Campaign.UUID, msg.Subscriber.UUID)))
+ },
+ "UnsubscribeURL": func(msg *Message) string {
+ return msg.unsubURL
+ },
+ "OptinURL": func(msg *Message) string {
+ // Add list IDs.
+ // TODO: Show private lists list on optin e-mail
+ return fmt.Sprintf(m.cfg.OptinURL, msg.Subscriber.UUID, "")
+ },
+ "Date": func(layout string) string {
+ if layout == "" {
+ layout = time.ANSIC
+ }
+ return time.Now().Format(layout)
+ },
+ }
+}
+
// addCampaign adds a campaign to the process queue.
func (m *Manager) addCampaign(c *models.Campaign) error {
// Validate messenger.
@@ -281,7 +305,6 @@ func (m *Manager) getPendingCampaignIDs() []int64 {
for _, c := range m.camps {
ids = append(ids, int64(c.ID))
}
-
return ids
}
@@ -358,17 +381,6 @@ func (m *Manager) exhaustCampaign(c *models.Campaign, status string) (*models.Ca
return cm, nil
}
-// Render takes a Message, executes its pre-compiled Campaign.Tpl
-// and applies the resultant bytes to Message.body to be used in messages.
-func (m *Message) Render() error {
- out := bytes.Buffer{}
- if err := m.Campaign.Tpl.ExecuteTemplate(&out, models.BaseTpl, m); err != nil {
- return err
- }
- m.Body = out.Bytes()
- return nil
-}
-
// trackLink register a URL and return its UUID to be used in message templates
// for tracking links.
func (m *Manager) trackLink(url, campUUID, subUUID string) string {
@@ -412,30 +424,13 @@ func (m *Manager) sendNotif(c *models.Campaign, status, reason string) error {
return m.notifCB(subject, data)
}
-// TemplateFuncs returns the template functions to be applied into
-// compiled campaign templates.
-func (m *Manager) TemplateFuncs(c *models.Campaign) template.FuncMap {
- return template.FuncMap{
- "TrackLink": func(url string, msg *Message) string {
- return m.trackLink(url, msg.Campaign.UUID, msg.Subscriber.UUID)
- },
- "TrackView": func(msg *Message) template.HTML {
- return template.HTML(fmt.Sprintf(``,
- fmt.Sprintf(m.cfg.ViewTrackURL, msg.Campaign.UUID, msg.Subscriber.UUID)))
- },
- "UnsubscribeURL": func(msg *Message) string {
- return msg.unsubURL
- },
- "OptinURL": func(msg *Message) string {
- // Add list IDs.
- // TODO: Show private lists list on optin e-mail
- return fmt.Sprintf(m.cfg.OptinURL, msg.Subscriber.UUID, "")
- },
- "Date": func(layout string) string {
- if layout == "" {
- layout = time.ANSIC
- }
- return time.Now().Format(layout)
- },
+// Render takes a Message, executes its pre-compiled Campaign.Tpl
+// and applies the resultant bytes to Message.body to be used in messages.
+func (m *Message) Render() error {
+ out := bytes.Buffer{}
+ if err := m.Campaign.Tpl.ExecuteTemplate(&out, models.BaseTpl, m); err != nil {
+ return err
}
+ m.Body = out.Bytes()
+ return nil
}
diff --git a/internal/subimporter/importer.go b/internal/subimporter/importer.go
index 0f60238..b329eed 100644
--- a/internal/subimporter/importer.go
+++ b/internal/subimporter/importer.go
@@ -21,11 +21,10 @@ import (
"strings"
"sync"
- "github.com/gofrs/uuid"
- "github.com/lib/pq"
-
"github.com/asaskevich/govalidator"
+ "github.com/gofrs/uuid"
"github.com/knadh/listmonk/models"
+ "github.com/lib/pq"
)
const (
@@ -34,7 +33,10 @@ const (
// commitBatchSize is the number of inserts to commit in a single SQL transaction.
commitBatchSize = 10000
+)
+// Various import statuses.
+const (
StatusNone = "none"
StatusImporting = "importing"
StatusStopping = "stopping"
@@ -113,7 +115,6 @@ func New(upsert *sql.Stmt, blacklist *sql.Stmt, updateListDate *sql.Stmt,
notifCB: notifCB,
status: Status{Status: StatusNone, logBuf: bytes.NewBuffer(nil)},
}
-
return &im
}
@@ -162,7 +163,6 @@ func (im *Importer) GetLogs() []byte {
if im.status.logBuf == nil {
return []byte{}
}
-
return im.status.logBuf.Bytes()
}
@@ -213,7 +213,6 @@ func (im *Importer) sendNotif(status string) error {
strings.Title(status),
s.Name)
)
-
return im.notifCB(subject, out)
}
@@ -526,7 +525,6 @@ func (s *Session) LoadCSV(srcPath string, delim rune) error {
close(s.subQueue)
failed = false
-
return nil
}
@@ -558,7 +556,6 @@ func (s *Session) mapCSVHeaders(csvHdrs []string, knownHdrs map[string]bool) map
s.log.Printf("ignoring unknown header '%s'", h)
continue
}
-
hdrKeys[h] = i
}
@@ -601,5 +598,4 @@ func countLines(r io.Reader) (int, error) {
return count, err
}
}
-
}
diff --git a/media.go b/media.go
index a07ee3d..20723c9 100644
--- a/media.go
+++ b/media.go
@@ -1,10 +1,13 @@
package main
import (
+ "bytes"
"fmt"
+ "mime/multipart"
"net/http"
"strconv"
+ "github.com/disintegration/imaging"
"github.com/gofrs/uuid"
"github.com/knadh/listmonk/internal/media"
"github.com/labstack/echo"
@@ -146,3 +149,28 @@ func handleDeleteMedia(c echo.Context) error {
app.media.Delete(thumbPrefix + m.Filename)
return c.JSON(http.StatusOK, okResp{true})
}
+
+// createThumbnail reads the file object and returns a smaller image
+func createThumbnail(file *multipart.FileHeader) (*bytes.Reader, error) {
+ src, err := file.Open()
+ if err != nil {
+ return nil, err
+ }
+ defer src.Close()
+
+ img, err := imaging.Decode(src)
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError,
+ fmt.Sprintf("Error decoding image: %v", err))
+ }
+
+ // Encode the image into a byte slice as PNG.
+ var (
+ thumb = imaging.Resize(img, thumbnailSize, 0, imaging.Lanczos)
+ out bytes.Buffer
+ )
+ if err := imaging.Encode(&out, thumb, imaging.PNG); err != nil {
+ return nil, err
+ }
+ return bytes.NewReader(out.Bytes()), nil
+}
diff --git a/utils.go b/utils.go
index b752871..4bf914c 100644
--- a/utils.go
+++ b/utils.go
@@ -4,15 +4,10 @@ import (
"bytes"
"crypto/rand"
"fmt"
- "log"
- "mime/multipart"
- "net/http"
"regexp"
"strconv"
"strings"
- "github.com/disintegration/imaging"
- "github.com/labstack/echo"
"github.com/lib/pq"
)
@@ -52,28 +47,6 @@ func generateFileName(fName string) string {
return name
}
-// createThumbnail reads the file object and returns a smaller image
-func createThumbnail(file *multipart.FileHeader) (*bytes.Reader, error) {
- src, err := file.Open()
- if err != nil {
- return nil, err
- }
- defer src.Close()
- img, err := imaging.Decode(src)
- if err != nil {
- return nil, echo.NewHTTPError(http.StatusInternalServerError,
- fmt.Sprintf("Error decoding image: %v", err))
- }
- t := imaging.Resize(img, thumbnailSize, 0, imaging.Lanczos)
- // Encode the image into a byte slice as PNG.
- var buf bytes.Buffer
- err = imaging.Encode(&buf, t, imaging.PNG)
- if err != nil {
- log.Fatal(err)
- }
- return bytes.NewReader(buf.Bytes()), nil
-}
-
// Given an error, pqErrMsg will try to return pq error details
// if it's a pq error.
func pqErrMsg(err error) string {
@@ -82,7 +55,6 @@ func pqErrMsg(err error) string {
return fmt.Sprintf("%s. %s", err, err.Detail)
}
}
-
return err.Error()
}
@@ -103,7 +75,6 @@ func normalizeTags(tags []string) []string {
out = append(out, string(rep))
}
}
-
return out
}
@@ -118,7 +89,6 @@ func makeMsgTpl(pageTitle, heading, msg string) msgTpl {
err.Title = pageTitle
err.MessageTitle = heading
err.Message = msg
-
return err
}
@@ -127,7 +97,6 @@ func makeMsgTpl(pageTitle, heading, msg string) msgTpl {
// resultant values.
func parseStringIDs(s []string) ([]int64, error) {
vals := make([]int64, 0, len(s))
-
for _, v := range s {
i, err := strconv.ParseInt(v, 10, 64)
if err != nil {
@@ -147,12 +116,11 @@ func parseStringIDs(s []string) ([]int64, error) {
// generateRandomString generates a cryptographically random, alphanumeric string of length n.
func generateRandomString(n int) (string, error) {
const dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-
var bytes = make([]byte, n)
+
if _, err := rand.Read(bytes); err != nil {
return "", err
}
-
for k, v := range bytes {
bytes[k] = dictionary[v%byte(len(dictionary))]
}