Add Templates with Parsing
+ Changes NewComposer to return error. + Add lib to handle templates using "text/template". + Add -T option to following commands - compose. - reply - forward + Quoted replies using templates. + Forwards as body using templates + Default templates are installed similar to filters. + Templates Config in aerc.conf. - Required templates are parsed while loading config. + Add aerc-templates.7 manual for using template data.
This commit is contained in:
parent
ad68a9e4e4
commit
3ba69edab5
14 changed files with 510 additions and 143 deletions
lib/templates
160
lib/templates/template.go
Normal file
160
lib/templates/template.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net/mail"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
type TemplateData struct {
|
||||
To []*mail.Address
|
||||
Cc []*mail.Address
|
||||
Bcc []*mail.Address
|
||||
From []*mail.Address
|
||||
Date time.Time
|
||||
Subject string
|
||||
// Only available when replying with a quote
|
||||
OriginalText string
|
||||
OriginalFrom []*mail.Address
|
||||
OriginalDate time.Time
|
||||
}
|
||||
|
||||
func TestTemplateData() TemplateData {
|
||||
defaults := map[string]string{
|
||||
"To": "John Doe <john@example.com>",
|
||||
"Cc": "Josh Doe <josh@example.com>",
|
||||
"From": "Jane Smith <jane@example.com>",
|
||||
"Subject": "This is only a test",
|
||||
"OriginalText": "This is only a test text",
|
||||
"OriginalFrom": "John Doe <john@example.com>",
|
||||
"OriginalDate": time.Now().Format("Mon Jan 2, 2006 at 3:04 PM"),
|
||||
}
|
||||
|
||||
return ParseTemplateData(defaults)
|
||||
}
|
||||
|
||||
func ParseTemplateData(defaults map[string]string) TemplateData {
|
||||
originalDate, _ := time.Parse("Mon Jan 2, 2006 at 3:04 PM", defaults["OriginalDate"])
|
||||
td := TemplateData{
|
||||
To: parseAddressList(defaults["To"]),
|
||||
Cc: parseAddressList(defaults["Cc"]),
|
||||
Bcc: parseAddressList(defaults["Bcc"]),
|
||||
From: parseAddressList(defaults["From"]),
|
||||
Date: time.Now(),
|
||||
Subject: defaults["Subject"],
|
||||
OriginalText: defaults["Original"],
|
||||
OriginalFrom: parseAddressList(defaults["OriginalFrom"]),
|
||||
OriginalDate: originalDate,
|
||||
}
|
||||
return td
|
||||
}
|
||||
|
||||
func parseAddressList(list string) []*mail.Address {
|
||||
addrs, err := mail.ParseAddressList(list)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
func wrapLine(text string, lineWidth int) string {
|
||||
words := strings.Fields(text)
|
||||
if len(words) == 0 {
|
||||
return text
|
||||
}
|
||||
wrapped := words[0]
|
||||
spaceLeft := lineWidth - len(wrapped)
|
||||
for _, word := range words[1:] {
|
||||
if len(word)+1 > spaceLeft {
|
||||
wrapped += "\n" + word
|
||||
spaceLeft = lineWidth - len(word)
|
||||
} else {
|
||||
wrapped += " " + word
|
||||
spaceLeft -= 1 + len(word)
|
||||
}
|
||||
}
|
||||
|
||||
return wrapped
|
||||
}
|
||||
|
||||
func wrapText(text string, lineWidth int) string {
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
lines := strings.Split(text, "\n")
|
||||
var wrapped string
|
||||
|
||||
for _, line := range lines {
|
||||
wrapped += wrapLine(line, lineWidth) + "\n"
|
||||
}
|
||||
return wrapped
|
||||
}
|
||||
|
||||
// Wraping lines at 70 so that with the "> " of the quote it is under 72
|
||||
func quote(text string) string {
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
|
||||
quoted := "> " + wrapText(text, 70)
|
||||
quoted = strings.ReplaceAll(quoted, "\n", "\n> ")
|
||||
return quoted
|
||||
}
|
||||
|
||||
var templateFuncs = template.FuncMap{
|
||||
"quote": quote,
|
||||
"wrapText": wrapText,
|
||||
"dateFormat": time.Time.Format,
|
||||
}
|
||||
|
||||
func findTemplate(templateName string, templateDirs []string) (string, error) {
|
||||
for _, dir := range templateDirs {
|
||||
templateFile, err := homedir.Expand(path.Join(dir, templateName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(templateFile); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return templateFile, nil
|
||||
}
|
||||
|
||||
return "", errors.New("Can't find template - " + templateName)
|
||||
}
|
||||
|
||||
func ParseTemplateFromFile(templateName string, templateDirs []string, data interface{}) ([]byte, error) {
|
||||
templateFile, err := findTemplate(templateName, templateDirs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
emailTemplate, err :=
|
||||
template.New(templateName).Funcs(templateFuncs).ParseFiles(templateFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var outString bytes.Buffer
|
||||
if err := emailTemplate.Execute(&outString, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outString.Bytes(), nil
|
||||
}
|
||||
|
||||
func ParseTemplate(templateText string, data interface{}) ([]byte, error) {
|
||||
emailTemplate, err :=
|
||||
template.New("email_template").Funcs(templateFuncs).Parse(templateText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var outString bytes.Buffer
|
||||
if err := emailTemplate.Execute(&outString, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return outString.Bytes(), nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue