You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EMailTemplate.php 38KB

iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
hace 4 años
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
hace 4 años
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
hace 4 años
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
hace 4 años
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
hace 4 años
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
hace 4 años
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
hace 4 años
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  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 Morris Jobke <hey@morrisjobke.de>
  17. * @author Roeland Jago Douma <roeland@famdouma.nl>
  18. * @author Simon Spannagel <simonspa@kth.se>
  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;width:150px;height:150px;padding:0px;position:relative;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;outline:0;text-align:center;text-decoration:none;position:absolute;max-height:100px;max-width:100px;width:auto;height:auto;position:absolute;top:0;bottom:0;left:0;right:0;margin: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:200px;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:200px;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\$;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 $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. $text = $this->themingDefaults->getName() . ' - ' . $this->themingDefaults->getSlogan($lang) . '<br>' . $l10n->t('This is an automatically sent email, please do not reply.');
  570. }
  571. if ($this->footerAdded) {
  572. return;
  573. }
  574. $this->footerAdded = true;
  575. $this->ensureBodyIsClosed();
  576. $this->htmlBody .= vsprintf($this->footer, [$text]);
  577. $this->htmlBody .= $this->tail;
  578. $this->plainBody .= PHP_EOL . '-- ' . PHP_EOL;
  579. $this->plainBody .= str_replace('<br>', PHP_EOL, $text);
  580. }
  581. /**
  582. * Returns the rendered email subject as string
  583. *
  584. * @return string
  585. */
  586. public function renderSubject(): string {
  587. return $this->subject;
  588. }
  589. /**
  590. * Returns the rendered HTML email as string
  591. *
  592. * @return string
  593. */
  594. public function renderHtml(): string {
  595. if (!$this->footerAdded) {
  596. $this->footerAdded = true;
  597. $this->ensureBodyIsClosed();
  598. $this->htmlBody .= $this->tail;
  599. }
  600. return $this->htmlBody;
  601. }
  602. /**
  603. * Returns the rendered plain text email as string
  604. *
  605. * @return string
  606. */
  607. public function renderText(): string {
  608. if (!$this->footerAdded) {
  609. $this->footerAdded = true;
  610. $this->ensureBodyIsClosed();
  611. $this->htmlBody .= $this->tail;
  612. }
  613. return $this->plainBody;
  614. }
  615. }