gitea

Development moved to Codeberg

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
  103. 103
  104. 104
  105. 105
  106. 106
  107. 107
  108. 108
  109. 109
  110. 110
  111. 111
  112. 112
  113. 113
  114. 114
  115. 115
  116. 116
  117. 117
  118. 118
  119. 119
  120. 120
  121. 121
  122. 122
  123. 123
  124. 124
  125. 125
  126. 126
  127. 127
  128. 128
  129. 129
  130. 130
  131. 131
  132. 132
  133. 133
  134. 134
  135. 135
  136. 136
  137. 137
  138. 138
  139. 139
  140. 140
  141. 141
  142. 142
  143. 143
  144. 144
  145. 145
  146. 146
  147. 147
  148. 148
  149. 149
  150. 150
  151. 151
  152. 152
  153. 153
  154. 154
  155. 155
  156. 156
  157. 157
  158. 158
  159. 159
  160. 160
  161. 161
  162. 162
  163. 163
  164. 164
  165. 165
  166. 166
  167. 167
  168. 168
  169. 169
  170. 170
  171. 171
  172. 172
  173. 173
  174. 174
  175. 175
  176. 176
  177. 177
  178. 178
  179. 179
  180. 180
  181. 181
  182. 182
  183. 183
  184. 184
  185. 185
  186. 186
  187. 187
  188. 188
  189. 189
  190. 190
  191. 191
  192. 192
  193. 193
  194. 194
  195. 195
  196. 196
  197. 197
  198. 198
  199. 199
  200. 200
  201. 201
  202. 202
  203. 203
  204. 204
  205. 205
  206. 206
  207. 207
  208. 208
  209. 209
  210. 210
  211. 211
  212. 212
  213. 213
  214. 214
  215. 215
  216. 216
  217. 217
  218. 218
  219. 219
  220. 220
  221. 221
  222. 222
  223. 223
  224. 224
  225. 225
  226. 226
  227. 227
  228. 228
  229. 229
  230. 230
  231. 231
  232. 232
  233. 233
  234. 234
  235. 235
  236. 236
  237. 237
  238. 238
  239. 239
  240. 240
  241. 241
  242. 242
  243. 243
  244. 244
  245. 245
  246. 246
  247. 247
  248. 248
  249. 249
  250. 250
  251. 251
  252. 252
  253. 253
  254. 254
  255. 255
  256. 256
  257. 257
  258. 258
  259. 259
  260. 260
  261. 261
  262. 262
  263. 263
  264. 264
  265. 265
  266. 266
  267. 267
  268. 268
  269. 269
  270. 270
  271. 271
  272. 272
  273. 273
  274. 274
  275. 275
  276. 276
  277. 277
  278. 278
  279. 279
  280. 280
---
date: "2019-10-23T17:00:00-03:00"
title: "Mail templates"
slug: "mail-templates"
weight: 45
toc: false
draft: false
menu:
  sidebar:
    parent: "advanced"
    name: "Mail templates"
    weight: 45
    identifier: "mail-templates"
---

# Mail templates

**Table of Contents**

{{< toc >}}

