Email.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <?php declare(strict_types=1);
  2. namespace Grav\Plugin\Login;
  3. use Grav\Common\Config\Config;
  4. use Grav\Common\Grav;
  5. use Grav\Common\Language\Language;
  6. use Grav\Common\Page\Pages;
  7. use Grav\Common\User\Interfaces\UserInterface;
  8. use Grav\Common\Utils;
  9. use Grav\Plugin\Login\Invitations\Invitation;
  10. use Psr\Log\LoggerInterface;
  11. class Email
  12. {
  13. /**
  14. * @param UserInterface $user
  15. * @param UserInterface|null $actor
  16. * @return void
  17. * @throws \Exception
  18. */
  19. public static function sendActivationEmail(UserInterface $user, UserInterface $actor = null): void
  20. {
  21. $email = $user->email;
  22. $token = (string)$user->get('activation_token', '');
  23. if (!$email || !str_contains($token, '::')) {
  24. return;
  25. }
  26. [$token, $expire] = explode('::', $token, 2);
  27. try {
  28. $config = static::getConfig();
  29. $param_sep = $config->get('system.param_sep', ':');
  30. $activationRoute = static::getLogin()->getRoute('activate');
  31. if (!$activationRoute) {
  32. throw new \RuntimeException('User activation route does not exist!');
  33. }
  34. /** @var Pages $pages */
  35. $pages = Grav::instance()['pages'];
  36. $activationLink = $pages->url(
  37. $activationRoute . '/token' . $param_sep . $token . '/username' . $param_sep . $user->username,
  38. null,
  39. true
  40. );
  41. $context = [
  42. 'activation_link' => $activationLink,
  43. 'expire' => $expire,
  44. ];
  45. $params = [
  46. 'to' => $user->email,
  47. ];
  48. static::sendEmail('activate', $context, $params, $user, $actor);
  49. } catch (\Exception $e) {
  50. static::getLogger()->error($e->getMessage());
  51. throw $e;
  52. }
  53. }
  54. /**
  55. * @param UserInterface $user
  56. * @param UserInterface|null $actor
  57. * @return void
  58. * @throws \Exception
  59. */
  60. public static function sendResetPasswordEmail(UserInterface $user, UserInterface $actor = null): void
  61. {
  62. $email = $user->email;
  63. $token = (string)$user->get('reset', '');
  64. if (!$email || !str_contains($token, '::')) {
  65. return;
  66. }
  67. [$token, $expire] = explode('::', $token, 2);
  68. try {
  69. $param_sep = static::getConfig()->get('system.param_sep', ':');
  70. $resetRoute = static::getLogin()->getRoute('reset');
  71. if (!$resetRoute) {
  72. throw new \RuntimeException('Password reset route does not exist!');
  73. }
  74. /** @var Pages $pages */
  75. $pages = Grav::instance()['pages'];
  76. $resetLink = $pages->url(
  77. "{$resetRoute}/task{$param_sep}login.reset/token{$param_sep}{$token}/user{$param_sep}{$user->username}/nonce{$param_sep}" . Utils::getNonce('reset-form'),
  78. null,
  79. true
  80. );
  81. $context = [
  82. 'reset_link' => $resetLink,
  83. 'expire' => $expire,
  84. ];
  85. $params = [
  86. 'to' => $user->email,
  87. ];
  88. static::sendEmail('reset-password', $context, $params, $user, $actor);
  89. } catch (\Exception $e) {
  90. static::getLogger()->error($e->getMessage());
  91. throw $e;
  92. }
  93. }
  94. /**
  95. * @param UserInterface $user
  96. * @param UserInterface|null $actor
  97. * @return void
  98. * @throws \Exception
  99. */
  100. public static function sendWelcomeEmail(UserInterface $user, UserInterface $actor = null): void
  101. {
  102. if (!$user->email) {
  103. return;
  104. }
  105. try {
  106. $context = [];
  107. $params = [
  108. 'to' => $user->email,
  109. ];
  110. static::sendEmail('welcome', $context, $params, $user, $actor);
  111. } catch (\Exception $e) {
  112. static::getLogger()->error($e->getMessage());
  113. throw $e;
  114. }
  115. }
  116. /**
  117. * @param UserInterface $user
  118. * @param UserInterface|null $actor
  119. * @return void
  120. * @throws \Exception
  121. */
  122. public static function sendNotificationEmail(UserInterface $user, UserInterface $actor = null): void
  123. {
  124. try {
  125. $to = static::getConfig()->get('plugins.email.to');
  126. if (!$to) {
  127. throw new \RuntimeException(static::getLanguage()->translate('PLUGIN_LOGIN.EMAIL_NOT_CONFIGURED'));
  128. }
  129. $context = [];
  130. $params = [
  131. 'to' => $to,
  132. ];
  133. static::sendEmail('notification', $context, $params, $user, $actor);
  134. } catch (\Exception $e) {
  135. static::getLogger()->error($e->getMessage());
  136. throw $e;
  137. }
  138. }
  139. /**
  140. * @param Invitation $invitation
  141. * @param string|null $message
  142. * @param UserInterface|null $actor
  143. * @return void
  144. * @throws \Exception
  145. */
  146. public static function sendInvitationEmail(Invitation $invitation, string $message = null, UserInterface $actor = null): void
  147. {
  148. if (!$invitation->email) {
  149. return;
  150. }
  151. try {
  152. $config = static::getConfig();
  153. $param_sep = $config->get('system.param_sep', ':');
  154. $inviteRoute = static::getLogin()->getRoute('register', true);
  155. if (!$inviteRoute) {
  156. throw new \RuntimeException('User registration route does not exist!');
  157. }
  158. /** @var Pages $pages */
  159. $pages = Grav::instance()['pages'];
  160. $invitationLink = $pages->url("{$inviteRoute}/{$param_sep}{$invitation->token}", null, true);
  161. $context = [
  162. 'invitation_link' => $invitationLink,
  163. 'invitation' => $invitation,
  164. 'message' => $message,
  165. ];
  166. $params = [
  167. 'to' => $invitation->email,
  168. ];
  169. static::sendEmail('invite', $context, $params, null, $actor);
  170. } catch (\Exception $e) {
  171. static::getLogger()->error($e->getMessage());
  172. throw $e;
  173. }
  174. }
  175. protected static function sendEmail(string $template, array $context, array $params, UserInterface $user = null, UserInterface $actor = null): void
  176. {
  177. $actor = $actor ?? static::getUser();
  178. $config = static::getConfig();
  179. // Twig context.
  180. $context += [
  181. 'actor' => $actor,
  182. 'user' => $user,
  183. 'site_name' => $config->get('site.title', 'Website'),
  184. 'author' => $config->get('site.author.name', ''),
  185. ];
  186. $params += [
  187. 'body' => '',
  188. 'template' => "emails/login/{$template}.html.twig",
  189. ];
  190. $email = static::getEmail();
  191. $message = $email->buildMessage($params, $context);
  192. $failedRecipients = null;
  193. $email->send($message, $failedRecipients);
  194. if ($failedRecipients) {
  195. $language = static::getLanguage();
  196. throw new \RuntimeException($language->translate(['PLUGIN_LOGIN.FAILED_TO_SEND_EMAILS', implode(', ', $failedRecipients)]));
  197. }
  198. }
  199. /**
  200. * @return Login
  201. */
  202. protected static function getLogin(): Login
  203. {
  204. return Grav::instance()['login'];
  205. }
  206. /**
  207. * @return LoggerInterface
  208. */
  209. protected static function getLogger(): LoggerInterface
  210. {
  211. return Grav::instance()['log'];
  212. }
  213. /**
  214. * @return UserInterface
  215. */
  216. protected static function getUser(): UserInterface
  217. {
  218. return Grav::instance()['user'];
  219. }
  220. /**
  221. * @return \Grav\Plugin\Email\Email
  222. */
  223. protected static function getEmail(): \Grav\Plugin\Email\Email
  224. {
  225. return Grav::instance()['Email'];
  226. }
  227. /**
  228. * @return Config
  229. */
  230. protected static function getConfig(): Config
  231. {
  232. return Grav::instance()['config'];
  233. }
  234. /**
  235. * @return Language
  236. */
  237. protected static function getLanguage(): Language
  238. {
  239. return Grav::instance()['language'];
  240. }
  241. }