Browse Source

Implement attachments support

- Add attachments for web ui
- Add attachments download
Alexey Edelev 4 years ago
parent
commit
eff40708ab
6 changed files with 75 additions and 21 deletions
  1. 18 0
      web/css/index.css
  2. 1 0
      web/css/styles.css
  3. 23 0
      web/js/index.js
  4. 9 7
      web/mail.go
  5. 18 14
      web/server.go
  6. 6 0
      web/templates/details.html

+ 18 - 0
web/css/index.css

@@ -216,4 +216,22 @@ html, body {
 .contentArea {
     border-radius: var(--default-radius);
     border: 1px solid var(--primary-color);
+}
+
+.attachment {
+    background-color: var(--bg-color);
+    border-radius: 20px;
+    border: 1px solid var(--primary-dark-color);
+    color: var(--primary-text-color);
+    font-weight: bold;
+    margin-right: 10px;
+    padding: var(--tiny-text-padding) var(--base-text-padding) var(--tiny-text-padding) var(--base-text-padding);
+    transition: background-color .2s, color .2s;
+}
+
+
+.attachment:hover, .attachment:focus {
+    background-color: var(--primary-dark-color);
+    color: var(--secondary-text-color);
+    cursor: pointer;
 }

+ 1 - 0
web/css/styles.css

@@ -23,6 +23,7 @@
     --base-text-padding: 10px;
     --base-border-padding: 8px;
     --small-text-padding: 6px;
+    --tiny-text-padding: 3px;
 
     --small-text-size: 14px;
     --normal-text-size: 16px;

+ 23 - 0
web/js/index.js

@@ -374,6 +374,29 @@ function restoreMail(mailId, callback) {
     })
 }
 
+function downloadAttachment(attachmentId, filename) {
+    $.ajax({
+        url: '/attachment/' + attachmentId,
+        xhrFields: {
+            responseType: 'blob'
+        },
+        success: function (data) {
+            // Ah-ha-ha-ha html and web is piece of shit full of hacks...
+            var a = document.createElement('a');
+            var url = window.URL.createObjectURL(data);
+            a.href = url;
+            a.download = filename;
+            document.body.append(a);
+            a.click();
+            a.remove();
+            window.URL.revokeObjectURL(url);
+        },
+        error: function(jqXHR, textStatus, errorThrown) {
+            showToast(Severity.Critical, 'Unable to download attachment: ' + errorThrown + ' ' + textStatus)
+        }
+    });
+}
+
 function setDetailsVisible(visible) {
     if (visible) {
         $('#mailDetails').show()

+ 9 - 7
web/mail.go

@@ -80,13 +80,14 @@ func (s *Server) handleMailDetails(w http.ResponseWriter, user, mailId string) {
 
 	s.storage.SetRead(user, mailId, true)
 	fmt.Fprint(w, s.templater.ExecuteDetails(&struct {
-		From    string
-		To      string
-		Subject string
-		Text    template.HTML
-		MailId  string
-		Read    bool
-		Trash   bool
+		From        string
+		To          string
+		Subject     string
+		Text        template.HTML
+		MailId      string
+		Read        bool
+		Trash       bool
+		Attachments []*common.AttachmentHeader
 	}{
 		From:    mail.Mail.Header.From,
 		To:      mail.Mail.Header.To,
@@ -96,6 +97,7 @@ func (s *Server) handleMailDetails(w http.ResponseWriter, user, mailId string) {
 		Read:    false,
 		Trash: mail.Trash ||
 			mail.Folder == common.Trash, //TODO: Legacy for old databases remove soon
+		Attachments: mail.Mail.Body.Attachments,
 	}))
 }
 

+ 18 - 14
web/server.go

@@ -60,13 +60,14 @@ const (
 )
 
 type Server struct {
-	authenticator *auth.Authenticator
-	fileServer    http.Handler
-	templater     *Templater
-	sessionStore  *sessions.CookieStore
-	storage       *db.Storage
-	Notifier      *webNotifier
-	scanner       common.Scanner
+	authenticator     *auth.Authenticator
+	fileServer        http.Handler
+	attachmentsServer http.Handler
+	templater         *Templater
+	sessionStore      *sessions.CookieStore
+	storage           *db.Storage
+	Notifier          *webNotifier
+	scanner           common.Scanner
 }
 
 func NewServer(scanner common.Scanner) *Server {
@@ -84,13 +85,14 @@ func NewServer(scanner common.Scanner) *Server {
 		return nil
 	}
 	s := &Server{
-		authenticator: authenticator,
-		templater:     NewTemplater("data/templates"),
-		fileServer:    http.FileServer(http.Dir("data")),
-		sessionStore:  sessions.NewCookieStore(make([]byte, 32)),
-		storage:       storage,
-		Notifier:      NewWebNotifier(),
-		scanner:       scanner,
+		authenticator:     authenticator,
+		templater:         NewTemplater("data/templates"),
+		fileServer:        http.FileServer(http.Dir("data")),
+		attachmentsServer: http.StripPrefix("/attachment/", http.FileServer(http.Dir(config.ConfigInstance().AttachmentsPath))),
+		sessionStore:      sessions.NewCookieStore(make([]byte, 32)),
+		storage:           storage,
+		Notifier:          NewWebNotifier(),
+		scanner:           scanner,
 	}
 
 	return s
@@ -107,6 +109,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		utils.StartsWith(r.URL.Path, "/assets/") ||
 		utils.StartsWith(r.URL.Path, "/js/") {
 		s.fileServer.ServeHTTP(w, r)
+	} else if utils.StartsWith(r.URL.Path, "/attachment") {
+		s.attachmentsServer.ServeHTTP(w, r)
 	} else if cap := utils.RegExpUtilsInstance().MailboxFinder.FindStringSubmatch(r.URL.Path); len(cap) == 3 {
 		user, token := s.extractAuth(w, r)
 		if !s.authenticator.Verify(user, token) {

+ 6 - 0
web/templates/details.html

@@ -11,6 +11,12 @@
             <img id="deleteIcon" class="iconBtn" style="width: 20px; margin-right: 10px;" onclick="removeMail({{.MailId}}, closeDetails);" src="/assets/remove.svg"/>
             <img class="iconBtn" style="width: 20px; height: 20px; margin-left:10px;" onclick="closeDetails();" src="/assets/back.svg"/>
         </div>
+        <div class="noselect" style="width: 100%; display: flex; flex-direction: row;">
+            <span class="primaryText" style="margin-top: auto; margin-bottom: auto; margin-right: 5px;">Attachments: </span>
+            {{range .Attachments}}
+                <div class="attachment" onclick="downloadAttachment({{.Id}}, {{.FileName}})">{{.FileName}}</div>
+            {{end}}
+        </div>
     </div>
     <div id="mailBody" class="contentArea horizontalPaddingBox">
         <div style="position: relative; max-width: 100%; max-height: 100%; width: 100%; height: 100%;">