Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ If you are updating from an earlier version, you will need to update the config
```bash
php artisan vendor:publish
```

```bash
php artisan migrate
```
Expand Down Expand Up @@ -72,7 +73,7 @@ If you would like to use your own migrations, you can skip this library migratio
```php
// In AppServiceProvider

public function boot()
public function register()
{
MailTracker::ignoreMigrations();
}
Expand Down Expand Up @@ -103,15 +104,16 @@ If you do not wish to have an email tracked, then you can add the `X-No-Track` h

### Storing content of mails in filesystem

By default, the content of an e-mail is stored in the `content` column in the database so that the e-mail can be viewed after it has been sent.
By default, the content of an e-mail is stored in the `content` column in the database so that the e-mail can be viewed after it has been sent.
If a lot of emails are sent, this can consume a lot of memory and slow down the database overall. It is possible to specify in the configuration that the content should be saved to a file in the file system.

````php
```php
'log-content-strategy' => 'filesystem',
'tracker-filesystem' => null
'tracker-filesystem-folder' => 'mail-tracker',
````
To use the filesystem you need to change the `log-content-strategy` from `database` to `filesystem`.
```
Comment on lines +110 to +114
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Vírgula faltando no snippet de config (quebra PHP ao copiar/colar).

Entre as linhas 'tracker-filesystem' e 'tracker-filesystem-folder' falta uma vírgula.

-    'tracker-filesystem' => null
+    'tracker-filesystem' => null,
     'tracker-filesystem-folder' => 'mail-tracker',
🤖 Prompt for AI Agents
In README.md around lines 110 to 114, the PHP config snippet is missing a comma
after the 'tracker-filesystem' line which will break PHP when copy/pasting; add
a trailing comma immediately after the 'tracker-filesystem' entry so the array
elements are properly separated and the snippet is valid PHP.


To use the filesystem you need to change the `log-content-strategy` from `database` to `filesystem`.
You can specify the disk with `tracker-filesystem` and the folder it should store the file in with `tracker-filesystem-folder`.

### Overriding models
Expand Down Expand Up @@ -148,6 +150,7 @@ class OwnEmailSentModel extends Model implements SentEmailModel {
If you have a specific email that you do not want to track, you can add the `X-No-Track` header to the email. This will prevent the email from being tracked. The header will be removed from the email prior to being sent.

In laravel 9 onwards you can introduce a headers method to your Mailable class. This will stop the tracking pixel/click tracking from applying to the Mailable

```php
public function headers()
{
Expand All @@ -159,7 +162,7 @@ public function headers()

## Skipping Open/Click Tracking for Anti-virus/Spam Filters

Some mail servers might scan emails before they deliver which can trigger the tracking pixel, or even clicked links. You can add an event listener to the ValidActionEvent to handle this.
Some mail servers might scan emails before they deliver which can trigger the tracking pixel, or even clicked links. You can add an event listener to the ValidActionEvent to handle this.

```php
class ValidUserListener {
Expand Down Expand Up @@ -187,27 +190,27 @@ When an email is sent, viewed, or a link is clicked, its tracking information is
You may want to do additional processing on these events, so an event is fired in these cases:

- jdavidbakr\MailTracker\Events\EmailSentEvent
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `sent_email` contains the `SentEmail` model
- jdavidbakr\MailTracker\Events\ViewEmailEvent
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `ip_address` contains the IP address that was used to trigger the event
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `ip_address` contains the IP address that was used to trigger the event
- jdavidbakr\MailTracker\Events\LinkClickedEvent
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `ip_address` contains the IP address that was used to trigger the event
- Public attribute `link_url` contains the clicked URL
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `ip_address` contains the IP address that was used to trigger the event
- Public attribute `link_url` contains the clicked URL

If you are using the Amazon SNS notification system, these events are fired so you can do additional processing.

- jdavidbakr\MailTracker\Events\EmailDeliveredEvent (when you received a "message delivered" event, you may want to mark the email as "good" or "delivered" in your database)
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `email_address` contains the specific address that was used to trigger the event
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `email_address` contains the specific address that was used to trigger the event
- jdavidbakr\MailTracker\Events\ComplaintMessageEvent (when you received a complaint, ex: marked as "spam", you may want to remove the email from your database)
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `email_address` contains the specific address that was used to trigger the event
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `email_address` contains the specific address that was used to trigger the event
- jdavidbakr\MailTracker\Events\PermanentBouncedMessageEvent (when you receive a permanent bounce, you may want to mark the email as bad or remove it from your database)
jdavidbakr\MailTracker\Events\TransientBouncedMessageEvent (when you receive a transient bounce. Check the event's public attributes for `bounce_sub_type` and `diagnostic_code` to determine if you want to do additional processing when this event is received.)
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `email_address` contains the specific address that was used to trigger the event
jdavidbakr\MailTracker\Events\TransientBouncedMessageEvent (when you receive a transient bounce. Check the event's public attributes for `bounce_sub_type` and `diagnostic_code` to determine if you want to do additional processing when this event is received.)
- Public attribute `sent_email` contains the `SentEmail` model
- Public attribute `email_address` contains the specific address that was used to trigger the event

To install an event listener, you will want to create a file like the following:

Expand Down
49 changes: 28 additions & 21 deletions config/mail-tracker.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
/**
* To disable the pixel injection, set this to false.
*/
'inject-pixel' => true,
'inject-pixel' => true,

/**
* To disable injecting tracking links, set this to false.
*/
'track-links' => true,
'track-links' => true,

/**
* Optionally expire old emails, set to 0 to keep forever.
*/
'expire-days' => 60,
'expire-days' => 60,

/**
* Where should the pingback URL route be?
*/
'route' => [
'prefix' => 'email',
'route' => [
'prefix' => 'email',
'middleware' => ['api'],
],

Expand All @@ -32,12 +32,12 @@
/**
* Where should the admin route be?
*/
'admin-route' => [
'enabled' => true, // Should the admin routes be enabled?
'prefix' => 'email-manager',
'admin-route' => [
'enabled' => true, // Should the admin routes be enabled?
'prefix' => 'email-manager',
'middleware' => [
'web',
'can:see-sent-emails'
'can:see-sent-emails',
],
],

Expand All @@ -48,60 +48,67 @@
* 'section' => 'content' for Default emailTraking use 'content'
* 'styles_section' => 'styles' for Default emailTraking use 'styles'
*/
'admin-template' => [
'name' => 'emailTrakingViews::layouts.app',
'admin-template' => [
'name' => 'emailTrakingViews::layouts.app',
'section' => 'content',
],

/**
* Number of emails per page in the admin view
*/
'emails-per-page' => 30,
'emails-per-page' => 30,

/**
* Date Format
*/
'date-format' => 'm/d/Y g:i a',
'date-format' => 'm/d/Y g:i a',

/**
* Default database connection name (optional - use null for default)
*/
'connection' => null,
'connection' => null,

/**
* The SNS notification topic - if set, discard all notifications not in this topic.
*/
'sns-topic' => null,
'sns-topic' => null,

/**
* Determines whether the body of the email is logged in the sent_emails table
*/
'log-content' => true,
'log-content' => true,

/**
* Determines whether the body should be stored in a file instead of database
* Can be either 'database' or 'filesystem'
*/
'log-content-strategy' => 'database',
'log-content-strategy' => 'database',

/**
* What filesystem we use for storing content html files
*/
'tracker-filesystem' => null,
'tracker-filesystem' => null,
'tracker-filesystem-folder' => 'mail-tracker',

/**
* What queue should we dispatch our tracking jobs to? Null will use the default queue.
*/
'tracker-queue' => null,
'tracker-queue' => null,

/**
* Size limit for content length stored in database
*/
'content-max-size' => 65535,
'content-max-size' => 65535,

/**
* Length of time to default past email search - if set, will set the default past limit to the amount of days below (Ex: => 356)
*/
'search-date-start' => null,
'search-date-start' => null,

/**
* Fallback method for when ValidateSignature has been introduced, but old links still need to be supported
*/
'fallback-event-listeners' => [
\jdavidbakr\MailTracker\Listener\DomainExistsInContentListener::class,
]
];
1 change: 1 addition & 0 deletions src/Events/ValidActionEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ValidActionEvent
use Dispatchable;

public $skip = false;
public $sent_email;

public function __construct(Model|SentEmailModel $sent_email)
{
Expand Down
22 changes: 22 additions & 0 deletions src/Events/ValidLinkEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace jdavidbakr\MailTracker\Events;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable;
use jdavidbakr\MailTracker\Contracts\SentEmailModel;

class ValidLinkEvent
{
use Dispatchable;

public $valid = false;
public $sent_email;
public $url;

public function __construct(Model|SentEmailModel $sent_email, $url)
{
$this->sent_email = $sent_email;
$this->url = $url;
}
}
19 changes: 19 additions & 0 deletions src/Listener/DomainExistsInContentListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace jdavidbakr\MailTracker\Listener;

use jdavidbakr\MailTracker\Events\ValidLinkEvent;

class DomainExistsInContentListener
{
public function handle(ValidLinkEvent $event): void
{
$url_host = parse_url($event->url, PHP_URL_HOST);
// If logging of content is on then
if (config('mail-tracker.log-content', true)) {
if ($event->sent_email && !empty($event->sent_email->domains_in_context) && in_array($url_host, $event->sent_email->domains_in_context)) {
$event->valid = true;
}
}
}
}
17 changes: 3 additions & 14 deletions src/MailTracker.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Mail\SentMessage;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use jdavidbakr\MailTracker\Contracts\SentEmailModel;
use jdavidbakr\MailTracker\Events\EmailSentEvent;
Expand Down Expand Up @@ -214,27 +215,15 @@ protected function inject_link_callback($matches)
$url = str_replace('&amp;', '&', $matches[2]);
}

return $matches[1].route(
return $matches[1].URL::signedRoute(
'mailTracker_n',
[
'l' => $url,
'h' => $this->hash
'h' => $this->hash,
]
);
}

/**
* Legacy function
*
* @param [type] $url
* @return boolean
*/
public static function hash_url($url)
{
// Replace "/" with "$"
return str_replace("/", "$", base64_encode($url));
}

/**
* Create the trackers
*
Expand Down
Loading