email.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <?php
  2. namespace Grav\Plugin;
  3. use Grav\Common\Grav;
  4. use Grav\Common\Plugin;
  5. use Grav\Common\Twig\Twig;
  6. use Grav\Plugin\Email\Email;
  7. use RocketTheme\Toolbox\Event\Event;
  8. use Swift_RfcComplianceException;
  9. class EmailPlugin extends Plugin
  10. {
  11. /**
  12. * @var Email
  13. */
  14. protected $email;
  15. /**
  16. * @return array
  17. */
  18. public static function getSubscribedEvents()
  19. {
  20. return [
  21. 'onPluginsInitialized' => ['onPluginsInitialized', 0],
  22. 'onFormProcessed' => ['onFormProcessed', 0],
  23. 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0]
  24. ];
  25. }
  26. /**
  27. * Initialize emailing.
  28. */
  29. public function onPluginsInitialized()
  30. {
  31. require_once __DIR__ . '/vendor/autoload.php';
  32. $this->email = new Email();
  33. if ($this->email->enabled()) {
  34. $this->grav['Email'] = $this->email;
  35. }
  36. }
  37. /**
  38. * Add twig paths to plugin templates.
  39. */
  40. public function onTwigTemplatePaths()
  41. {
  42. $twig = $this->grav['twig'];
  43. $twig->twig_paths[] = __DIR__ . '/templates';
  44. }
  45. /**
  46. * Send email when processing the form data.
  47. *
  48. * @param Event $event
  49. */
  50. public function onFormProcessed(Event $event)
  51. {
  52. $form = $event['form'];
  53. $action = $event['action'];
  54. $params = $event['params'];
  55. if (!$this->email->enabled()) {
  56. return;
  57. }
  58. switch ($action) {
  59. case 'email':
  60. // Prepare Twig variables
  61. $vars = array(
  62. 'form' => $form
  63. );
  64. $grav = Grav::instance();
  65. $grav->fireEvent('onEmailSend', new Event(['params' => &$params, 'vars' => &$vars]));
  66. // Build message
  67. $message = $this->buildMessage($params, $vars);
  68. if (isset($params['attachments'])) {
  69. $filesToAttach = (array)$params['attachments'];
  70. if ($filesToAttach) foreach ($filesToAttach as $fileToAttach) {
  71. $filesValues = $form->value($fileToAttach);
  72. if ($filesValues) foreach($filesValues as $fileValues) {
  73. if (isset($fileValues['file'])) {
  74. $filename = $fileValues['file'];
  75. } else {
  76. $filename = ROOT_DIR . $fileValues['path'];
  77. }
  78. $message->attach(\Swift_Attachment::fromPath($filename));
  79. }
  80. }
  81. }
  82. // Send e-mail
  83. $this->email->send($message);
  84. break;
  85. }
  86. }
  87. /**
  88. * Build e-mail message.
  89. *
  90. * @param array $params
  91. * @param array $vars
  92. * @return \Swift_Message
  93. */
  94. protected function buildMessage(array $params, array $vars = array())
  95. {
  96. /** @var Twig $twig */
  97. $twig = $this->grav['twig'];
  98. // Extend parameters with defaults.
  99. $params += array(
  100. 'bcc' => $this->config->get('plugins.email.bcc', array()),
  101. 'body' => $this->config->get('plugins.email.body', '{% include "forms/data.html.twig" %}'),
  102. 'cc' => $this->config->get('plugins.email.cc', array()),
  103. 'cc_name' => $this->config->get('plugins.email.cc_name'),
  104. 'charset' => $this->config->get('plugins.email.charset', 'utf-8'),
  105. 'from' => $this->config->get('plugins.email.from'),
  106. 'from_name' => $this->config->get('plugins.email.from_name'),
  107. 'content_type' => $this->config->get('plugins.email.content_type', 'text/html'),
  108. 'reply_to' => $this->config->get('plugins.email.reply_to', array()),
  109. 'reply_to_name' => $this->config->get('plugins.email.reply_to_name'),
  110. 'subject' => !empty($vars['form']) && $vars['form'] instanceof Form ? $vars['form']->page()->title() : null,
  111. 'to' => $this->config->get('plugins.email.to'),
  112. 'to_name' => $this->config->get('plugins.email.to_name'),
  113. 'process_markdown' => false,
  114. );
  115. // Create message object.
  116. $message = $this->email->message();
  117. if (!$params['to']) {
  118. throw new \RuntimeException($this->grav['language']->translate('PLUGIN_EMAIL.PLEASE_CONFIGURE_A_TO_ADDRESS'));
  119. }
  120. if (!$params['from']) {
  121. throw new \RuntimeException($this->grav['language']->translate('PLUGIN_EMAIL.PLEASE_CONFIGURE_A_FROM_ADDRESS'));
  122. }
  123. // Process parameters.
  124. foreach ($params as $key => $value) {
  125. switch ($key) {
  126. case 'bcc':
  127. foreach ($this->parseAddressValue($value, $vars) as $address) {
  128. try {
  129. $message->addBcc($address->mail, $address->name);
  130. } catch (Swift_RfcComplianceException $e) {
  131. continue;
  132. }
  133. }
  134. break;
  135. case 'body':
  136. if (is_string($value)) {
  137. $body = $twig->processString($value, $vars);
  138. if ($params['process_markdown']) {
  139. $parsedown = new \Parsedown();
  140. $body = $parsedown->text($body);
  141. }
  142. $content_type = !empty($params['content_type']) ? $twig->processString($params['content_type'], $vars) : null;
  143. $charset = !empty($params['charset']) ? $twig->processString($params['charset'], $vars) : null;
  144. $message->setBody($body, $content_type, $charset);
  145. }
  146. elseif (is_array($value)) {
  147. foreach ($value as $body_part) {
  148. $body_part += array(
  149. 'charset' => $params['charset'],
  150. 'content_type' => $params['content_type'],
  151. );
  152. $body = !empty($body_part['body']) ? $twig->processString($body_part['body'], $vars) : null;
  153. if ($params['process_markdown']) {
  154. $parsedown = new \Parsedown();
  155. $body = $parsedown->text($body);
  156. }
  157. $content_type = !empty($body_part['content_type']) ? $twig->processString($body_part['content_type'], $vars) : null;
  158. $charset = !empty($body_part['charset']) ? $twig->processString($body_part['charset'], $vars) : null;
  159. if (!$message->getBody()) {
  160. $message->setBody($body, $content_type, $charset);
  161. }
  162. else {
  163. $message->addPart($body, $content_type, $charset);
  164. }
  165. }
  166. }
  167. break;
  168. case 'cc':
  169. if (is_string($value) && !empty($params['cc_name'])) {
  170. $value = array(
  171. 'mail' => $twig->processString($value, $vars),
  172. 'name' => $twig->processString($params['cc_name'], $vars),
  173. );
  174. }
  175. foreach ($this->parseAddressValue($value, $vars) as $address) {
  176. try {
  177. $message->addCc($address->mail, $address->name);
  178. } catch (Swift_RfcComplianceException $e) {
  179. continue;
  180. }
  181. }
  182. break;
  183. case 'from':
  184. if (is_string($value) && !empty($params['from_name'])) {
  185. $value = array(
  186. 'mail' => $twig->processString($value, $vars),
  187. 'name' => $twig->processString($params['from_name'], $vars),
  188. );
  189. }
  190. foreach ($this->parseAddressValue($value, $vars) as $address) {
  191. try {
  192. $message->addFrom($address->mail, $address->name);
  193. } catch (Swift_RfcComplianceException $e) {
  194. continue;
  195. }
  196. }
  197. break;
  198. case 'reply_to':
  199. if (is_string($value) && !empty($params['reply_to_name'])) {
  200. $value = array(
  201. 'mail' => $twig->processString($value, $vars),
  202. 'name' => $twig->processString($params['reply_to_name'], $vars),
  203. );
  204. }
  205. foreach ($this->parseAddressValue($value, $vars) as $address) {
  206. try {
  207. $message->addReplyTo($address->mail, $address->name);
  208. } catch (Swift_RfcComplianceException $e) {
  209. continue;
  210. }
  211. }
  212. break;
  213. case 'subject':
  214. $message->setSubject($twig->processString($this->grav['language']->translate($value), $vars));
  215. break;
  216. case 'to':
  217. if (is_string($value) && !empty($params['to_name'])) {
  218. $value = array(
  219. 'mail' => $twig->processString($value, $vars),
  220. 'name' => $twig->processString($params['to_name'], $vars),
  221. );
  222. }
  223. foreach ($this->parseAddressValue($value, $vars) as $address) {
  224. try {
  225. $message->addTo($address->mail, $address->name);
  226. } catch (Swift_RfcComplianceException $e) {
  227. continue;
  228. }
  229. }
  230. break;
  231. }
  232. }
  233. return $message;
  234. }
  235. /**
  236. * Return parsed e-mail address value.
  237. *
  238. * @param $value
  239. * @param array $vars
  240. * @return array
  241. */
  242. protected function parseAddressValue($value, array $vars = array())
  243. {
  244. $parsed = array();
  245. /** @var Twig $twig */
  246. $twig = $this->grav['twig'];
  247. // Single e-mail address string
  248. if (is_string($value)) {
  249. $parsed[] = (object) array(
  250. 'mail' => $twig->processString($value, $vars),
  251. 'name' => null,
  252. );
  253. }
  254. else {
  255. // Cast value as array
  256. $value = (array) $value;
  257. // Single e-mail address array
  258. if (!empty($value['mail'])) {
  259. $parsed[] = (object) array(
  260. 'mail' => $twig->processString($value['mail'], $vars),
  261. 'name' => !empty($value['name']) ? $twig->processString($value['name'], $vars) : NULL,
  262. );
  263. }
  264. // Multiple addresses (either as strings or arrays)
  265. elseif (!(empty($value['mail']) && !empty($value['name']))) {
  266. foreach ($value as $y => $itemx) {
  267. $addresses = $this->parseAddressValue($itemx, $vars);
  268. if (($address = reset($addresses))) {
  269. $parsed[] = $address;
  270. }
  271. }
  272. }
  273. }
  274. return $parsed;
  275. }
  276. }