From 6c42b31ae9fb3de503bd59d1150ffdf7dcdf9866 Mon Sep 17 00:00:00 2001 From: Jack Punter Date: Mon, 6 May 2024 19:35:53 +0100 Subject: [PATCH] Add Markdown parsing to the preview --- go.mod | 2 + go.sum | 2 + main.go | 97 +++++++++++++++++++++++++++++--------------- static/css/site.css | 17 ++++---- templates/index.html | 30 ++++++++------ 5 files changed, 94 insertions(+), 54 deletions(-) diff --git a/go.mod b/go.mod index 15e34b7..5e42d78 100644 --- a/go.mod +++ b/go.mod @@ -3,3 +3,5 @@ module send_email_site go 1.21.6 require github.com/pelletier/go-toml/v2 v2.2.1 + +require github.com/russross/blackfriday/v2 v2.1.0 diff --git a/go.sum b/go.sum index 02a16d6..1da22db 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtos github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/main.go b/main.go index df42c20..0e23089 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "time" "github.com/pelletier/go-toml/v2" + "github.com/russross/blackfriday/v2" ) // ------------- Api configs ------------- @@ -68,6 +69,48 @@ func ParseArguments(rawArgs string) map[string]string { return result } +/* + * This is taken directly from the https://git.handmadecities.com/meetups/meetupinvite2000 repo + * Although I have added the blackfriday processing to it rather than doing it separately. + */ +func parseMailFile(contents string) (subject string, body string, valid bool) { + /* Remove leading and trailing newlines */ + /* *** */ + contents = strings.TrimSpace(contents) + lines := strings.Split(contents, "\n") + + /* Check if there is at least one line */ + /* *** */ + if len(lines) == 0 { + return "", "", false + } + + /* Check if the first line is a title with the '#' Markdown symbol */ + /* *** */ + if strings.HasPrefix(lines[0], "#") { // Extract subject without the '#' symbol and leading/trailing spaces + subject = strings.TrimSpace(strings.TrimPrefix(lines[0], "#")) + } else { + // If the first line is not a title, return an error + return "", "", false + } + + /* Concatenate the remaining lines to form the body */ + /* *** */ + body = strings.Join(lines[1:], "\n") + + /* Check if the body is not empty */ + /* *** */ + if body == "" { + return "", "", false + } + + html := blackfriday.Run([]byte(body)) + + /* If all checks pass, set valid to true */ + /* *** */ + return subject, string(html), true +} + func GetMailingList(cfg ApiConfig) []string { type EmailsResonse struct { Emails []string `json:"emails"` @@ -123,47 +166,34 @@ func main() { } defer r.Body.Close() arguments := ParseArguments(string(body)) - // textarea_content := string(body) - email_content, err := url.QueryUnescape(arguments["email_input"]) + + decoded_markdown, err := url.QueryUnescape(arguments["email_input"]) if err != nil { panic(err) } + subject, html_body, valid := parseMailFile(decoded_markdown) + if !valid { + log.Print("Failed to parse the provided markdown") + return + } + // Return a response to HTMX model := PostmarkTemplateModel{ Name: apiConfig.Postmark.SenderName, Email: apiConfig.Postmark.SenderEmail, - Body: email_content, - Subject: "This is a test subject", // TODO(jack): Get from user input on page + Body: html_body, + Subject: subject, // TODO(jack): Get from user input on page } renderedEmail := renderPostmarkTemplate(apiConfig, postmarkTemplate, model) - w.Write([]byte(renderedEmail.Html)) - }) - mux.HandleFunc("/subject_preview", func(w http.ResponseWriter, r *http.Request) { - log.Printf("%v %v", r.Method, r.URL) - body, err := io.ReadAll(r.Body) + // Render the priview to a file so that we can include it as an Iframe as the rendered HTML is a full document. + err = os.WriteFile("./static/preview.html", []byte(renderedEmail.Html), 0666) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + log.Panicf("Failed to write file %v", err) } - defer r.Body.Close() - arguments := ParseArguments(string(body)) - - decoded_subject, err := url.QueryUnescape(arguments["subject"]) - if err != nil { - panic(err) - } - - model := PostmarkTemplateModel{ - Name: apiConfig.Postmark.SenderName, - Email: apiConfig.Postmark.SenderEmail, - Body: "", - Subject: decoded_subject, // TODO(jack): Get from user input on page - } - renderedEmail := renderPostmarkTemplate(apiConfig, postmarkTemplate, model) - w.Write([]byte(renderedEmail.Subject)) + templates.ExecuteTemplate(w, "mail_content", renderedEmail.Subject) }) // Endpoint called on load or button press by htmx to retrieve and populate the mailing list @@ -186,6 +216,7 @@ func main() { body_string := string(body) arguments := ParseArguments(body_string) fmt.Printf("%v\n", arguments) + var recipients []string if arguments["destination"] == "test" { recipients = []string{apiConfig.TestEmail} @@ -195,17 +226,17 @@ func main() { panic("unknown destination in arguments") } - decoded_email, err := url.QueryUnescape(arguments["email_input"]) + decoded_markdown, err := url.QueryUnescape(arguments["email_input"]) if err != nil { panic(err) } - decoded_subject, err := url.QueryUnescape(arguments["subject"]) - if err != nil { - panic(err) + subject, html_body, valid := parseMailFile(decoded_markdown) + if !valid { + log.Print("Failed to parse the provided markdown") + return } - - sendBatchWithTemplate(apiConfig, decoded_email, decoded_subject, recipients) + sendBatchWithTemplate(apiConfig, html_body, subject, recipients) }) srv := &http.Server{ diff --git a/static/css/site.css b/static/css/site.css index 621ecd3..2186220 100644 --- a/static/css/site.css +++ b/static/css/site.css @@ -11,29 +11,27 @@ } } +* { + box-sizing: border-box; +} + body { + overflow-y: scroll; + overflow-x: hidden; width: 100%; height: 100%; background-color: #111; color: #CCC; tab-size: 4; line-height: 1.2; - /* text-align: justify; */ -} - -body { - overflow-y: scroll; - /* Show vertical scrollbar */ } .main-container { margin-left: auto; margin-right: auto; width: 95%; - max-width: 1080px; + max-width: 1280px; padding: 1em 0 1em 0; - /* padding-top:1em; - padding-bottom:1em; */ } .content { @@ -97,4 +95,5 @@ ul { display: flex; flex-direction: column; flex-wrap: nowrap; + width: 95%; } \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 9c8732b..64e1f11 100644 --- a/templates/index.html +++ b/templates/index.html @@ -20,21 +20,17 @@

This is an input

-
- Subject: - -

-
- -
-
 

-
+
@@ -71,4 +66,15 @@
  • {{ . }}
  • {{end}} -{{end}} \ No newline at end of file +{{end}} + +{{ block "mail_content" . }} +
    +
    Subject:
    +
    {{ . }}
    +
    +
    + + +{{ end }} \ No newline at end of file