1
0

EMailTemplate.php 34 KB

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