diff --git a/composer.json b/composer.json index bd0415fc..f36b36ea 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "ext-json": "*", "ext-mbstring": "*", "ext-openssl": "*", - "nesbot/carbon": "^2.16" + "nesbot/carbon": "^2.16", + "phpmailer/phpmailer": "^6.2" } -} \ No newline at end of file +} diff --git a/src/Console/Kernel.class.php b/src/Console/Kernel.class.php index 21323c65..706f97f4 100644 --- a/src/Console/Kernel.class.php +++ b/src/Console/Kernel.class.php @@ -17,7 +17,7 @@ class Kernel public function __construct(ApplicationLoader $applicationLoader, ServiceManager $serviceManager, ConsoleApplication $console) { - $applicationLoader->loadOnly(['elements', 'services']); + $applicationLoader->loadOnly(['elements', 'services', 'emails']); $serviceManager->processServiceSuppliers(true); diff --git a/src/Email/Email.class.php b/src/Email/Email.class.php new file mode 100644 index 00000000..22298636 --- /dev/null +++ b/src/Email/Email.class.php @@ -0,0 +1,92 @@ +fromName = $fromName; + + return $this; + } + + protected function subject(string $subject) + { + $this->subject = $subject; + + return $this; + } + + protected function text(string $message) + { + $this->message = $message; + + return $this; + } + + protected function html(string $message) + { + $this->message = $message; + + $this->usingHTML = true; + + return $this; + } + + protected function view(string $template) + { + // Replace any dot syntax within the template name + $template = str_replace(".", "/", $template); + $template = APP_DIR . '/resources/' . $template . '.html'; + + // Check if the email template exists first... + if(file_exists($template)) + { + $emailTemplateContent = file_get_contents($template); + + $emailTemplateVars = $this->getStringsBetween( + $emailTemplateContent, + '{{', '}}' + ); + + // Get publicly accessible class properties which are used for passing in view data + $emailViewData = get_object_vars($this); + + // Loop through each view key and replace any that are found + foreach($emailViewData as $viewKey => $viewData) + { + /* + * The tag is checked if it is there within this function, + * before injecting any data, that is why the $emailTemplateVars + * are passed in. XSS filtering also takes place inside this + * function as well. + */ + $this->replaceTag($viewKey, $viewData, $emailTemplateVars, $emailTemplateContent); + } + + // Set the email body to a HTML message + $this->html($emailTemplateContent); + } + + return $this; + } +} \ No newline at end of file diff --git a/src/Email/EmailManager.class.php b/src/Email/EmailManager.class.php new file mode 100644 index 00000000..c5fca511 --- /dev/null +++ b/src/Email/EmailManager.class.php @@ -0,0 +1,96 @@ +isSMTP(); + $mailer->SMTPAuth = true; + + return $mailer; + } + + private function setMailerEncryption(string $from, $mailer) + { + $encryptionTypes = [ + 'starttls' => PHPMailer::ENCRYPTION_STARTTLS, + 'smtps' => PHPMailer::ENCRYPTION_SMTPS, + ]; + + $mailer->SMTPSecure = $encryptionTypes[config("email.senders.$from.encryption")]; + + return $mailer; + } + + private function setSmtpHost(string $from, $mailer) + { + $mailer->Host = config("email.senders.$from.host"); + + return $mailer; + } + + private function setSmtpPort(string $from, $mailer) + { + $mailer->Port = (int)config("email.senders.$from.port"); + + return $mailer; + } + + private function setSmtpAuthCredentials(string $from, $mailer) + { + $mailer->Username = config("email.senders.$from.username"); + $mailer->Password = config("email.senders.$from.password"); + + return $mailer; + } + + public function send(string $to, string $from, array $ccs, array $bccs, Email $email) + { + if(empty($from)) + { + $from = config('email.default'); + } + + $mailer = $this->createNewMailer(); + + $mailer = $this->setMailerEncryption($from, $mailer); + $mailer = $this->setSmtpHost($from, $mailer); + $mailer = $this->setSmtpPort($from, $mailer); + $mailer = $this->setSmtpAuthCredentials($from, $mailer); + + $mailer->setFrom($mailer->Username, $email->fromName); + + $mailer->addAddress($to); + + foreach($ccs as $cc) + { + $mailer->addCC($cc); + } + + foreach($bccs as $bcc) + { + $mailer->addBCC($bcc); + } + + $mailer->isHTML($email->usingHTML); + + $mailer->Subject = $email->subject; + + $mailer->Body = $email->message; + + $mailer->send(); + } +} \ No newline at end of file diff --git a/src/Email/Facade/SendEmail.class.php b/src/Email/Facade/SendEmail.class.php new file mode 100644 index 00000000..4cff1fb7 --- /dev/null +++ b/src/Email/Facade/SendEmail.class.php @@ -0,0 +1,13 @@ +$method(...$arguments); + } +} \ No newline at end of file diff --git a/src/Email/SendEmail.php b/src/Email/SendEmail.php new file mode 100644 index 00000000..d79004c8 --- /dev/null +++ b/src/Email/SendEmail.php @@ -0,0 +1,68 @@ +emailManager = $emailManager; + } + + public function from(string $from) + { + $this->from = $from; + + return $this; + } + + public function to(string $to) + { + $this->to = $to; + + return $this; + } + + public function cc(string $cc) + { + $this->ccs[] = $cc; + + return $this; + } + + public function bcc(string $bcc) + { + $this->bccs[] = $bcc; + + return $this; + } + + public function send(Email $email) + { + defer(function() use ($email) + { + $email + ->setFromName() + ->setSubject() + ->setMessage(); + + $this->emailManager->send( + $this->to, + $this->from, + $this->ccs, + $this->bccs, + $email + ); + }); + } +} \ No newline at end of file diff --git a/src/System/ApplicationLoader.class.php b/src/System/ApplicationLoader.class.php index cfa13ce4..590360b0 100644 --- a/src/System/ApplicationLoader.class.php +++ b/src/System/ApplicationLoader.class.php @@ -19,6 +19,7 @@ public function load() $this->loadServices(); $this->loadMiddleware(); $this->loadControllers(); + $this->loadEmailClasses(); } public function loadOnly(array $inclusions) @@ -42,6 +43,11 @@ public function loadOnly(array $inclusions) { $this->loadControllers(); } + + if(in_array('emails', $inclusions, true)) + { + $this->loadEmailClasses(); + } } private function loadDirectory($directory) @@ -80,6 +86,11 @@ private function loadElementLogicClasses() $this->loadDirectory(APP_DIR . '/app/View/Elements/'); } + private function loadEmailClasses() + { + $this->loadDirectory(APP_DIR . '/app/Email/'); + } + public function loadThirdPartyPackages() { $composerClassmapFileLocation = APP_DIR . '/vendor/composer/autoload_classmap.php';