Compare commits

..
16 Commits
Author SHA1 Message Date
Dave Mc Nicoll bab77d3823 - Fixed a bug where an empty hostname provided would cause a crash 2026-07-14 20:36:23 +00:00
Dave Mc Nicoll 81f5276f9b - Added named email address 2026-06-15 20:22:05 +00:00
Dave Mc Nicoll 1f61c964bc - Added setBCC and setCC methods to interface. 2024-06-03 21:53:31 -04:00
Dave Mc Nicoll fe6fdd3987 - Default value of bcc if nothing was provided 2023-11-01 11:33:38 -04:00
Dave Mc Nicoll b284b67d91 - Fixed some minor typos 2023-05-31 18:45:16 +00:00
mcndave f0b2ab10fd Merge branch 'master' of https://git.mcnd.ca/mcndave/the-bugs 2023-05-22 16:04:36 +00:00
mcndave cbfc87e12d - Migrating to SymfonyMailer from Swift 2023-05-22 16:03:46 +00:00
mcndave 1e7ac5db47 - Changed Laminas namespace 2023-03-30 18:24:33 +00:00
Dave Mc Nicoll 34b850c780 Bugfixes made on the interface 2022-02-25 16:41:49 +00:00
mcndave bee7e25935 - Work on SwiftMailer interface 2021-01-06 19:32:56 +00:00
Dave Mc Nicoll 689c3203e7 Merge branch 'master' of https://git.mcnd.ca/mcndave/the-bugs into dev 2020-10-06 11:31:08 -04:00
Dave Mc Nicoll b856ad1f94 - Fixed some function arguments 2020-10-06 11:29:38 -04:00
Dave Mc Nicoll d3e6100d17 Merge branch 'master' of https://git.mcnd.ca/mcndave/the-bugs into dev 2020-10-06 11:13:06 -04:00
Dave Mc Nicoll bec3c36657 - Removed hard-coded configs 2020-10-06 11:12:11 -04:00
Dave Mc Nicoll a7dcdf4ee7 - Work done on the Bug Email component 2019-08-21 15:54:17 -04:00
Dave Mc Nicoll ec873145d2 - Small WIP of template and adjusted the composer.json allowing registration into the autoload 2019-07-04 13:27:49 -04:00
11 changed files with 393 additions and 24 deletions

No files matched your search

