~migadu/alps-devel

Implement reply-all functionality for the alps theme v1 APPLIED

Silvan Jegen: 1
 Implement reply-all functionality for the alps theme

 3 files changed, 80 insertions(+), 49 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~migadu/alps-devel/patches/49177/mbox | git am -3
Learn more about email & git

[PATCH] Implement reply-all functionality for the alps theme Export this patch

We slightly refactor the code to make it easier to read, while we are
at it.

This should address https://todo.sr.ht/~migadu/alps/155.
---
 plugins/base/routes.go   | 127 ++++++++++++++++++++++++---------------
 themes/alps/compose.html |   1 +
 themes/alps/message.html |   1 +
 3 files changed, 80 insertions(+), 49 deletions(-)

diff --git a/plugins/base/routes.go b/plugins/base/routes.go
index ba3de30..8aaed73 100644
--- a/plugins/base/routes.go
+++ b/plugins/base/routes.go
@@ -811,65 +811,94 @@ func handleReply(ctx *alps.Context) error {

	var msg OutgoingMessage
	if ctx.Request().Method == http.MethodGet {
		// Populate fields from original message
		partPath, err := parsePartPath(ctx.QueryParam("part"))
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, err)
		}

		var inReplyTo *IMAPMessage
		var part *message.Entity
		err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
			var err error
			inReplyTo, part, err = getMessagePart(c, inReplyToPath.Mailbox, inReplyToPath.Uid, partPath)
			return err
		})
		msg, err = populateMessageFromOriginalMessage(ctx, inReplyToPath)
		if err != nil {
			return err
		}
	}

		mimeType, _, err := part.Header.ContentType()
	return handleCompose(ctx, &msg, &composeOptions{InReplyTo: &inReplyToPath})
}

func populateMessageFromOriginalMessage(ctx *alps.Context, inReplyToPath messagePath) (OutgoingMessage, error) {
	var ret OutgoingMessage

	partPath, err := parsePartPath(ctx.QueryParam("part"))
	if err != nil {
		return ret, echo.NewHTTPError(http.StatusBadRequest, err)
	}

	var inReplyTo *IMAPMessage
	var part *message.Entity
	err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
		var err error
		inReplyTo, part, err = getMessagePart(c, inReplyToPath.Mailbox,
			inReplyToPath.Uid, partPath)
		return err
	})
	if err != nil {
		return ret, err
	}

	mimeType, _, err := part.Header.ContentType()
	if err != nil {
		return ret, fmt.Errorf("failed to parse part Content-Type: %v", err)
	}

	var text string
	switch mimeType {
	case "text/plain":
		ret.Text, err = quote(part.Body)
	case "text/html":
		text, err = html2text.FromReader(part.Body, html2text.Options{})
		if err != nil {
			return fmt.Errorf("failed to parse part Content-Type: %v", err)
			return ret, err
		}

		if mimeType == "text/plain" {
			msg.Text, err = quote(part.Body)
			if err != nil {
				return err
			}
		} else if mimeType == "text/html" {
			text, err := html2text.FromReader(part.Body, html2text.Options{})
			if err != nil {
				return err
			}
			msg.Text, err = quote(strings.NewReader(text))
			if err != nil {
				return nil
			}
		} else {
			err := fmt.Errorf("cannot forward %q part", mimeType)
			return echo.NewHTTPError(http.StatusBadRequest, err)
		}
		ret.Text, err = quote(strings.NewReader(text))

		var hdr mail.Header
		hdr.GenerateMessageID()
		mid, _ := hdr.MessageID()
		msg.MessageID = "<" + mid + ">"
		msg.InReplyTo = inReplyTo.Envelope.MessageID
		// TODO: populate From from known user addresses and inReplyTo.Envelope.To
		replyTo := inReplyTo.Envelope.ReplyTo
		if len(replyTo) == 0 {
			replyTo = inReplyTo.Envelope.From
		}
		msg.To = unwrapIMAPAddressList(replyTo)
		msg.Subject = inReplyTo.Envelope.Subject
		if !strings.HasPrefix(strings.ToLower(msg.Subject), "re:") {
			msg.Subject = "Re: " + msg.Subject
		}
	default:
		err := fmt.Errorf("cannot forward %q part", mimeType)
		err = echo.NewHTTPError(http.StatusBadRequest, err)
	}
	if err != nil {
		return ret, err
	}

	return handleCompose(ctx, &msg, &composeOptions{InReplyTo: &inReplyToPath})
	var hdr mail.Header
	hdr.GenerateMessageID()
	mid, _ := hdr.MessageID()
	ret.MessageID = "<" + mid + ">"
	ret.InReplyTo = inReplyTo.Envelope.MessageID
	// TODO: populate From from known user addresses and inReplyTo.Envelope.To
	replyTo := inReplyTo.Envelope.ReplyTo
	if len(replyTo) == 0 {
		replyTo = inReplyTo.Envelope.From
	}
	ret.To = unwrapIMAPAddressList(replyTo)

	if ctx.QueryParam("all") != "" {
		filtered := filterOutUsername(ctx.Session.Username(),
			inReplyTo.Envelope.To)
		ret.To = unwrapIMAPAddressList(append(replyTo, filtered...))
		ret.Cc = unwrapIMAPAddressList(inReplyTo.Envelope.Cc)
	}

	ret.Subject = inReplyTo.Envelope.Subject
	if !strings.HasPrefix(strings.ToLower(ret.Subject), "re:") {
		ret.Subject = "Re: " + ret.Subject
	}

	return ret, nil
}

func filterOutUsername(username string, addresses []imap.Address) []imap.Address {
	for i, addr := range addresses {
		if addr.Addr() == username {
			return append(addresses[:i], addresses[i+1:]...)
		}
	}
	return addresses
}

func handleForward(ctx *alps.Context) error {
diff --git a/themes/alps/compose.html b/themes/alps/compose.html
index 7d7c822..e424960 100644
--- a/themes/alps/compose.html
+++ b/themes/alps/compose.html
@@ -34,6 +34,7 @@
            type="text"
            name="cc"
            id="cc"
            value="{{ join .Message.Cc ", " }}"
          />

          <label>Bcc</label>
diff --git a/themes/alps/message.html b/themes/alps/message.html
index 8f67e8d..daa69ef 100644
--- a/themes/alps/message.html
+++ b/themes/alps/message.html
@@ -83,6 +83,7 @@
                <a class="action-group button-link" href="{{.Message.URL}}/edit{{if .Message.TextPart}}?part={{.Message.TextPart.PathString}}{{end}}">Edit draft</a>
              {{else}}
                <a class="action-group button-link" href="{{.Message.URL}}/reply{{if .Message.TextPart}}?part={{.Message.TextPart.PathString}}{{end}}">Reply</a>
                <a class="action-group button-link" href="{{.Message.URL}}/reply{{if .Message.TextPart}}?part={{.Message.TextPart.PathString}}&all=true{{end}}">Reply all</a>
                <a class="action-group button-link" href="{{.Message.URL}}/forward{{if .Message.TextPart}}?part={{.Message.TextPart.PathString}}{{end}}">Forward</a>
              {{end}}
            </span>
-- 
2.43.0
LGTM and pushed, thanks!

(Please feel free to ping me if I forget about a patch!)