To craft the e-mail subject and contents for certain operations, Gitea can be customized by using templates. The templates
for these functions are located under the [`custom` directory](https://docs.gitea.io/en-us/customizing-gitea/).
Gitea has an internal template that serves as default in case there's no custom alternative.

Custom templates are loaded when Gitea starts. Changes made to them are not recognized until Gitea is restarted again.

## Mail notifications supporting templates

Currently, the following notification events make use of templates:

| Action name | Usage                                                                                                        |
| ----------- | ------------------------------------------------------------------------------------------------------------ |
| `new`       | A new issue or pull request was created.                                                                     |
| `comment`   | A new comment was created in an existing issue or pull request.                                              |
| `close`     | An issue or pull request was closed.                                                                         |
| `reopen`    | An issue or pull request was reopened.                                                                       |
| `review`    | The head comment of a review in a pull request.                                                              |
| `approve`   | The head comment of a approving review for a pull request.                                                   |
| `reject`    | The head comment of a review requesting changes for a pull request.                                          |
| `code`      | A single comment on the code of a pull request.                                                              |
| `assigned`  | Used was assigned to an issue or pull request.                                                               |
| `default`   | Any action not included in the above categories, or when the corresponding category template is not present. |

The path for the template of a particular message type is:

```sh
custom/templates/mail/{action type}/{action name}.tmpl
```

Where `{action type}` is one of `issue` or `pull` (for pull requests), and `{action name}` is one of the names listed above.

For example, the specific template for a mail regarding a comment in a pull request is:

```sh
custom/templates/mail/pull/comment.tmpl
```

However, creating templates for each and every action type/name combination is not required.
A fallback system is used to choose the appropriate template for an event. The _first existing_
template on this list is used:

- The specific template for the desired **action type** and **action name**.
- The template for action type `issue` and the desired **action name**.
- The template for the desired **action type**, action name `default`.
- The template for action type `issue`, action name `default`.

The only mandatory template is action type `issue`, action name `default`, which is already embedded in Gitea
unless it's overridden by the user in the `custom` directory.

## Template syntax

Mail templates are UTF-8 encoded text files that need to follow one of the following formats:

```
Text and macros for the subject line
------------
Text and macros for the mail body
```

or

```
Text and macros for the mail body
```

Specifying a _subject_ section is optional (and therefore also the dash line separator). When used, the separator between
_subject_ and _mail body_ templates requires at least three dashes; no other characters are allowed in the separator line.

_Subject_ and _mail body_ are parsed by [Golang's template engine](https://golang.org/pkg/text/template/) and
are provided with a _metadata context_ assembled for each notification. The context contains the following elements:

| Name               | Type             | Available     | Usage                                                                                                                                                                                                                                             |
| ------------------ | ---------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `.FallbackSubject` | string           | Always        | A default subject line. See Below.                                                                                                                                                                                                                |
| `.Subject`         | string           | Only in body  | The _subject_, once resolved.                                                                                                                                                                                                                     |
| `.Body`            | string           | Always        | The message of the issue, pull request or comment, parsed from Markdown into HTML and sanitized. Do not confuse with the _mail body_.                                                                                                             |
| `.Link`            | string           | Always        | The address of the originating issue, pull request or comment.                                                                                                                                                                                    |
| `.Issue`           | models.Issue     | Always        | The issue (or pull request) originating the notification. To get data specific to a pull request (e.g. `HasMerged`), `.Issue.PullRequest` can be used, but care should be taken as this field will be `nil` if the issue is _not_ a pull request. |
| `.Comment`         | models.Comment   | If applicable | If the notification is from a comment added to an issue or pull request, this will contain the information about the comment.                                                                                                                     |
| `.IsPull`          | bool             | Always        | `true` if the mail notification is associated with a pull request (i.e. `.Issue.PullRequest` is not `nil`).                                                                                                                                       |
| `.Repo`            | string           | Always        | Name of the repository, including owner name (e.g. `mike/stuff`)                                                                                                                                                                                  |
| `.User`            | models.User      | Always        | Owner of the repository from which the event originated. To get the user name (e.g. `mike`),`.User.Name` can be used.                                                                                                                             |
| `.Doer`            | models.User      | Always        | User that executed the action triggering the notification event. To get the user name (e.g. `rhonda`), `.Doer.Name` can be used.                                                                                                                  |
| `.IsMention`       | bool             | Always        | `true` if this notification was only generated because the user was mentioned in the comment, while not being subscribed to the source. It will be `false` if the recipient was subscribed to the issue or repository.                            |
| `.SubjectPrefix`   | string           | Always        | `Re: ` if the notification is about other than issue or pull request creation; otherwise an empty string.                                                                                                                                         |
| `.ActionType`      | string           | Always        | `"issue"` or `"pull"`. Will correspond to the actual _action type_ independently of which template was selected.                                                                                                                                  |
| `.ActionName`      | string           | Always        | It will be one of the action types described above (`new`, `comment`, etc.), and will correspond to the actual _action name_ independently of which template was selected.                                                                        |
| `.ReviewComments`  | []models.Comment | Always        | List of code comments in a review. The comment text will be in `.RenderedContent` and the referenced code will be in `.Patch`.                                                                                                                    |

All names are case sensitive.

### The _subject_ part of the template

The template engine used for the mail _subject_ is golang's [`text/template`](https://golang.org/pkg/text/template/).
Please refer to the linked documentation for details about its syntax.

The _subject_ is built using the following steps:

- A template is selected according to the type of notification and to what templates are present.
- The template is parsed and resolved (e.g. `{{.Issue.Index}}` is converted to the number of the issue
  or pull request).
- All space-like characters (e.g. `TAB`, `LF`, etc.) are converted to normal spaces.
- All leading, trailing and redundant spaces are removed.
- The string is truncated to its first 256 runes (characters).

If the end result is an empty string, **or** no subject template was available (i.e. the selected template
did not include a subject part), Gitea's **internal default** will be used.

The internal default (fallback) subject is the equivalent of:

```sh
{{.SubjectPrefix}}[{{.Repo}}] {{.Issue.Title}} (#{{.Issue.Index}})
```

For example: `Re: [mike/stuff] New color palette (#38)`

Gitea's default subject can also be found in the template _metadata_ as `.FallbackSubject` from any of
the two templates, even if a valid subject template is present.

### The _mail body_ part of the template

The template engine used for the _mail body_ is golang's [`html/template`](https://golang.org/pkg/html/template/).
Please refer to the linked documentation for details about its syntax.

The _mail body_ is parsed after the mail subject, so there is an additional _metadata_ field which is
the actual rendered subject, after all considerations.

The expected result is HTML (including structural elements like`<html>`, `<body>`, etc.). Styling
through `<style>` blocks, `class` and `style` attributes is possible. However, `html/template`
does some [automatic escaping](https://golang.org/pkg/html/template/#hdr-Contexts) that should be considered.

Attachments (such as images or external style sheets) are not supported. However, other templates can
be referenced too, for example to provide the contents of a `<style>` element in a centralized fashion.
The external template must be placed under `custom/mail` and referenced relative to that directory.
For example, `custom/mail/styles/base.tmpl` can be included using `{{template styles/base}}`.

The mail is sent with `Content-Type: multipart/alternative`, so the body is sent in both HTML
and text formats. The latter is obtained by stripping the HTML markup.

## Troubleshooting

How a mail is rendered is directly dependent on the capabilities of the mail application. Many mail
clients don't even support HTML, so they show the text version included in the generated mail.

If the template fails to render, it will be noticed only at the moment the mail is sent.
A default subject is used if the subject template fails, and whatever was rendered successfully
from the the _mail body_ is used, disregarding the rest.

Please check [Gitea's logs](https://docs.gitea.io/en-us/logging-configuration/) for error messages in case of trouble.

## Example

`custom/templates/mail/issue/default.tmpl`:

```html
[{{.Repo}}] @{{.Doer.Name}}
{{if eq .ActionName "new"}}
    created
{{else if eq .ActionName "comment"}}
    commented on
{{else if eq .ActionName "close"}}
    closed
{{else if eq .ActionName "reopen"}}
    reopened
{{else}}
    updated
{{end}}
{{if eq .ActionType "issue"}}
    issue
{{else}}
    pull request
{{end}}
#{{.Issue.Index}}: {{.Issue.Title}}
------------
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>{{.Subject}}</title>
</head>

<body>
    {{if .IsMention}}
    <p>
        You are receiving this because @{{.Doer.Name}} mentioned you.
    </p>
    {{end}}
    <p>
        <p>
        <a href="{{AppUrl}}/{{.Doer.LowerName}}">@{{.Doer.Name}}</a>
        {{if not (eq .Doer.FullName "")}}
            ({{.Doer.FullName}})
        {{end}}
        {{if eq .ActionName "new"}}
            created
        {{else if eq .ActionName "close"}}
            closed
        {{else if eq .ActionName "reopen"}}
            reopened
        {{else}}
            updated
        {{end}}
        <a href="{{.Link}}">{{.Repo}}#{{.Issue.Index}}</a>.
        </p>
        {{if not (eq .Body "")}}
            <h3>Message content:</h3>
            <hr>
            {{.Body | Str2html}}
        {{end}}
    </p>
    <hr>
    <p>
        <a href="{{.Link}}">View it on Gitea</a>.
    </p>
</body>
</html>
```

This template produces something along these lines:

### Subject

> [mike/stuff] @rhonda commented on pull request #38: New color palette

### Mail body

> [@rhonda](#) (Rhonda Myers) updated [mike/stuff#38](#).
>
> #### Message content:
>
> \_********************************\_********************************
>
> Mike, I think we should tone down the blues a little.
> \_********************************\_********************************
>
> [View it on Gitea](#).

## Advanced

The template system contains several functions that can be used to further process and format
the messages. Here's a list of some of them:

| Name             | Parameters  | Available | Usage                                                                       |
| ---------------- | ----------- | --------- | --------------------------------------------------------------------------- |
| `AppUrl`         | -           | Any       | Gitea's URL                                                                 |
| `AppName`        | -           | Any       | Set from `app.ini`, usually "Gitea"                                         |
| `AppDomain`      | -           | Any       | Gitea's host name                                                           |
| `EllipsisString` | string, int | Any       | Truncates a string to the specified length; adds ellipsis as needed         |
| `Str2html`       | string      | Body only | Sanitizes text by removing any HTML tags from it.                           |
| `Safe`           | string      | Body only | Takes the input as HTML; can be used for `.ReviewComments.RenderedContent`. |

These are _functions_, not metadata, so they have to be used:

```html
Like this:         {{Str2html "Escape<my>text"}}
Or this:           {{"Escape<my>text" | Str2html}}
Or this:           {{AppUrl}}
But not like this: {{.AppUrl}}
```