+6 -1
View File
@@ -10,5 +10,10 @@
"email": "info@mcnd.ca"
}
],
"require": {}
"require": {},
"autoload": {
"psr-4": {
"TheBugs\\": "src/"
}
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace TheBugs\Email;
class EmailConfiguration
{
const AUTH_TYPE_SMTP = 1;
protected $type;
public $smtpHost = "";
public $smtpUsername = "";
public $smtpPassword = "";
public $smtpPort = 25;
public $smtpUseTLS = false;
public $toAddress = "";
public $fromAddress = "";
public $fromName = "";
public $smtpVerifyPeer = false;
public function __construct($type = self::AUTH_TYPE_SMTP)
{
$this->type = $type;
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace TheBugs\Email;
trait EmailInfoTrait
{
protected /*stringable*/ $from;
protected string|\Stringable|array $to;
protected string|\Stringable|array $cc;
protected string|\Stringable|array $bcc;
protected array $attachments = [];
protected EmailConfiguration $emailConfiguration;
public function setFrom(string|\Stringable|array $from) : MailerInterface
{
$this->from = $from;
return $this;
}
public function setTo(string|\Stringable|array $to) : MailerInterface
{
$this->to = $to;
return $this;
}
public function setCC(string|\Stringable|array $cc) : MailerInterface
{
$this->cc = $cc;
return $this;
}
public function setBCC(string|\Stringable|array $bcc) : MailerInterface
{
$this->bcc = $bcc;
return $this;
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace TheBugs\Email;
interface MailerInterface
{
public function send(string $subject, string $message, bool $html = true) : bool;
public function setFrom(string|\Stringable|array $from) : self;
public function attach(mixed $attachment) : self;
public function setTo(string|\Stringable|array $to) : self;
public function setBCC(string|\Stringable|array $bcc) : self;
public function setCC(string|\Stringable|array $cc) : self;
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace TheBugs\Email;
class SwiftMailer implements MailerInterface
{
use EmailInfoTrait;
protected $transport;
public function __construct(EmailConfiguration $configuration) {
$this->emailConfiguration = $configuration;
$this->transport = ( new \Swift_SmtpTransport($this->emailConfiguration->smtpHost, $this->emailConfiguration->smtpPort) )
->setUsername($this->emailConfiguration->smtpUsername)
->setPassword($this->emailConfiguration->smtpPassword);
}
public function send(string $subject, string $message, bool $html = true) : bool
{
$swiftObj = ( new \Swift_Message($subject) )
->setFrom( $this->from )
->setTo( $this->to )
->setCC($this->cc ?? [])
->setBCC($this->bcc ?? [])
->setBody( $message, $html ? 'text/html' : 'text/plain' );
foreach ($this->attachments as $attachment) {
$swiftObj->attach($attachment);
}
$this->attachments = [];
return ( new \Swift_Mailer($this->transport) )->send($swiftObj);
}
public function attach(mixed $attachment) : self
{
$this->attachments[] = $attachment;
return $this;
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
namespace TheBugs\Email;
use Symfony\Component\Mailer\{ Mailer, Transport };
use Symfony\Component\Mime\Email;
class SymfonyMailer implements MailerInterface
{
use EmailInfoTrait;
public Mailer $mailer;
public function __construct(EmailConfiguration $configuration) {
$this->emailConfiguration = $configuration;
if ($this->emailConfiguration->smtpHost) {
if ($this->emailConfiguration->smtpUsername) {
$this->mailer = new Mailer(Transport::fromDsn(sprintf("smtp://%s:%s@%s:%d?verify_peer=%s", $this->emailConfiguration->smtpUsername, $this->emailConfiguration->smtpPassword, $this->emailConfiguration->smtpHost, $this->emailConfiguration->smtpPort, $this->emailConfiguration->smtpVerifyPeer ? 'true' : 'false')));
}
else {
$this->mailer = new Mailer(Transport::fromDsn(sprintf("smtp://%s:%d?verify_peer=%s", $this->emailConfiguration->smtpHost, $this->emailConfiguration->smtpPort, $this->emailConfiguration->smtpVerifyPeer ? 'true' : 'false')));
}
}
}
public function send(string $subject, string $message, bool $html = true) : bool
{
$email = ( new Email() )
->subject($subject)
->from(...$this->from())
->to(...$this->to())
->cc(...$this->cc())
->bcc(...$this->bcc());
if ($html) {
$email->html($message);
}
else {
$email->text($message);
}
foreach ($this->attachments as $path) {
$email->attachFromPath($path);
}
$this->attachments = [];
try {
$this->mailer->send($email);
}
catch(\Exception $e) {
return false;
}
return true;
}
public function attach(mixed $attachment) : self
{
$this->attachments[] = $attachment;
return $this;
}
protected function from() : array
{
return $this->arrayToAddress($this->from);
}
protected function to() : array
{
return $this->arrayToAddress($this->to);
}
protected function cc() : array
{
return $this->arrayToAddress($this->cc ?? []);
}
protected function bcc() : array
{
return $this->arrayToAddress($this->bcc ?? []);
}
protected function arrayToAddress(string|\Stringable|array $array) : array
{
$list = [];
foreach((array) $array as $addr => $name) {
if (is_array($name)) {
foreach($name as $email => $fullname) {
$list[] = "$fullname <$email>";
}
}
elseif (is_numeric($addr)) {
$list[] = $name;
}
else {
$list[] = "$name <$addr>";
}
}
return $list;
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace TheBugs;
use Psr\Http\Message\ResponseInterface,
Psr\Http\Message\ServerRequestInterface,
Psr\Http\Server\MiddlewareInterface,
Psr\Http\Server\RequestHandlerInterface;
class EmailErrorMiddleware implements MiddlewareInterface
{
protected /* EmailConfiguration */ $emailConfiguration;
protected /* Callable */ $callable;
protected /* Mailer */ $mailer;
public function __construct(Email\EmailConfiguration $emailConfiguration, Email\MailerInterface $mailer, Callable $callable)
{
$this->emailConfiguration = $emailConfiguration;
$this->callable = $callable;
$this->mailer = $mailer;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$response = $handler->handle($request);
if ( $response->getStatusCode() === 500 ) {
$this->mailer->setTo($this->emailConfiguration->toAddress);
$this->mailer->setFrom([$this->emailConfiguration->fromAddress => $this->emailConfiguration->fromName]);
$bugReport = $response->getBody()->__toString();
if ( false === ( $this->mailer->send(error_get_last()['message'], $bugReport, true) ) ) {
error_log("Impossile to send an email bug report from " . static::class);
}
return $this->callable->call($this);
}
return $response;
}
}
+16 -8
View File
@@ -9,10 +9,10 @@ use Psr\Http\Message\ResponseFactoryInterface,
Psr\Http\Server\MiddlewareInterface,
Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response,
Zend\Diactoros\ServerRequest,
Zend\Diactoros\Stream,
Zend\Diactoros\Uri;
use Laminas\Diactoros\Response,
Laminas\Diactoros\ServerRequest,
Laminas\Diactoros\Stream,
Laminas\Diactoros\Uri;
class JavascriptMiddleware implements MiddlewareInterface
{
@@ -21,10 +21,12 @@ class JavascriptMiddleware implements MiddlewareInterface
"headers" => [
"User-Agent" => "TheBugs/1.0",
],
# Callable functions
"functions" => []
];
/**
* Filter list, allows custom invokable
* Filter list, allows custom invokable too
* @var array
*/
protected $filters = [];
@@ -46,9 +48,9 @@ class JavascriptMiddleware implements MiddlewareInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ( ( $this->filters['code'] ?? false ) && ($this->filters['code'] !== $request->getCode())) {
return $handler->handle($request);
}
# if ( ( $this->filters['code'] ?? false ) && ($this->filters['code'] !== $request->getCode())) {
# return $handler->handle($request);
# }
foreach($this->filters['headers'] as $key => $value) {
if ( ! in_array($value, $request->getHeader($key)) ) {
@@ -56,6 +58,12 @@ class JavascriptMiddleware implements MiddlewareInterface
}
}
foreach($this->filters['functions'] as $func) {
if ( $func->call($this) ) {
return $handler->handle($request);
}
}
$this->throwError(json_decode($request->getBody(), true));
}
-15
View File
@@ -1,15 +0,0 @@
<?php
use Whoops\Run as Whoops;
use Whoops\Handler\PrettyPageHandler as Handler;
$whoops = new Whoops();
$whoops->allowQuit(false);
$whoops->writeToOutput(false);
$whoops->pushHandler(new Handler());
IF CLI {
$handler->handleUnconditionally(true);
}
$body = $whoops->handleException($this->exception);
+46
View File
@@ -0,0 +1,46 @@
class ErrorHandler
{
constructor(options) {
if ( options ) {
if ( "url" in options ) {
this.url = options['url'];
}
}
this.catchError();
}
catchError() {
window.onerror = function(message, url, line, column, error) {
fetch(this.url ? this.url : window.location.href, {
method: "post",
headers: {
'Accept': "application/json",
'Content-Type': "application/json",
'User-Agent': "TheBugs/1.0"
},
body: JSON.stringify({
message: message,
url: url,
line: line,
column: column,
stack: error.stack,
location: window.location.toString()
})
}).then( response => response ).then(data => {
console.info("Error reported", data);
});
return false;
}.bind(this);
}
get url() {
return this._url;
}
set url(set) {
return this._url = set;
}
}
+34
View File
@@ -0,0 +1,34 @@
<script>
(send => {
XMLHttpRequest.prototype.send = function() {
this.addEventListener('load', function(e) {
switch(this.status) {
case 500:
let response = JSON.parse(this.response),
content = JSON.stringify(response.error.trace, null, 2),
element = document.createElement('div'),
body = document.querySelector("body");
body.appendChild(element);
element.outerHTML = `
<div ondblclick="this.parentNode.removeChild(this);">
<dialog style="top:10vh;width:75%;z-index:1500;background:#444;color:#444;border:0px solid #2f2a2a;padding:2px;position:fixed; max-height:80vh;">
<div style="background:#fff">
<div style="padding:15px; color:#fff; border-bottom:2px solid #444;background:#c61f1f;font-weight:bold">${response.error.message}</div>
<pre style="overflow-y:auto;max-height:65vh;padding:15px;">${content}</pre>
<div style="padding:8px 15px; color:#fff; border-top:2px solid #444;text-align:right;background:#c61f1f;font-weight:bold">${response.error.file}:${response.error.line}</div>
</div>
</dialog>
<div style="position:fixed;top:0;left:0;width:100vw;height:100vh;background:#000;opacity:0.5"></div>
</div>
`.trim();
body.querySelector("dialog").showModal();
break;
}
});
send.apply(this, arguments);
};
})(XMLHttpRequest.prototype.send);
</script>