EMailTemplate.php 36 KB

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