EMailTemplate.php 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright 2017, Morris Jobke <hey@morrisjobke.de>
  5. * @copyright 2017, Lukas Reschke <lukas@statuscode.ch>
  6. *
  7. * @author Bjoern Schiessle <bjoern@schiessle.org>
  8. * @author brad2014 <brad2014@users.noreply.github.com>
  9. * @author Brad Rubenstein <brad@wbr.tech>
  10. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  11. * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
  12. * @author Joas Schilling <coding@schilljs.com>
  13. * @author Julius Härtl <jus@bitgrid.net>
  14. * @author Liam JACK <liamjack@users.noreply.github.com>
  15. * @author Lukas Reschke <lukas@statuscode.ch>
  16. * @author medcloud <42641918+medcloud@users.noreply.github.com>
  17. * @author Morris Jobke <hey@morrisjobke.de>
  18. * @author Roeland Jago Douma <roeland@famdouma.nl>
  19. * @author Tomasz Paluszkiewicz <tomasz.paluszkiewicz@gmail.com>
  20. *
  21. * @license GNU AGPL version 3 or any later version
  22. *
  23. * This program is free software: you can redistribute it and/or modify
  24. * it under the terms of the GNU Affero General Public License as
  25. * published by the Free Software Foundation, either version 3 of the
  26. * License, or (at your option) any later version.
  27. *
  28. * This program is distributed in the hope that it will be useful,
  29. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  30. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  31. * GNU Affero General Public License for more details.
  32. *
  33. * You should have received a copy of the GNU Affero General Public License
  34. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  35. *
  36. */
  37. namespace OC\Mail;
  38. use OCP\Defaults;
  39. use OCP\IURLGenerator;
  40. use OCP\L10N\IFactory;
  41. use OCP\Mail\IEMailTemplate;
  42. /**
  43. * Class EMailTemplate
  44. *
  45. * addBodyText and addBodyButtonGroup automatically opens the body
  46. * addFooter, renderHtml, renderText automatically closes the body and the HTML if opened
  47. *
  48. * @package OC\Mail
  49. */
  50. class EMailTemplate implements IEMailTemplate {
  51. /** @var Defaults */
  52. protected $themingDefaults;
  53. /** @var IURLGenerator */
  54. protected $urlGenerator;
  55. /** @var IFactory */
  56. protected $l10nFactory;
  57. /** @var string */
  58. protected $emailId;
  59. /** @var array */
  60. protected $data;
  61. /** @var string */
  62. protected $subject = '';
  63. /** @var string */
  64. protected $htmlBody = '';
  65. /** @var string */
  66. protected $plainBody = '';
  67. /** @var bool indicated if the footer is added */
  68. protected $headerAdded = false;
  69. /** @var bool indicated if the body is already opened */
  70. protected $bodyOpened = false;
  71. /** @var bool indicated if there is a list open in the body */
  72. protected $bodyListOpened = false;
  73. /** @var bool indicated if the footer is added */
  74. protected $footerAdded = false;
  75. protected $head = <<<EOF
  76. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  77. <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" style="-webkit-font-smoothing:antialiased;background:#fff!important">
  78. <head>
  79. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  80. <meta name="viewport" content="width=device-width">
  81. <title></title>
  82. <style type="text/css">@media only screen{html{min-height:100%;background:#fff}}@media only screen and (max-width:610px){table.body img{width:auto;height:auto}table.body center{min-width:0!important}table.body .container{width:95%!important}table.body .columns{height:auto!important;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:30px!important;padding-right:30px!important}th.small-12{display:inline-block!important;width:100%!important}table.menu{width:100%!important}table.menu td,table.menu th{width:auto!important;display:inline-block!important}table.menu.vertical td,table.menu.vertical th{display:block!important}table.menu[align=center]{width:auto!important}}</style>
  83. </head>
  84. <body style="-moz-box-sizing:border-box;-ms-text-size-adjust:100%;-webkit-box-sizing:border-box;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;margin:0;background:#fff!important;box-sizing:border-box;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;min-width:100%;padding:0;text-align:left;width:100%!important">
  85. <span class="preheader" style="color:#F5F5F5;display:none!important;font-size:1px;line-height:1px;max-height:0;max-width:0;mso-hide:all!important;opacity:0;overflow:hidden;visibility:hidden">
  86. </span>
  87. <table class="body" style="-webkit-font-smoothing:antialiased;margin:0;background:#fff;border-collapse:collapse;border-spacing:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;width:100%">
  88. <tr style="padding:0;text-align:left;vertical-align:top">
  89. <td class="center" align="center" valign="top" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  90. <center data-parsed="" style="min-width:580px;width:100%">
  91. EOF;
  92. protected $tail = <<<EOF
  93. </center>
  94. </td>
  95. </tr>
  96. </table>
  97. <!-- prevent Gmail on iOS font size manipulation -->
  98. <div style="display:none;white-space:nowrap;font:15px courier;line-height:0">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
  99. </body>
  100. </html>
  101. EOF;
  102. protected $header = <<<EOF
  103. <table align="center" class="wrapper header float-center" style="Margin:0 auto;background:#fff;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
  104. <tr style="padding:0;text-align:left;vertical-align:top">
  105. <td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:20px;text-align:left;vertical-align:top;word-wrap:break-word">
  106. <table align="center" class="container" style="Margin:0 auto;background:0 0;border-collapse:collapse;border-spacing:0;margin:0 auto;padding:0;text-align:inherit;vertical-align:top;width:150px">
  107. <tbody>
  108. <tr style="padding:0;text-align:left;vertical-align:top">
  109. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  110. <table class="row collapse" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%">
  111. <tbody>
  112. <tr style="padding:0;text-align:left;vertical-align:top">
  113. <center data-parsed="" style="background-color:%s;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px">
  114. <img class="logo float-center" src="%s" alt="%s" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto">
  115. </center>
  116. </tr>
  117. </tbody>
  118. </table>
  119. </td>
  120. </tr>
  121. </tbody>
  122. </table>
  123. </td>
  124. </tr>
  125. </table>
  126. <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
  127. <tbody>
  128. <tr style="padding:0;text-align:left;vertical-align:top">
  129. <td height="40px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-size:80px;font-weight:400;hyphens:auto;line-height:80px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td>
  130. </tr>
  131. </tbody>
  132. </table>
  133. EOF;
  134. protected $heading = <<<EOF
  135. <table align="center" class="container main-heading float-center" style="Margin:0 auto;background:0 0!important;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:580px">
  136. <tbody>
  137. <tr style="padding:0;text-align:left;vertical-align:top">
  138. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  139. <h1 class="text-center" style="Margin:0;Margin-bottom:10px;color:inherit;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:24px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:center;word-wrap:normal">%s</h1>
  140. </td>
  141. </tr>
  142. </tbody>
  143. </table>
  144. <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
  145. <tbody>
  146. <tr style="padding:0;text-align:left;vertical-align:top">
  147. <td height="36px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-size:40px;font-weight:400;hyphens:auto;line-height:36px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td>
  148. </tr>
  149. </tbody>
  150. </table>
  151. EOF;
  152. protected $bodyBegin = <<<EOF
  153. <table align="center" class="wrapper content float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%">
  154. <tr style="padding:0;text-align:left;vertical-align:top">
  155. <td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  156. <table align="center" class="container" style="Margin:0 auto;background:#fff;border-collapse:collapse;border-spacing:0;margin:0 auto;padding:0;text-align:inherit;vertical-align:top;width:580px">
  157. <tbody>
  158. <tr style="padding:0;text-align:left;vertical-align:top">
  159. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  160. EOF;
  161. protected $bodyText = <<<EOF
  162. <table class="row description" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%">
  163. <tbody>
  164. <tr style="padding:0;text-align:left;vertical-align:top">
  165. <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px">
  166. <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  167. <tr style="padding:0;text-align:left;vertical-align:top">
  168. <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
  169. <p style="Margin:0;Margin-bottom:10px;color:#777;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:center">%s</p>
  170. </th>
  171. <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
  172. </tr>
  173. </table>
  174. </th>
  175. </tr>
  176. </tbody>
  177. </table>
  178. EOF;
  179. // note: listBegin (like bodyBegin) is not processed through sprintf, so "%" is not escaped as "%%". (bug #12151)
  180. protected $listBegin = <<<EOF
  181. <table class="row description" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%">
  182. <tbody>
  183. <tr style="padding:0;text-align:left;vertical-align:top">
  184. <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px">
  185. <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
  186. EOF;
  187. protected $listItem = <<<EOF
  188. <tr style="padding:0;text-align:left;vertical-align:top">
  189. <td style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left;width:15px;">
  190. <p class="text-left" style="Margin:0;Margin-bottom:10px;color:#777;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;padding-left:10px;text-align:left">%s</p>
  191. </td>
  192. <td style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
  193. <p class="text-left" style="Margin:0;Margin-bottom:10px;color:#555;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;padding-left:10px;text-align:left">%s</p>
  194. </td>
  195. <td class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></td>
  196. </tr>
  197. EOF;
  198. protected $listEnd = <<<EOF
  199. </table>
  200. </th>
  201. </tr>
  202. </tbody>
  203. </table>
  204. EOF;
  205. protected $buttonGroup = <<<EOF
  206. <table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  207. <tbody>
  208. <tr style="padding:0;text-align:left;vertical-align:top">
  209. <td height="50px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:50px;font-weight:400;hyphens:auto;line-height:50px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td>
  210. </tr>
  211. </tbody>
  212. </table>
  213. <table align="center" class="row btn-group" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%">
  214. <tbody>
  215. <tr style="padding:0;text-align:left;vertical-align:top">
  216. <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px">
  217. <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  218. <tr style="padding:0;text-align:left;vertical-align:top">
  219. <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
  220. <center data-parsed="" style="min-width:490px;width:100%%">
  221. <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;max-height:60px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;background-color:%1\$s;color:#fefefe;">
  222. <tr style="padding:0;text-align:left;vertical-align:top">
  223. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  224. <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  225. <tr style="padding:0;text-align:left;vertical-align:top">
  226. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  227. <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;border-radius:2px;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;outline:1px solid %6\$s;text-decoration:none">%7\$s</a>
  228. </td>
  229. </tr>
  230. </table>
  231. </td>
  232. </tr>
  233. </table>
  234. <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto">
  235. <tr style="padding:0;text-align:left;vertical-align:top">
  236. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  237. <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  238. <tr style="padding:0;text-align:left;vertical-align:top">
  239. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:#777;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  240. <a href="%8\$s" style="Margin:0;background-color:#fff;border:0 solid #777;border-radius:2px;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;outline:1px solid #CBCBCB;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">%9\$s</a>
  241. </td>
  242. </tr>
  243. </table>
  244. </td>
  245. </tr>
  246. </table>
  247. </center>
  248. </th>
  249. <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
  250. </tr>
  251. </table>
  252. </th>
  253. </tr>
  254. </tbody>
  255. </table>
  256. EOF;
  257. protected $button = <<<EOF
  258. <table class="spacer" style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  259. <tbody>
  260. <tr style="padding:0;text-align:left;vertical-align:top">
  261. <td height="50px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:50px;font-weight:400;hyphens:auto;line-height:50px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td>
  262. </tr>
  263. </tbody>
  264. </table>
  265. <table align="center" class="row btn-group" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%%">
  266. <tbody>
  267. <tr style="padding:0;text-align:left;vertical-align:top">
  268. <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0 auto;padding:0;padding-bottom:30px;padding-left:30px;padding-right:30px;text-align:left;width:550px">
  269. <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  270. <tr style="padding:0;text-align:left;vertical-align:top">
  271. <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left">
  272. <center data-parsed="" style="min-width:490px;width:100%%">
  273. <table class="button btn default primary float-center" style="Margin:0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0;max-height:60px;padding:0;text-align:center;vertical-align:top;width:auto;background:%1\$s;color:#fefefe;background-color:%1\$s;">
  274. <tr style="padding:0;text-align:left;vertical-align:top">
  275. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  276. <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%%">
  277. <tr style="padding:0;text-align:left;vertical-align:top">
  278. <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid %2\$s;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  279. <a href="%3\$s" style="Margin:0;border:0 solid %4\$s;border-radius:2px;color:%5\$s;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;outline:1px solid %5\$s;text-decoration:none">%7\$s</a>
  280. </td>
  281. </tr>
  282. </table>
  283. </td>
  284. </tr>
  285. </table>
  286. </center>
  287. </th>
  288. <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
  289. </tr>
  290. </table>
  291. </th>
  292. </tr>
  293. </tbody>
  294. </table>
  295. EOF;
  296. protected $bodyEnd = <<<EOF
  297. </td>
  298. </tr>
  299. </tbody>
  300. </table>
  301. </td>
  302. </tr>
  303. </table>
  304. EOF;
  305. protected $footer = <<<EOF
  306. <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
  307. <tbody>
  308. <tr style="padding:0;text-align:left;vertical-align:top">
  309. <td height="60px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:60px;font-weight:400;hyphens:auto;line-height:60px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td>
  310. </tr>
  311. </tbody>
  312. </table>
  313. <table align="center" class="wrapper footer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
  314. <tr style="padding:0;text-align:left;vertical-align:top">
  315. <td class="wrapper-inner" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
  316. <center data-parsed="" style="min-width:580px;width:100%%">
  317. <table class="spacer float-center" style="Margin:0 auto;border-collapse:collapse;border-spacing:0;float:none;margin:0 auto;padding:0;text-align:center;vertical-align:top;width:100%%">
  318. <tbody>
  319. <tr style="padding:0;text-align:left;vertical-align:top">
  320. <td height="15px" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:15px;font-weight:400;hyphens:auto;line-height:15px;margin:0;mso-line-height-rule:exactly;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">&#xA0;</td>
  321. </tr>
  322. </tbody>
  323. </table>
  324. <p class="text-center float-center" align="center" style="Margin:0;Margin-bottom:10px;color:#C8C8C8;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:12px;font-weight:400;line-height:16px;margin:0;margin-bottom:10px;padding:0;text-align:center">%s</p>
  325. </center>
  326. </td>
  327. </tr>
  328. </table>
  329. EOF;
  330. public function __construct(Defaults $themingDefaults,
  331. IURLGenerator $urlGenerator,
  332. IFactory $l10nFactory,
  333. $emailId,
  334. array $data) {
  335. $this->themingDefaults = $themingDefaults;
  336. $this->urlGenerator = $urlGenerator;
  337. $this->l10nFactory = $l10nFactory;
  338. $this->htmlBody .= $this->head;
  339. $this->emailId = $emailId;
  340. $this->data = $data;
  341. }
  342. /**
  343. * Sets the subject of the email
  344. *
  345. * @param string $subject
  346. */
  347. public function setSubject(string $subject) {
  348. $this->subject = $subject;
  349. }
  350. /**
  351. * Adds a header to the email
  352. */
  353. public function addHeader() {
  354. if ($this->headerAdded) {
  355. return;
  356. }
  357. $this->headerAdded = true;
  358. $logoUrl = $this->urlGenerator->getAbsoluteURL($this->themingDefaults->getLogo(false));
  359. $this->htmlBody .= vsprintf($this->header, [$this->themingDefaults->getColorPrimary(), $logoUrl, $this->themingDefaults->getName()]);
  360. }
  361. /**
  362. * Adds a heading to the email
  363. *
  364. * @param string $title
  365. * @param string|bool $plainTitle Title that is used in the plain text email
  366. * if empty the $title is used, if false none will be used
  367. */
  368. public function addHeading(string $title, $plainTitle = '') {
  369. if ($this->footerAdded) {
  370. return;
  371. }
  372. if ($plainTitle === '') {
  373. $plainTitle = $title;
  374. }
  375. $this->htmlBody .= vsprintf($this->heading, [htmlspecialchars($title)]);
  376. if ($plainTitle !== false) {
  377. $this->plainBody .= $plainTitle . PHP_EOL . PHP_EOL;
  378. }
  379. }
  380. /**
  381. * Open the HTML body when it is not already
  382. */
  383. protected function ensureBodyIsOpened() {
  384. if ($this->bodyOpened) {
  385. return;
  386. }
  387. $this->htmlBody .= $this->bodyBegin;
  388. $this->bodyOpened = true;
  389. }
  390. /**
  391. * Adds a paragraph to the body of the email
  392. *
  393. * @param string $text Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
  394. * @param string|bool $plainText Text that is used in the plain text email
  395. * if empty the $text is used, if false none will be used
  396. */
  397. public function addBodyText(string $text, $plainText = '') {
  398. if ($this->footerAdded) {
  399. return;
  400. }
  401. if ($plainText === '') {
  402. $plainText = $text;
  403. $text = htmlspecialchars($text);
  404. }
  405. $this->ensureBodyListClosed();
  406. $this->ensureBodyIsOpened();
  407. $this->htmlBody .= vsprintf($this->bodyText, [$text]);
  408. if ($plainText !== false) {
  409. $this->plainBody .= $plainText . PHP_EOL . PHP_EOL;
  410. }
  411. }
  412. /**
  413. * Adds a list item to the body of the email
  414. *
  415. * @param string $text Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
  416. * @param string $metaInfo Note: When $plainMetaInfo falls back to this, HTML is automatically escaped in the HTML email
  417. * @param string $icon Absolute path, must be 16*16 pixels
  418. * @param string|bool $plainText Text that is used in the plain text email
  419. * if empty or true the $text is used, if false none will be used
  420. * @param string|bool $plainMetaInfo Meta info that is used in the plain text email
  421. * if empty or true the $metaInfo is used, if false none will be used
  422. * @param integer plainIndent If > 0, Indent plainText by this amount.
  423. * @since 12.0.0
  424. */
  425. public function addBodyListItem(string $text, string $metaInfo = '', string $icon = '', $plainText = '', $plainMetaInfo = '', $plainIndent = 0) {
  426. $this->ensureBodyListOpened();
  427. if ($plainText === '' || $plainText === true) {
  428. $plainText = $text;
  429. $text = htmlspecialchars($text);
  430. $text = str_replace("\n", "<br/>", $text); // convert newlines to HTML breaks
  431. }
  432. if ($plainMetaInfo === '' || $plainMetaInfo === true) {
  433. $plainMetaInfo = $metaInfo;
  434. $metaInfo = htmlspecialchars($metaInfo);
  435. }
  436. $htmlText = $text;
  437. if ($metaInfo) {
  438. $htmlText = '<em style="color:#777;">' . $metaInfo . '</em><br>' . $htmlText;
  439. }
  440. if ($icon !== '') {
  441. $icon = '<img src="' . htmlspecialchars($icon) . '" alt="&bull;">';
  442. } else {
  443. $icon = '&bull;';
  444. }
  445. $this->htmlBody .= vsprintf($this->listItem, [$icon, $htmlText]);
  446. if ($plainText !== false) {
  447. if ($plainIndent === 0) {
  448. /*
  449. * If plainIndent is not set by caller, this is the old NC17 layout code.
  450. */
  451. $this->plainBody .= ' * ' . $plainText;
  452. if ($plainMetaInfo !== false) {
  453. $this->plainBody .= ' (' . $plainMetaInfo . ')';
  454. }
  455. $this->plainBody .= PHP_EOL;
  456. } else {
  457. /*
  458. * Caller can set plainIndent > 0 to format plainText in tabular fashion.
  459. * with plainMetaInfo in column 1, and plainText in column 2.
  460. * The plainMetaInfo label is right justified in a field of width
  461. * "plainIndent". Multilines after the first are indented plainIndent+1
  462. * (to account for space after label). Fixes: #12391
  463. */
  464. /** @var string $label */
  465. $label = ($plainMetaInfo !== false)? $plainMetaInfo : '';
  466. $this->plainBody .= sprintf("%{$plainIndent}s %s\n",
  467. $label,
  468. str_replace("\n", "\n" . str_repeat(' ', $plainIndent + 1), $plainText));
  469. }
  470. }
  471. }
  472. protected function ensureBodyListOpened() {
  473. if ($this->bodyListOpened) {
  474. return;
  475. }
  476. $this->ensureBodyIsOpened();
  477. $this->bodyListOpened = true;
  478. $this->htmlBody .= $this->listBegin;
  479. }
  480. protected function ensureBodyListClosed() {
  481. if (!$this->bodyListOpened) {
  482. return;
  483. }
  484. $this->bodyListOpened = false;
  485. $this->htmlBody .= $this->listEnd;
  486. }
  487. /**
  488. * Adds a button group of two buttons to the body of the email
  489. *
  490. * @param string $textLeft Text of left button; Note: When $plainTextLeft falls back to this, HTML is automatically escaped in the HTML email
  491. * @param string $urlLeft URL of left button
  492. * @param string $textRight Text of right button; Note: When $plainTextRight falls back to this, HTML is automatically escaped in the HTML email
  493. * @param string $urlRight URL of right button
  494. * @param string $plainTextLeft Text of left button that is used in the plain text version - if unset the $textLeft is used
  495. * @param string $plainTextRight Text of right button that is used in the plain text version - if unset the $textRight is used
  496. */
  497. public function addBodyButtonGroup(string $textLeft,
  498. string $urlLeft,
  499. string $textRight,
  500. string $urlRight,
  501. string $plainTextLeft = '',
  502. string $plainTextRight = '') {
  503. if ($this->footerAdded) {
  504. return;
  505. }
  506. if ($plainTextLeft === '') {
  507. $plainTextLeft = $textLeft;
  508. $textLeft = htmlspecialchars($textLeft);
  509. }
  510. if ($plainTextRight === '') {
  511. $plainTextRight = $textRight;
  512. $textRight = htmlspecialchars($textRight);
  513. }
  514. $this->ensureBodyIsOpened();
  515. $this->ensureBodyListClosed();
  516. $color = $this->themingDefaults->getColorPrimary();
  517. $textColor = $this->themingDefaults->getTextColorPrimary();
  518. $this->htmlBody .= vsprintf($this->buttonGroup, [$color, $color, $urlLeft, $color, $textColor, $textColor, $textLeft, $urlRight, $textRight]);
  519. $this->plainBody .= PHP_EOL . $plainTextLeft . ': ' . $urlLeft . PHP_EOL;
  520. $this->plainBody .= $plainTextRight . ': ' . $urlRight . PHP_EOL . PHP_EOL;
  521. }
  522. /**
  523. * Adds a button to the body of the email
  524. *
  525. * @param string $text Text of button; Note: When $plainText falls back to this, HTML is automatically escaped in the HTML email
  526. * @param string $url URL of button
  527. * @param string|false $plainText Text of button in plain text version
  528. * if empty the $text is used, if false none will be used
  529. *
  530. * @since 12.0.0
  531. */
  532. public function addBodyButton(string $text, string $url, $plainText = '') {
  533. if ($this->footerAdded) {
  534. return;
  535. }
  536. $this->ensureBodyIsOpened();
  537. $this->ensureBodyListClosed();
  538. if ($plainText === '') {
  539. $plainText = $text;
  540. $text = htmlspecialchars($text);
  541. }
  542. $color = $this->themingDefaults->getColorPrimary();
  543. $textColor = $this->themingDefaults->getTextColorPrimary();
  544. $this->htmlBody .= vsprintf($this->button, [$color, $color, $url, $color, $textColor, $textColor, $text]);
  545. if ($plainText !== false) {
  546. $this->plainBody .= $plainText . ': ';
  547. }
  548. $this->plainBody .= $url . PHP_EOL;
  549. }
  550. /**
  551. * Close the HTML body when it is open
  552. */
  553. protected function ensureBodyIsClosed() {
  554. if (!$this->bodyOpened) {
  555. return;
  556. }
  557. $this->ensureBodyListClosed();
  558. $this->htmlBody .= $this->bodyEnd;
  559. $this->bodyOpened = false;
  560. }
  561. /**
  562. * Adds a logo and a text to the footer. <br> in the text will be replaced by new lines in the plain text email
  563. *
  564. * @param string $text If the text is empty the default "Name - Slogan<br>This is an automatically sent email" will be used
  565. */
  566. public function addFooter(string $text = '', ?string $lang = null) {
  567. if ($text === '') {
  568. $l10n = $this->l10nFactory->get('lib', $lang);
  569. $slogan = $this->themingDefaults->getSlogan($lang);
  570. if ($slogan !== '') {
  571. $slogan = ' - ' . $slogan;
  572. }
  573. $text = $this->themingDefaults->getName() . $slogan . '<br>' . $l10n->t('This is an automatically sent email, please do not reply.');
  574. }
  575. if ($this->footerAdded) {
  576. return;
  577. }
  578. $this->footerAdded = true;
  579. $this->ensureBodyIsClosed();
  580. $this->htmlBody .= vsprintf($this->footer, [$text]);
  581. $this->htmlBody .= $this->tail;
  582. $this->plainBody .= PHP_EOL . '-- ' . PHP_EOL;
  583. $this->plainBody .= str_replace('<br>', PHP_EOL, $text);
  584. }
  585. /**
  586. * Returns the rendered email subject as string
  587. *
  588. * @return string
  589. */
  590. public function renderSubject(): string {
  591. return $this->subject;
  592. }
  593. /**
  594. * Returns the rendered HTML email as string
  595. *
  596. * @return string
  597. */
  598. public function renderHtml(): string {
  599. if (!$this->footerAdded) {
  600. $this->footerAdded = true;
  601. $this->ensureBodyIsClosed();
  602. $this->htmlBody .= $this->tail;
  603. }
  604. return $this->htmlBody;
  605. }
  606. /**
  607. * Returns the rendered plain text email as string
  608. *
  609. * @return string
  610. */
  611. public function renderText(): string {
  612. if (!$this->footerAdded) {
  613. $this->footerAdded = true;
  614. $this->ensureBodyIsClosed();
  615. $this->htmlBody .= $this->tail;
  616. }
  617. return $this->plainBody;
  618. }
  619. }