diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fac51f..0ed61a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ Notable changes to Mailpit will be documented in this file. +## 0.1.5 + +### Feature +- Improved message search - any order & phrase quoting + +### UI +- Change breakpoints for mobile view of messages +- Resize iframes with viewport resize + + ## 0.1.4 ### Feature diff --git a/go.mod b/go.mod index 2cf68d8..16d80d1 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/jhillyerd/enmime v0.10.0 github.com/k3a/html2text v1.0.8 github.com/klauspost/compress v1.15.9 + github.com/mattn/go-shellwords v1.0.12 github.com/mhale/smtpd v0.8.0 github.com/ostafen/clover/v2 v2.0.0-alpha.2 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index ad6e1e6..ca52bba 100644 --- a/go.sum +++ b/go.sum @@ -124,6 +124,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mhale/smtpd v0.8.0 h1:5JvdsehCg33PQrZBvFyDMMUDQmvbzVpZgKob7eYBJc0= github.com/mhale/smtpd v0.8.0/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= diff --git a/server/ui-src/App.vue b/server/ui-src/App.vue index 8d6467e..ac135e2 100644 --- a/server/ui-src/App.vue +++ b/server/ui-src/App.vue @@ -443,33 +443,33 @@ export default {
-
-
+
+
{{ getRelativeCreated(message) }}
-
+
{{ message.From.Name ? message.From.Name : message.From.Address }}
-
+
{{ message.From.Name ? message.From.Name : message.From.Address }}
-
+
{{ getPrimaryEmailTo(message) }} [+{{message.To.length - 1}}]
-
+
{{ message.Subject != "" ? message.Subject : "[ no subject ]" }}
-
+
{{ getFileSize(message.Size) }}
-
+
{{ getRelativeCreated(message) }}
diff --git a/server/ui-src/assets/styles.scss b/server/ui-src/assets/styles.scss index 7e77772..75c0196 100644 --- a/server/ui-src/assets/styles.scss +++ b/server/ui-src/assets/styles.scss @@ -44,6 +44,7 @@ font-family: Courier New, Courier, System, fixed-width; font-size: 0.85em; } + #nav-plain-text { white-space: pre-wrap; } @@ -52,7 +53,7 @@ margin: 15px 0 0; th { - padding-right: 10px; + padding-right: 1.5rem; font-weight: normal; vertical-align: top; } @@ -62,6 +63,14 @@ } } +#nav-html { + padding-right: 1.5rem; +} + +#preview-html { + min-height: 300px; +} + .list-group-item:first-child { border-top: 0; } diff --git a/server/ui-src/templates/Message.vue b/server/ui-src/templates/Message.vue index 1370014..3682df7 100644 --- a/server/ui-src/templates/Message.vue +++ b/server/ui-src/templates/Message.vue @@ -15,6 +15,7 @@ export default { iframes: [], // for resizing } }, + mounted() { var self = this; @@ -46,11 +47,29 @@ export default { self.srcURI = 'api/' + self.mailbox + '/' + self.message.ID + '/source'; }); }, + + unmounted: function() { + window.removeEventListener("resize", this.resizeIframes); + }, + methods: { resizeIframe: function(el) { let i = el.target; i.style.height = i.contentWindow.document.body.scrollHeight + 50 + 'px'; }, + + resizeIframes: function() { + let h = document.getElementById('preview-html'); + if (h) { + h.style.height = h.contentWindow.document.body.scrollHeight + 50 + 'px'; + } + + let s = document.getElementById('message-src'); + if (s) { + s.style.height = s.contentWindow.document.body.scrollHeight + 50 + 'px'; + } + }, + allAttachments: function(message){ let a = []; for (let i in message.Attachments) { diff --git a/storage/database.go b/storage/database.go index 063e4ab..a489572 100644 --- a/storage/database.go +++ b/storage/database.go @@ -18,6 +18,7 @@ import ( "github.com/axllent/mailpit/server/websockets" "github.com/jhillyerd/enmime" "github.com/klauspost/compress/zstd" + "github.com/mattn/go-shellwords" "github.com/ostafen/clover/v2" ) @@ -327,21 +328,58 @@ func List(mailbox string, start, limit int) ([]data.Summary, error) { } // Search returns a summary of items mathing a search. It searched the SearchText field. -func Search(mailbox, search string, start, limit int) ([]data.Summary, error) { +func Search(mailbox, s string, start, limit int) ([]data.Summary, error) { mailbox = sanitizeMailboxName(mailbox) - sq := fmt.Sprintf("(?i)%s", cleanString(regexp.QuoteMeta(search))) + s = strings.ToLower(s) + s = strings.Replace(s, "'", `\'`, -1) + s = strings.Replace(s, "(", ``, -1) + s = strings.Replace(s, ")", ``, -1) + // add another quote if quotes are odd + quotes := strings.Count(s, `"`) + if quotes%2 != 0 { + s += `"` + } + + p := shellwords.NewParser() + args, err := p.Parse(s) + if err != nil { + return nil, errors.New("Your search contains invalid characters") + } + + results := []data.Summary{} + include := []string{} + + for _, w := range args { + word := cleanString(w) + if word != "" { + include = append(include, fmt.Sprintf("%s", regexp.QuoteMeta(word))) + } + } + + if len(include) == 0 { + return results, nil + } + + var where clover.Criteria + + for i, w := range include { + if i == 0 { + where = clover.Field("SearchText").Like(w) + } else { + where = where.And(clover.Field("SearchText").Like(w)) + } + } + q, err := db.FindAll(clover.NewQuery(mailbox). Skip(start). Limit(limit). Sort(clover.SortOption{Field: "Created", Direction: -1}). - Where(clover.Field("SearchText").Like(sq))) + Where(where)) if err != nil { return nil, err } - results := []data.Summary{} - for _, d := range q { cs := &data.Summary{} if err := d.Unmarshal(cs); err != nil { diff --git a/storage/database_test.go b/storage/database_test.go index e38b45a..9d428f6 100644 --- a/storage/database_test.go +++ b/storage/database_test.go @@ -259,9 +259,9 @@ RepeatTest: case 2: search = fmt.Sprintf("to-%d@example.com", i) case 3: - search = fmt.Sprintf("Subject line %d end", i) + search = fmt.Sprintf("\"Subject line %d end\"", i) default: - search = fmt.Sprintf("the email body %d jdsauk dwqmdqw", i) + search = fmt.Sprintf("\"the email body %d jdsauk dwqmdqw\"", i) } summaries, err := Search(DefaultMailbox, search, 0, 10) diff --git a/updater/updater.go b/updater/updater.go index 31b52aa..7c94031 100644 --- a/updater/updater.go +++ b/updater/updater.go @@ -178,7 +178,7 @@ func GithubUpdate(repo, appName, currentVersion string) (string, error) { // get the running binary oldExec, err := os.Executable() if err != nil { - panic(err) + return "", err } if err = replaceFile(oldExec, newExec); err != nil {