1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
{{mark "draft"}}
account: {{account}}
{{nameLegend}}
{{if .Draft.Sent}}
THIS DRAFT HAS BEEN SENT. It is kept as the record of what went out; editing it
now changes nothing that anybody received.
{{end -}}
{{mark "headers"}}
From: {{text .From}}
Subject: {{subject .Draft.Subject}}
{{- if .Draft.InReplyTo}}
In reply to: {{text .Draft.InReplyTo}}
{{- end}}
{{- if .Draft.ParentGone}}
Note: the message this answers is no longer in the mirror — it was expunged on
the server. The reply is unaffected; only the quoted text came from it.
{{- end}}
Format: {{.Draft.WireFormat}}
{{- if .Draft.RichFormat}}
The body is MIME because the draft holds a code listing or an image,
which plain text would damage; a plain rendering goes alongside.
{{- end}}
{{- if .Draft.HasAttachments}}
It is a mixed because files are enclosed beside the body. Attaching
one does not change the body.
{{- end}}
{{mark "blocks"}}
Quoted passages below were written by whoever is being answered, not by mailweb
and not by you. Everything else is yours.
A quote is attributed twice where you have assigned a petname: `quoting` is what
you see, `goes out as` is the line the recipients see. A petname is local and is
never sent — it would mean nothing to them and is not theirs to read.
{{range .Blocks}}
[{{.Position}}] {{.Kind}}{{if .Meta.Language}} ({{text .Meta.Language}}){{end}}
{{- if .HasQuotedFrom}}
quoting {{name .QuotedFrom}}
goes out as: {{text .Meta.Attribution}}
{{- else if .Meta.Attribution}}
{{text .Meta.Attribution}}
{{- end}}
{{- if eq .Kind "file"}}
{{text .Content}} — {{text .Asset.MimeType}}, {{.AssetSize}}
fetch: {{.AssetURL}}
The type is what the bytes are, not what the upload called them.
{{- else if .Content}}
{{safe .Content}}
{{- else}}
(empty)
{{- end}}
{{end}}
{{- if .Attachments}}
{{mark "attachments"}}
Enclosed beside the body, not part of it: they add nothing to the text above,
and every mail client lists them for the recipient.
{{range .Attachments}}
{{text .Content}} — {{text .Asset.MimeType}}, {{.AssetSize}}
fetch: {{.AssetURL}}
remove: POST /draft/{{$.Draft.Token}}/assets/{{.Asset.ID}}/remove (no fields)
{{- end}}
{{.AttachmentSize}} of the {{.AttachmentLimit}} a draft may hold.
{{end}}
{{mark "sending"}}
{{if not .SMTPEnabled -}}
No SMTP is configured for this account, so nothing can be sent. What follows is
who this reply would go to.
{{end -}}
{{if .Sets}}
Each option below is a different set of people. Nothing is selected, and none of
these happens until a person presses the matching button on the HTML page.
{{range .Sets}}
{{.Label}} — {{.Count}} recipient{{if ne .Count 1}}s{{end}}
{{.Explain}}
To: {{if .To}}{{names .To}}{{else}}(nobody){{end}}
{{- if .Cc}}
Cc: {{names .Cc}}
{{- end}}
{{- if .NoReply}}
Warning: this address looks like one that does not accept replies. Mail sent
here is commonly discarded without anybody reading it.
{{- end}}
{{- if .NeedsTo}}
Cannot be sent: everyone here is in Cc and nobody is in To. A message with
an empty To is accepted by some servers and delivered to nobody. Add an
address to To with the route below.
{{- else}}
send: POST /draft/{{$.Draft.Token}}/send (form-urlencoded, set={{.Name}})
{{- end}}
{{end}}
{{- else}}
{{if .Draft.ParentID}}This draft names nobody to send to. The message it answers
carried no address that could be replied to.{{else}}This is a new mail and is
addressed to nobody yet.{{end}} Enter an address with the route below.
{{end}}
{{mark "routes"}}
Composing is not sending. This draft is stored on this machine and nothing has
left it; the send route above is the only thing that does, and it is described
here rather than advertised as an action to take.
HTML version: {{.HTMLViewURL}}
To edit this draft, use the block routes below. They take JSON, they write only
to this machine, and every one of them that can reorder answers with the whole
block list — so the positions come back from the server rather than being
guessed. A draft that has been sent refuses all of them with 409.
GET /api/drafts/{{.Draft.Token}}
this draft as blocks: subject, contents, format
PATCH /api/drafts/{{.Draft.Token}}
{"subject": "..."}
POST /api/drafts/{{.Draft.Token}}/blocks
{"kind": "text"|"quote"|"code", "after": N}
PATCH /api/drafts/{{.Draft.Token}}/blocks/{id}
{"content": "...", "meta": {...}}
POST /api/drafts/{{.Draft.Token}}/blocks/{id}/move
{"position": N}
DELETE /api/drafts/{{.Draft.Token}}/blocks/{id}
The line attributing a quoted passage is not editable: it names who wrote the
quote, in the words they used, and it goes out to everyone this reply is
addressed to.
Recipients and attachments take form encoding rather than JSON, because they are
driven by forms on the HTML page that must work without script. Both are inert:
they write to this machine, and the send route above still names a *set* rather
than an address, so nothing reaches the wire that was not stored and shown here
first.
POST /draft/{{.Draft.Token}}/recipients
(form-urlencoded) fields: address, kind=to|cc
Adds an address you name to the "entered" set. It is
kept apart from the sets derived from the message
being answered, which cannot be edited: their rows are
what the send button's label is built from. Several
addresses may be comma-separated; one bad address
adds none of them.
POST /draft/{{.Draft.Token}}/recipients/remove
(form-urlencoded) fields: address, kind=to|cc
Removes an entered address. Only from that set.
POST /draft/{{.Draft.Token}}/assets
Attaches a file. Either multipart/form-data with the
file under the field name `file` (repeatable), or the
bytes as the whole request body with ?filename=
naming it. The stored type is derived from the bytes;
whatever the request calls it is ignored. At most
25.0MB per file and {{.AttachmentLimit}} per draft, refused with
413 naming the limit.
All routes accept `Accept: text/llm`, or append `?view=llm` to the URL:
/drafts every unsent draft
/msg/{id} the message being answered
/draft/{{.Draft.Token}}
this draft
POST /draft/{{.Draft.Token}}/discard
no fields: delete this draft, sending nothing
POST /msg/{id}/reply (application/x-www-form-urlencoded)
fields: body optional, the reply's text
subject optional, overrides the derived one
Composes a draft against another message and sends
nothing; answers with the URL of the draft it made.
POST /compose (application/x-www-form-urlencoded)
fields: to, cc optional, repeatable, the addresses
subject optional
body optional, the mail's text
Composes a draft that answers nothing and sends
nothing; answers with the URL of the draft it made.
This is how a mail to somebody not already in a
thread is written.
|