├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── filament-email.php ├── database ├── factories │ ├── EmailFactory.php │ └── UserFactory.php └── migrations │ ├── add_attachments_field_to_filament_email_log_table.php.stub │ ├── add_team_id_field_to_filament_email_log_table.php.stub │ └── create_filament_email_table.php.stub ├── resources ├── lang │ ├── de │ │ └── filament-email.php │ ├── en │ │ └── filament-email.php │ ├── es │ │ └── filament-email.php │ ├── fr │ │ └── filament-email.php │ ├── hu │ │ └── filament-email.php │ ├── id │ │ └── filament-email.php │ ├── it │ │ └── filament-email.php │ ├── nl │ │ └── filament-email.php │ ├── pl │ │ └── filament-email.php │ ├── pt │ │ └── filament-email.php │ ├── pt_BR │ │ └── filament-email.php │ └── tr │ │ └── filament-email.php └── views │ ├── .gitkeep │ ├── attachments.blade.php │ ├── html_view.blade.php │ └── raw_body.blade.php ├── screenshots ├── filters.png ├── logo-filament.svg ├── resend.png ├── table.png ├── update-and-resend.png └── view.png └── src ├── Commands └── FilamentEmailCommand.php ├── Facades └── FilamentEmail.php ├── Filament └── Resources │ ├── Actions │ ├── NextAction.php │ └── PreviousAction.php │ ├── Concernes │ └── CanPaginateViewRecord.php │ ├── EmailResource.php │ └── EmailResource │ ├── Actions │ ├── AdvancedResendEmailAction.php │ ├── ResendEmailAction.php │ ├── ResendEmailBulkAction.php │ └── ViewEmailAction.php │ └── Pages │ ├── ListEmails.php │ └── ViewEmail.php ├── FilamentEmail.php ├── FilamentEmailServiceProvider.php ├── Listeners └── FilamentEmailLogger.php ├── Mail └── ResendMail.php ├── Models └── Email.php └── Providers └── EmailMessageServiceProvider.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `filament-email` will be documented in this file. 4 | 5 | ## v1.12.0 - 2025-09-26 6 | 7 | ### What's Changed 8 | 9 | * Bump actions/checkout from 4 to 5 by @dependabot[bot] in https://github.com/RickDBCN/filament-email/pull/101 10 | 11 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.11.0...v1.12.0 12 | 13 | ## v1.11.0 - 2025-07-31 14 | 15 | ### What's Changed 16 | 17 | * Bump stefanzweifel/git-auto-commit-action from 5 to 6 by @dependabot[bot] in https://github.com/RickDBCN/filament-email/pull/98 18 | * Bump aglipanci/laravel-pint-action from 2.5 to 2.6 by @dependabot[bot] in https://github.com/RickDBCN/filament-email/pull/100 19 | * feat: add Portuguese (Brazil) translations for email log by @samuelterra22 in https://github.com/RickDBCN/filament-email/pull/99 20 | 21 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.10.1...v1.11.0 22 | 23 | ## v1.10.1 - 2025-06-04 24 | 25 | ### What's Changed 26 | 27 | * Fix typo in EmailResource by @hofmannsven in https://github.com/RickDBCN/filament-email/pull/97 28 | 29 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.10.0...v1.10.1 30 | 31 | ## v1.10.0 - 2025-05-25 32 | 33 | ### What's Changed 34 | 35 | * Add Spanish translations by @gboquizosanchez in https://github.com/RickDBCN/filament-email/pull/94 36 | * Update README.md by @gboquizosanchez in https://github.com/RickDBCN/filament-email/pull/96 37 | * Localization fixes by @gboquizosanchez in https://github.com/RickDBCN/filament-email/pull/95 38 | 39 | ### New Contributors 40 | 41 | * @gboquizosanchez made their first contribution in https://github.com/RickDBCN/filament-email/pull/94 42 | 43 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.9.2...v1.10.0 44 | 45 | ## v1.9.2 - 2025-05-20 46 | 47 | ### What's Changed 48 | 49 | * Bump dependabot/fetch-metadata from 2.3.0 to 2.4.0 by @dependabot in https://github.com/RickDBCN/filament-email/pull/92 50 | * French Translation by @agencetwogether in https://github.com/RickDBCN/filament-email/pull/93 51 | 52 | ### New Contributors 53 | 54 | * @agencetwogether made their first contribution in https://github.com/RickDBCN/filament-email/pull/93 55 | 56 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.9.1...v1.9.2 57 | 58 | ## v1.9.1 - 2025-04-06 59 | 60 | ### What's Changed 61 | 62 | * Fixed #89 - thanks to @Notobia for reporting 63 | 64 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.9.0...v1.9.1 65 | 66 | ## v1.9.0' - 2025-03-11 67 | 68 | ### What's Changed 69 | 70 | * Implemented new tests 71 | * Encapsulated email resource actions within a dedicated class 72 | * Resolved League\Flysystem\CorruptedPathDetected exception in the raw_body template implementation 73 | 74 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.8.0...v1.9.0' 75 | 76 | ## v1.8.0 - 2025-02-26 77 | 78 | ### What's Changed 79 | 80 | * Compatibility with Laravel 12.x 81 | * Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 by @dependabot in https://github.com/RickDBCN/filament-email/pull/85 82 | * Bump aglipanci/laravel-pint-action from 2.4 to 2.5 by @dependabot in https://github.com/RickDBCN/filament-email/pull/86 83 | * Hungarian translations and raw body view display fix by @borex in https://github.com/RickDBCN/filament-email/pull/88 84 | 85 | ### New Contributors 86 | 87 | * @borex made their first contribution in https://github.com/RickDBCN/filament-email/pull/88 88 | 89 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.7.2...v1.8.0 90 | 91 | ## v1.7.2 - 2024-11-05 92 | 93 | ### What's Changed 94 | 95 | * Add Indonesian translations by @robiokidenis in https://github.com/RickDBCN/filament-email/pull/81 96 | 97 | ### New Contributors 98 | 99 | * @robiokidenis made their first contribution in https://github.com/RickDBCN/filament-email/pull/81 100 | 101 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.7.1...v1.7.2 102 | 103 | ## v1.7.1 - 2024-11-03 104 | 105 | ### What's Changed 106 | 107 | * New from and attachments table filter 108 | * New resend e-mail bulk action 109 | * Enhanched attachments section into view 110 | * Updated translations 111 | 112 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.7.0...v1.7.1 113 | 114 | ## v1.7.0 - 2024-11-01 115 | 116 | ### What's Changed 117 | 118 | * Added loading indicator to ListEmail for large datasets 119 | 120 | ## v1.6.2 - 2024-09-15 121 | 122 | ### What's Changed 123 | 124 | * Fixed #79 - thanks to @Jacobtims for reporting 125 | * Fix: Ignore Email global scope if app is running in console 126 | 127 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.6.1...v1.6.2 128 | 129 | ## v.1.6.1 - 2024-09-01 130 | 131 | ### What's Changed 132 | 133 | * filament()->auth()->user() instead of auth()->user() by @user-aquadic in https://github.com/RickDBCN/filament-email/pull/77 134 | * Feat config cluster by @gaetan-hexadog in https://github.com/RickDBCN/filament-email/pull/78 135 | 136 | ### New Contributors 137 | 138 | * @user-aquadic made their first contribution in https://github.com/RickDBCN/filament-email/pull/77 139 | * @gaetan-hexadog made their first contribution in https://github.com/RickDBCN/filament-email/pull/78 140 | 141 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.6.0...v1.6.1 142 | 143 | ## v1.6.0 - 2024-08-30 144 | 145 | ### What's Changed 146 | 147 | * Enhanched table filter layout with new DateRangePicker 148 | 149 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.5.2...v1.6.0 150 | 151 | ## v1.5.2 - 2024-08-26 152 | 153 | ### What's Changed 154 | 155 | * Fixed #75 - Corrupted path detected (thanks to @patriktoth67 for reporting) 156 | 157 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.5.1...v1.5.2 158 | 159 | ## v1.5.1 - 2024-07-27 160 | 161 | ### What's Changed 162 | 163 | * Restored InteractsWithActions for list view 164 | 165 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.5.0...v1.5.1 166 | 167 | ## v1.5.0 - 2024-07-26 168 | 169 | ### What's Changed 170 | 171 | * Added previous/next buttons into view page 172 | 173 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.9...v1.5.0 174 | 175 | ## v1.4.9 - 2024-07-24 176 | 177 | ### What's Changed 178 | 179 | * Add German translations by @hofmannsven in https://github.com/RickDBCN/filament-email/pull/70 180 | * Bump dependabot/fetch-metadata from 2.1.0 to 2.2.0 by @dependabot in https://github.com/RickDBCN/filament-email/pull/71 181 | * Added tr translation by @eneskomur in https://github.com/RickDBCN/filament-email/pull/72 182 | 183 | ### New Contributors 184 | 185 | * @hofmannsven made their first contribution in https://github.com/RickDBCN/filament-email/pull/70 186 | * @eneskomur made their first contribution in https://github.com/RickDBCN/filament-email/pull/72 187 | 188 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.8...v1.4.9 189 | 190 | ## v1.4.8 - 2024-07-02 191 | 192 | ### What's Changed 193 | 194 | * feat: add resource lang to pt_BR. by @samuelterra22 in https://github.com/RickDBCN/filament-email/pull/67 195 | 196 | ### New Contributors 197 | 198 | * @samuelterra22 made their first contribution in https://github.com/RickDBCN/filament-email/pull/67 199 | 200 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.7...v1.4.8 201 | 202 | ## v1.4.7 - 2024-06-19 203 | 204 | ### What's Changed 205 | 206 | * Fix missing storage option in resend and delete by @cooljet84 in https://github.com/RickDBCN/filament-email/pull/66 207 | 208 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.6...v.1.4.7 209 | 210 | ## v1.4.6 - 2024-06-18 211 | 212 | ### What's Changed 213 | 214 | * Allow custom storage disk & config for attachment storage by @cooljet84 in https://github.com/RickDBCN/filament-email/pull/65 215 | 216 | ### New Contributors 217 | 218 | * @cooljet84 made their first contribution in https://github.com/RickDBCN/filament-email/pull/65 219 | 220 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.5...v1.4.6 221 | 222 | ## v1.4.5 - 2024-05-27 223 | 224 | ### What's Changed 225 | 226 | * Allow disabling of scheduled prune by @ryanldy in https://github.com/RickDBCN/filament-email/pull/62+ 227 | * Reviewed code from pull request #62 with minor changes 228 | 229 | ### New Contributors 230 | 231 | * @ryanldy made their first contribution in https://github.com/RickDBCN/filament-email/pull/62 232 | 233 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.4...v1.4.5 234 | 235 | ## v1.4.4 - 2024-05-19 236 | 237 | ### What's Changed 238 | 239 | * Fixed [#61](https://github.com/RickDBCN/filament-email/issues/61) - "Undefined array key attachments" for re-send e-mail action when there's no attachments and toggle button is disabled (thanks to @StaffCollab for reporting) 240 | 241 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.3...v1.4.4 242 | 243 | ## v1.4.3 - 2024-05-16 244 | 245 | ### What's Changed 246 | 247 | * Adding the option to configure pagination by @edwink75 in https://github.com/RickDBCN/filament-email/pull/60 248 | * Reviewed code from pull request #60 with minor changes 249 | 250 | ### New Contributors 251 | 252 | * @edwink75 made their first contribution in https://github.com/RickDBCN/filament-email/pull/60 253 | 254 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.2...v1.4.3 255 | 256 | ## v1.4.2 - 2024-05-13 257 | 258 | ### What's Changed 259 | 260 | * Enhanched view attachments layout. 261 | * Fixed some config default. 262 | * Updated README screenshot. 263 | 264 | **Full Changelog**: https://github.com/RickDBCN/filament-email/compare/v1.4.1...v1.4.2 265 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) RickDBCN 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/rickdbcn/filament-email.svg?style=flat-square)](https://packagist.org/packages/rickdbcn/filament-email) 6 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/rickdbcn/filament-email/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/rickdbcn/filament-email/actions?query=workflow%3Arun-tests+branch%3Amain) 7 | [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/rickdbcn/filament-email/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/rickdbcn/filament-email/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/rickdbcn/filament-email.svg?style=flat-square)](https://packagist.org/packages/rickdbcn/filament-email) 9 | Snyk Security 10 | License 11 | 12 | Log all outgoing emails in your Laravel project within your Filament panel. You can also resend emails with 1-click in case your recipient hasn't received your email. 13 | 14 | ## Version Compatibility 15 | 16 | | Plugin | Filament | Laravel | PHP | 17 | |--------|----------| ------------- | -------------| 18 | | 1.x | 3.x | 10.x | 8.x | 19 | | 1.x | 3.x | 11.x \| 12.x | 8.2 \| 8.3 \| 8.4 | 20 | | 2.x | 4.x | 11.x \| 12.x | 8.3 \| 8.4 | 21 | 22 | > [!CAUTION] 23 | > After update to v1.3.1 or 1.4.0 you need to re-publish and run migrations 24 | > 25 | > ```bash 26 | > php artisan vendor:publish --tag="filament-email-migrations" 27 | > php artisan migrate 28 | > ``` 29 | 30 | ## Installation 31 | 32 | You can install the package via composer: 33 | 34 | ```bash 35 | composer require rickdbcn/filament-email 36 | ``` 37 | 38 | Publish and run the migrations with 39 | 40 | ```bash 41 | php artisan vendor:publish --tag="filament-email-migrations" 42 | php artisan migrate 43 | ``` 44 | 45 | You can publish the config file with: 46 | 47 | ```bash 48 | php artisan vendor:publish --tag="filament-email-config" 49 | ``` 50 | 51 | Register the plugin through your panel service provider: 52 | ```php 53 | ->plugin(\RickDBCN\FilamentEmail\FilamentEmail::make()) 54 | ``` 55 | 56 | ## Configuration 57 | 58 | ```php 59 | use RickDBCN\FilamentEmail\Models\Email; 60 | use RickDBCN\FilamentEmail\Filament\Resources\EmailResource; 61 | 62 | return [ 63 | 64 | 'resource' => [ 65 | 'class' => EmailResource::class, 66 | 'model' => Email::class, 67 | 'cluster' => null, 68 | 'group' => null, 69 | 'sort' => null, 70 | 'icon' => null, 71 | 'default_sort_column' => 'created_at', 72 | 'default_sort_direction' => 'desc', 73 | 'datetime_format' => 'Y-m-d H:i:s', 74 | 'table_search_fields' => [ 75 | 'subject', 76 | 'from', 77 | 'to', 78 | 'cc', 79 | 'bcc', 80 | ], 81 | ], 82 | 83 | 'keep_email_for_days' => 60, 84 | 85 | 'label' => null, 86 | 87 | 'prune_enabled' => true, 88 | 89 | 'prune_crontab' => '0 0 * * *', 90 | 91 | 'can_access' => [ 92 | 'role' => [], 93 | ], 94 | 95 | 'pagination_page_options' => [ 96 | 10, 25, 50, 'all', 97 | ], 98 | 99 | 'attachments_disk' => 'local', 100 | 'store_attachments' => true, 101 | 102 | //Use this option for customize tenant model class 103 | //'tenant_model' => \App\Models\Team::class, 104 | 105 | ]; 106 | ``` 107 | 108 | ## Testing 109 | 110 | ```bash 111 | composer test 112 | ``` 113 | 114 | ## Screenshots 115 | 116 | ### E-mail list 117 | 118 | 119 | 120 | ### Advanced filters 121 | 122 | 123 | 124 | ### Resend e-mail 125 | 126 | 127 | 128 | ### Update addresses and resend e-mail 129 | 130 | 131 | 132 | ### E-mail view with attachments 133 | 134 | 135 | 136 | ## Languages Supported 137 | 138 | Filament Email Plugin is translated for: 139 | 140 | - English EN 141 | - Dutch NL 142 | - Italian IT 143 | - German DE 144 | - Portuguese PT 145 | - Turkish TR 146 | - Hungarian HU 147 | - Spanish ES 148 | 149 | ## Credits 150 | 151 | - [Rick de Boer](https://github.com/RickDBCN) 152 | - [Marco Germani](https://github.com/marcogermani87) 153 | - [All Contributors](../../contributors) 154 | 155 | ## License 156 | 157 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 158 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rickdbcn/filament-email", 3 | "description": "Log all outbound emails you send through your Filament application", 4 | "keywords": [ 5 | "RickDBCN", 6 | "laravel", 7 | "filament-email", 8 | "marcogermani87" 9 | ], 10 | "homepage": "https://github.com/rickdbcn/filament-email", 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Rick de Boer", 15 | "email": "rickdb@calvintijd.nl", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "php": "^8.3", 21 | "filament/filament": "^4.0", 22 | "illuminate/contracts": "^11.0|^12.0", 23 | "laravel/framework": "^11.0|^12.0", 24 | "spatie/laravel-package-tools": "^1.16.0", 25 | "malzariey/filament-daterangepicker-filter": "^4.0" 26 | }, 27 | "require-dev": { 28 | "laravel/pint": "^1.25", 29 | "nunomaduro/collision": "^8.0", 30 | "orchestra/testbench": "^10.0", 31 | "pestphp/pest": "^4.1", 32 | "pestphp/pest-plugin-arch": "^4.0", 33 | "pestphp/pest-plugin-laravel": "^4.0", 34 | "pestphp/pest-plugin-livewire": "^4.0" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "RickDBCN\\FilamentEmail\\": "src/", 39 | "RickDBCN\\FilamentEmail\\Database\\Factories\\": "database/factories/" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "RickDBCN\\FilamentEmail\\Tests\\": "tests/" 45 | } 46 | }, 47 | "scripts": { 48 | "post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi", 49 | "analyse": "vendor/bin/phpstan analyse", 50 | "test": "vendor/bin/pest", 51 | "test-coverage": "vendor/bin/pest --coverage", 52 | "format": "vendor/bin/pint" 53 | }, 54 | "config": { 55 | "sort-packages": true, 56 | "allow-plugins": { 57 | "pestphp/pest-plugin": true, 58 | "phpstan/extension-installer": true 59 | } 60 | }, 61 | "extra": { 62 | "laravel": { 63 | "providers": [ 64 | "RickDBCN\\FilamentEmail\\FilamentEmailServiceProvider" 65 | ], 66 | "aliases": { 67 | "FilamentEmail": "RickDBCN\\FilamentEmail\\Facades\\FilamentEmail" 68 | } 69 | } 70 | }, 71 | "minimum-stability": "dev", 72 | "prefer-stable": true 73 | } 74 | -------------------------------------------------------------------------------- /config/filament-email.php: -------------------------------------------------------------------------------- 1 | [ 9 | 'class' => EmailResource::class, 10 | 'model' => Email::class, 11 | 'cluster' => null, 12 | 'group' => null, 13 | 'sort' => null, 14 | 'icon' => null, 15 | 'default_sort_column' => 'created_at', 16 | 'default_sort_direction' => 'desc', 17 | 'datetime_format' => 'Y-m-d H:i:s', 18 | 'table_search_fields' => [ 19 | 'subject', 20 | 'from', 21 | 'to', 22 | 'cc', 23 | 'bcc', 24 | ], 25 | 'has_title_case_model_label' => false, 26 | ], 27 | 28 | 'keep_email_for_days' => 60, 29 | 30 | 'label' => null, 31 | 32 | 'prune_enabled' => true, 33 | 34 | 'prune_crontab' => '0 0 * * *', 35 | 36 | 'can_access' => [ 37 | 'role' => [], 38 | ], 39 | 40 | 'pagination_page_options' => [ 41 | 10, 25, 50, 'all', 42 | ], 43 | 44 | 'attachments_disk' => 'local', 45 | 'store_attachments' => true, 46 | 47 | // Custom tenancy options 48 | // 'tenant_model' => \App\Models\Team::class, 49 | // 'scope_to_tenant' => true 50 | 51 | ]; 52 | -------------------------------------------------------------------------------- /database/factories/EmailFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->email(), 19 | 'to' => $this->faker->email(), 20 | 'cc' => $this->faker->email(), 21 | 'subject' => $this->faker->words(5, asText: true), 22 | 'text_body' => $this->faker->paragraph(3), 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name, 16 | 'email' => $this->faker->unique()->safeEmail, 17 | ]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /database/migrations/add_attachments_field_to_filament_email_log_table.php.stub: -------------------------------------------------------------------------------- 1 | after('sent_debug_info', function (Blueprint $table) { 13 | $table->json('attachments')->nullable()->default(null); 14 | }); 15 | }); 16 | } 17 | 18 | public function down() 19 | { 20 | Schema::table('filament_email_log', function (Blueprint $table) { 21 | $table->dropColumn('attachments'); 22 | }); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /database/migrations/add_team_id_field_to_filament_email_log_table.php.stub: -------------------------------------------------------------------------------- 1 | after('id', function (Blueprint $table) { 13 | $table->foreignId('team_id')->nullable(); 14 | }); 15 | }); 16 | } 17 | 18 | public function down() 19 | { 20 | Schema::table('filament_email_log', function (Blueprint $table) { 21 | $table->dropColumn('team_id'); 22 | }); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /database/migrations/create_filament_email_table.php.stub: -------------------------------------------------------------------------------- 1 | id(); 13 | $table->string('from')->nullable(); 14 | $table->string('to')->nullable(); 15 | $table->string('cc')->nullable(); 16 | $table->string('bcc')->nullable(); 17 | $table->string('subject')->nullable(); 18 | $table->longText('text_body')->nullable(); 19 | $table->longText('html_body')->nullable(); 20 | $table->longText('raw_body')->nullable(); 21 | $table->longText('sent_debug_info')->nullable(); 22 | 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | public function down() 28 | { 29 | Schema::drop('filament_email_log'); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /resources/lang/de/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Erstellt am', 5 | 'sent_at' => 'Versendet am', 6 | 'html' => 'HTML', 7 | 'text' => 'Text', 8 | 'raw' => 'Raw', 9 | 'debug_info' => 'Debug-Info', 10 | 'cc' => 'Cc', 11 | 'to' => 'An', 12 | 'from' => 'Von', 13 | 'header' => 'Header', 14 | 'resend_email_heading' => 'E-Mail erneut senden', 15 | 'resend_email_description' => 'Sind Sie sicher, dass Sie diese E-Mail erneut senden möchten?', 16 | 'resend_email_success' => 'Die E-Mail wurde erfolgreich gesendet', 17 | 'resend_email_error' => 'E-Mail konnte nicht gesendet werden, interner Fehler', 18 | 'email_log' => 'E-Mail-Protokoll', 19 | 'subject' => 'Betreff', 20 | 'from_filter' => 'Von', 21 | 'to_filter' => 'An', 22 | 'navigation_label' => 'E-Mail-Protokoll', 23 | 'navigation_group' => 'Admin', 24 | 'bcc' => 'Bcc', 25 | 'insert_multiple_email_placelholder' => 'Geben Sie eine oder mehrere E-Mails ein', 26 | 'update_and_resend_email_heading' => 'E-Mail aktualisieren und erneut senden', 27 | 'attachments' => 'Anhänge', 28 | 'download' => 'Herunterladen', 29 | 'download_attachment_error' => 'Anhang konnte nicht heruntergeladen werden!', 30 | 'add_attachments' => 'Anhänge hinzufügen', 31 | 'attachments_number' => ':count Anhang|:count Anhänge', 32 | 'previous' => 'Vorherige', 33 | 'next' => 'Nächste', 34 | 'have_attachments' => 'Anhänge haben?', 35 | 'yes' => 'ja', 36 | 'no' => 'nein', 37 | 'model_label' => 'E-Mail-Protokoll', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/en/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Created at', 5 | 'sent_at' => 'Sent at', 6 | 'html' => 'HTML', 7 | 'text' => 'Text', 8 | 'raw' => 'Raw', 9 | 'debug_info' => 'Debug info', 10 | 'cc' => 'Cc', 11 | 'to' => 'To', 12 | 'from' => 'From', 13 | 'header' => 'Headers', 14 | 'resend_email_heading' => 'Resend e-mail', 15 | 'resend_email_description' => 'Are you sure you want to resend this e-mail?', 16 | 'resend_email_success' => 'The e-mail was sent with success', 17 | 'resend_email_error' => 'Unable to send e-mail, internal error', 18 | 'email_log' => 'E-mail Log', 19 | 'subject' => 'Subject', 20 | 'from_filter' => 'From', 21 | 'to_filter' => 'To', 22 | 'navigation_label' => 'E-mail Log', 23 | 'navigation_group' => 'Admin', 24 | 'bcc' => 'Bcc', 25 | 'insert_multiple_email_placelholder' => 'Insert one or more e-mail', 26 | 'update_and_resend_email_heading' => 'Update and resend e-mail', 27 | 'attachments' => 'Attachments', 28 | 'download' => 'Download', 29 | 'download_attachment_error' => 'Unable to download attachment!', 30 | 'add_attachments' => 'Add attachments', 31 | 'attachments_number' => ':count attachment|:count attachments', 32 | 'previous' => 'Previous', 33 | 'next' => 'Next', 34 | 'have_attachments' => 'Have attachments?', 35 | 'yes' => 'yes', 36 | 'no' => 'no', 37 | 'model_label' => 'E-mail Log', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/es/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Creado el', 5 | 'sent_at' => 'Enviado el', 6 | 'html' => 'HTML', 7 | 'text' => 'Texto', 8 | 'raw' => 'Sin formato', 9 | 'debug_info' => 'Información de depuración', 10 | 'cc' => 'Cc', 11 | 'to' => 'Para', 12 | 'from' => 'De', 13 | 'header' => 'Encabezados', 14 | 'resend_email_heading' => 'Reenviar correo electrónico', 15 | 'resend_email_description' => '¿Está seguro/a de que desea reenviar este correo electrónico?', 16 | 'resend_email_success' => 'El correo electrónico se envió correctamente', 17 | 'resend_email_error' => 'No se pudo enviar el correo electrónico, error interno', 18 | 'email_log' => 'Registro de correos', 19 | 'subject' => 'Asunto', 20 | 'from_filter' => 'De', 21 | 'to_filter' => 'Para', 22 | 'navigation_label' => 'Registro de correos', 23 | 'navigation_group' => 'Administración', 24 | 'bcc' => 'Cco', 25 | 'insert_multiple_email_placelholder' => 'Ingrese uno o más correos electrónicos', 26 | 'update_and_resend_email_heading' => 'Actualizar y reenviar correo electrónico', 27 | 'attachments' => 'Adjuntos', 28 | 'download' => 'Descargar', 29 | 'download_attachment_error' => '¡No se pudo descargar el adjunto!', 30 | 'add_attachments' => 'Agregar adjuntos', 31 | 'attachments_number' => ':count adjunto|:count adjuntos', 32 | 'previous' => 'Anterior', 33 | 'next' => 'Siguiente', 34 | 'have_attachments' => '¿Tiene adjuntos?', 35 | 'yes' => 'sí', 36 | 'no' => 'no', 37 | 'model_label' => 'Registro de correos', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/fr/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Créé le', 5 | 'sent_at' => 'Envoyé le', 6 | 'html' => 'HTML', 7 | 'text' => 'Texte', 8 | 'raw' => 'Brut', 9 | 'debug_info' => 'Informations de débogage', 10 | 'cc' => 'Cc (Copie C-carbone)', 11 | 'to' => 'A', 12 | 'from' => 'De', 13 | 'header' => 'En-têtes', 14 | 'resend_email_heading' => 'Renvoyer l\'e-mail', 15 | 'resend_email_description' => 'Etes-vous sûr de vouloir renvoyer cet e-mail ?', 16 | 'resend_email_success' => 'L\'e-mail a bien été renvoyé', 17 | 'resend_email_error' => 'Impossible d\'envoyer l\'email, une erreur interne est survenue', 18 | 'email_log' => 'E-mail Log', 19 | 'subject' => 'Sujet', 20 | 'from_filter' => 'De', 21 | 'to_filter' => 'A', 22 | 'navigation_label' => 'E-mail Log', 23 | 'navigation_group' => 'Admin', 24 | 'bcc' => 'Bcc (Copie carbone cachée)', 25 | 'insert_multiple_email_placelholder' => 'Renseigner une ou plusieurs adresses e-mail', 26 | 'update_and_resend_email_heading' => 'Mettre à jour et renvoyer l\'e-mail', 27 | 'attachments' => 'Pièces jointes', 28 | 'download' => 'Télécharger', 29 | 'download_attachment_error' => 'Impossible de télécharger la pièce jointe !', 30 | 'add_attachments' => 'Ajouter des pièces jointes', 31 | 'attachments_number' => ':count pièce jointe|:count pièces jointes', 32 | 'previous' => 'Précédent', 33 | 'next' => 'Suivant', 34 | 'have_attachments' => 'Contient une pièce jointe ?', 35 | 'yes' => 'oui', 36 | 'no' => 'non', 37 | 'model_label' => 'E-mail Log', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/hu/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Létrehozva', 5 | 'sent_at' => 'Elküldve', 6 | 'html' => 'HTML', 7 | 'text' => 'Szöveg', 8 | 'raw' => 'Forrás', 9 | 'debug_info' => 'Technikai adatok', // 'Hibakeresési információ', 10 | 'cc' => 'Másolat (Cc)', 11 | 'to' => 'Címzett', 12 | 'from' => 'Feladó', 13 | 'header' => 'Fejlécek', 14 | 'resend_email_heading' => 'E-mail újraküldése', 15 | 'resend_email_description' => 'Biztosan újra szeretné küldeni ezt az e-mailt?', 16 | 'resend_email_success' => 'Az e-mail sikeresen elküldve', 17 | 'resend_email_error' => 'Nem sikerült elküldeni az e-mailt, belső hiba', 18 | 'email_log' => 'E-mail napló', 19 | 'subject' => 'Tárgy', 20 | 'from_filter' => 'Dátumtól', 21 | 'to_filter' => 'Dátumig', 22 | 'navigation_label' => 'E-mail napló', 23 | 'navigation_group' => 'Rendszer', 24 | 'bcc' => 'Rejtett másolat (Bcc)', 25 | 'insert_multiple_email_placelholder' => 'Adjon meg egy vagy több e-mail címet', 26 | 'update_and_resend_email_heading' => 'E-mail frissítése és újraküldése', 27 | 'attachments' => 'Csatolmányok', 28 | 'download' => 'Letöltés', 29 | 'download_attachment_error' => 'Nem sikerült letölteni a csatolmányt!', 30 | 'add_attachments' => 'Csatolmányok hozzáadása', 31 | 'attachments_number' => ':count csatolmány|:count csatolmányok', 32 | 'previous' => 'Előző', 33 | 'next' => 'Következő', 34 | 'model_label' => 'E-mail napló', 35 | ]; 36 | -------------------------------------------------------------------------------- /resources/lang/id/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Dibuat pada', 5 | 'sent_at' => 'Dikirim pada', 6 | 'html' => 'HTML', 7 | 'text' => 'Teks', 8 | 'raw' => 'Mentah', 9 | 'debug_info' => 'Info debug', 10 | 'cc' => 'Cc', 11 | 'to' => 'Kepada', 12 | 'from' => 'Dari', 13 | 'header' => 'Header', 14 | 'resend_email_heading' => 'Kirim ulang email', 15 | 'resend_email_description' => 'Apakah Anda yakin ingin mengirim ulang email ini?', 16 | 'resend_email_success' => 'Email berhasil dikirim', 17 | 'resend_email_error' => 'Tidak dapat mengirim email, terjadi kesalahan internal', 18 | 'email_log' => 'Log Email', 19 | 'subject' => 'Subjek', 20 | 'from_filter' => 'Dari', 21 | 'to_filter' => 'Kepada', 22 | 'navigation_label' => 'Log Email', 23 | 'navigation_group' => 'Admin', 24 | 'bcc' => 'Bcc', 25 | 'insert_multiple_email_placelholder' => 'Masukkan satu atau lebih email', 26 | 'update_and_resend_email_heading' => 'Perbarui dan kirim ulang email', 27 | 'attachments' => 'Lampiran', 28 | 'download' => 'Unduh', 29 | 'download_attachment_error' => 'Tidak dapat mengunduh lampiran!', 30 | 'add_attachments' => 'Tambah lampiran', 31 | 'attachments_number' => ':count lampiran|:count lampiran', 32 | 'previous' => 'Sebelumnya', 33 | 'next' => 'Berikutnya', 34 | 'have_attachments' => 'Memiliki lampiran?', 35 | 'yes' => 'ya', 36 | 'no' => 'tidak', 37 | 'model_label' => 'Log Email', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/it/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Creato il', 5 | 'sent_at' => 'Data di invio', 6 | 'html' => 'HTML', 7 | 'text' => 'Testo', 8 | 'raw' => 'Raw', 9 | 'debug_info' => 'Info di debug', 10 | 'cc' => 'Cc', 11 | 'to' => 'A', 12 | 'from' => 'Da', 13 | 'header' => 'Intestazione', 14 | 'resend_email_heading' => 'Re-invia e-mail', 15 | 'resend_email_description' => 'Sei sicuro di voler re-inviare questa e-mail?', 16 | 'resend_email_success' => "L'e-mail è stata inviata con successo", 17 | 'resend_email_error' => "Impossibile inviare l'email, errore interno", 18 | 'email_log' => 'E-mail Log', 19 | 'subject' => 'Oggetto', 20 | 'from_filter' => 'Dal', 21 | 'to_filter' => 'Al', 22 | 'navigation_label' => 'E-mail Log', 23 | 'navigation_group' => 'Admin', 24 | 'bcc' => 'Bcc', 25 | 'insert_multiple_email_placelholder' => 'Inserisci una o più e-mail', 26 | 'update_and_resend_email_heading' => 'Modifica and re-invia e-mail', 27 | 'attachments' => 'Allegati', 28 | 'download' => 'Scarica', 29 | 'download_attachment_error' => "Impossibile scaricare l'allegato!", 30 | 'add_attachments' => 'Aggiungi allegati', 31 | 'attachments_number' => ':count allegato|:count allegati', 32 | 'previous' => 'Precedente', 33 | 'next' => 'Prossimo', 34 | 'have_attachments' => 'Contiene allegati?', 35 | 'yes' => 'si', 36 | 'no' => 'no', 37 | 'model_label' => 'E-mail Log', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/nl/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Aangemaakt op', 5 | 'sent_at' => 'Verstuurd op', 6 | 'html' => 'HTML', 7 | 'text' => 'Text', 8 | 'raw' => 'Ruwe', 9 | 'debug_info' => 'Foutopsporingsinformatie', 10 | 'cc' => 'Cc', 11 | 'to' => 'Aan', 12 | 'from' => 'Van', 13 | 'header' => 'Headers', 14 | 'resend_email_heading' => 'Verstuur e-mail opnieuw', 15 | 'resend_email_description' => 'Weet je zeker dat je de mail opnieuw wilt versturen?', 16 | 'resend_email_success' => 'De e-mail is succesvol verzonden', 17 | 'resend_email_error' => 'Er is iets misgegaan met het versturen van de e-mail', 18 | 'email_log' => 'E-mail Log', 19 | 'subject' => 'Onderwerp', 20 | 'from_filter' => 'Van', 21 | 'to_filter' => 'Aan', 22 | 'navigation_label' => 'E-mail Log', 23 | 'navigation_group' => 'Admin', 24 | 'bcc' => 'Bcc', 25 | 'insert_multiple_email_placelholder' => 'Type een of meerdere e-mailadressen in', 26 | 'update_and_resend_email_heading' => 'Bijwerken en e-mail opnieuw versturen', 27 | 'attachments' => 'Bijlagen', 28 | 'download' => 'Downloaden', 29 | 'download_attachment_error' => 'Kan bijlage niet downloaden!', 30 | 'add_attachments' => 'Bijlagen toevoegen', 31 | 'attachments_number' => ':count bijlage|:count bijlagen', 32 | 'previous' => 'Vorig', 33 | 'next' => 'Volgende', 34 | 'have_attachments' => 'Heb bijlagen?', 35 | 'yes' => 'ja', 36 | 'no' => 'nee', 37 | 'model_label' => 'E-mail Log', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/pl/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Utworzono', 5 | 'sent_at' => 'Data wysłania', 6 | 'html' => 'HTML', 7 | 'text' => 'Text', 8 | 'raw' => 'Raw', 9 | 'debug_info' => 'Debugowanie', 10 | 'cc' => 'Kopia do', 11 | 'to' => 'Do', 12 | 'from' => 'Od', 13 | 'header' => 'Nagłówki', 14 | 'resend_email_heading' => 'Wyślij ponownie e-mail', 15 | 'resend_email_description' => 'Czy na pewno chcesz ponownie wysłać tę wiadomość e-mail?', 16 | 'resend_email_success' => 'Wiadomość e-mail została wysłana pomyślnie.', 17 | 'resend_email_error' => 'Nie można wysłać wiadomości e-mail, błąd wewnętrzny.', 18 | 'email_log' => 'Dziennik wiadomości e-mail', 19 | 'subject' => 'Temat', 20 | 'from_filter' => 'Od', 21 | 'to_filter' => 'Do', 22 | 'navigation_label' => 'Dziennik wiadomości e-mail', 23 | 'navigation_group' => 'Administracja', 24 | 'bcc' => 'Ukryta kopia do', 25 | 'insert_multiple_email_placelholder' => 'Wprowadź jeden lub więcej adresów e-mail', 26 | 'update_and_resend_email_heading' => 'Zaktualizuj i wyślij ponownie wiadomość e-mail', 27 | 'attachments' => 'Załączniki', 28 | 'download' => 'Pobierz', 29 | 'download_attachment_error' => 'Nie można pobrać załącznika!', 30 | 'add_attachments' => 'Dodaj załączniki', 31 | 'attachments_number' => '{0} Brak załączników|{1} :count załącznik|[2,4] :count załączniki|[5,*] :count załączników', 32 | 'previous' => 'Cofnij', 33 | 'next' => 'Dalej', 34 | 'have_attachments' => 'Posiada załączniki?', 35 | 'yes' => 'tak', 36 | 'no' => 'nie', 37 | 'model_label' => 'Dziennik wiadomości e-mail', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/pt/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Criado em', 5 | 'sent_at' => 'Enviado em', 6 | 'html' => 'HTML', 7 | 'text' => 'Texto', 8 | 'raw' => 'Bruto', 9 | 'debug_info' => 'Informações de depuração', 10 | 'cc' => 'Cc', 11 | 'to' => 'Para', 12 | 'from' => 'De', 13 | 'header' => 'Cabeçalhos', 14 | 'resend_email_heading' => 'Reenviar e-mail', 15 | 'resend_email_description' => 'Você tem certeza que deseja reenviar este e-mail?', 16 | 'resend_email_success' => 'O e-mail foi enviado com sucesso', 17 | 'resend_email_error' => 'Não foi possível enviar o e-mail, erro interno', 18 | 'email_log' => 'Registro de E-mail', 19 | 'subject' => 'Assunto', 20 | 'from_filter' => 'De', 21 | 'to_filter' => 'Para', 22 | 'navigation_label' => 'Registro de E-mail', 23 | 'navigation_group' => 'Admin', 24 | 'bcc' => 'Bcc', 25 | 'insert_multiple_email_placelholder' => 'Insira um ou mais e-mails', 26 | 'update_and_resend_email_heading' => 'Atualizar e reenviar e-mail', 27 | 'attachments' => 'Anexos', 28 | 'download' => 'Baixar', 29 | 'download_attachment_error' => 'Não foi possível baixar o anexo!', 30 | 'add_attachments' => 'Adicionar anexos', 31 | 'attachments_number' => ':count anexo|:count anexos', 32 | 'previous' => 'Próximo', 33 | 'next' => 'Anterior', 34 | 'have_attachments' => 'Tem anexos?', 35 | 'yes' => 'sim', 36 | 'no' => 'não', 37 | 'model_label' => 'Registro de E-mail', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/pt_BR/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Criado em', 5 | 'sent_at' => 'Enviado em', 6 | 'html' => 'HTML', 7 | 'text' => 'Texto', 8 | 'raw' => 'Bruto', 9 | 'debug_info' => 'Informações de depuração', 10 | 'cc' => 'Cc', 11 | 'to' => 'Para', 12 | 'from' => 'De', 13 | 'header' => 'Cabeçalhos', 14 | 'resend_email_heading' => 'Reenviar e-mail', 15 | 'resend_email_description' => 'Tem certeza de que deseja reenviar este e-mail?', 16 | 'resend_email_success' => 'O e-mail foi enviado com sucesso', 17 | 'resend_email_error' => 'Não foi possível enviar o e-mail, erro interno', 18 | 'email_log' => 'Log de E-mails', 19 | 'subject' => 'Assunto', 20 | 'from_filter' => 'Remetente', 21 | 'to_filter' => 'Destinatário', 22 | 'navigation_label' => 'Log de E-mails', 23 | 'navigation_group' => 'Administração', 24 | 'bcc' => 'Cco', 25 | 'insert_multiple_email_placelholder' => 'Insira um ou mais e-mails', 26 | 'update_and_resend_email_heading' => 'Atualizar e reenviar e-mail', 27 | 'attachments' => 'Anexos', 28 | 'download' => 'Baixar', 29 | 'download_attachment_error' => 'Não foi possível baixar o anexo!', 30 | 'add_attachments' => 'Adicionar anexos', 31 | 'attachments_number' => ':count anexo|:count anexos', 32 | 'previous' => 'Anterior', 33 | 'next' => 'Próximo', 34 | 'have_attachments' => 'Possui anexos?', 35 | 'yes' => 'sim', 36 | 'no' => 'não', 37 | 'model_label' => 'Log de E-mails', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/lang/tr/filament-email.php: -------------------------------------------------------------------------------- 1 | 'Oluşturulma tarihi', 5 | 'sent_at' => 'Gönderilme tarihi', 6 | 'html' => 'HTML', 7 | 'text' => 'Metin', 8 | 'raw' => 'Ham Veri', 9 | 'debug_info' => 'Hata ayıklama bilgisi', 10 | 'cc' => 'Cc', 11 | 'to' => 'Alıcı', 12 | 'from' => 'Gönderen', 13 | 'header' => 'Üstbilgiler', 14 | 'resend_email_heading' => 'E-postayı yeniden gönder', 15 | 'resend_email_description' => 'Bu e-postayı yeniden göndermek istediğinize emin misiniz?', 16 | 'resend_email_success' => 'E-posta başarıyla gönderildi', 17 | 'resend_email_error' => 'E-posta gönderilemedi, dahili hata', 18 | 'email_log' => 'Gönderilen E-postalar', 19 | 'subject' => 'Konu', 20 | 'from_filter' => 'İlk tarih', 21 | 'to_filter' => 'Son tarih', 22 | 'navigation_label' => 'Gönderilen E-postalar', 23 | 'navigation_group' => 'Yönetim', 24 | 'bcc' => 'Bcc', 25 | 'insert_multiple_email_placelholder' => 'Bir veya daha fazla e-posta girin', 26 | 'update_and_resend_email_heading' => 'E-postayı güncelle ve yeniden gönder', 27 | 'attachments' => 'Ekler', 28 | 'download' => 'İndir', 29 | 'download_attachment_error' => 'Eki indirme başarısız!', 30 | 'add_attachments' => 'Dosya ekle', 31 | 'attachments_number' => ':count ek|:count ek', 32 | 'previous' => 'Öncesi', 33 | 'next' => 'Sonraki', 34 | 'have_attachments' => 'Ekleri var?', 35 | 'yes' => 'evet', 36 | 'no' => 'hayir', 37 | 'model_label' => 'Gönderilen E-postalar', 38 | ]; 39 | -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickDBCN/filament-email/59df11e1ca658db09c73cceabfb5c39bf6e2f15c/resources/views/.gitkeep -------------------------------------------------------------------------------- /resources/views/attachments.blade.php: -------------------------------------------------------------------------------- 1 | attachments; ?> 2 | 3 | @if(!empty($attachments)) 4 |
5 | @foreach($attachments as $attachment) 6 | 7 | 8 | {{ $attachment['name'] }} 9 | 10 | {{ ($this->downloadAction)(['path' => $attachment['path'], 'name' => $attachment['name'] , 'type' => $attachment['contentType']]) }} 11 | 12 | @endforeach 13 |
14 | @endif 15 | 16 | -------------------------------------------------------------------------------- /resources/views/html_view.blade.php: -------------------------------------------------------------------------------- 1 | 5 |
6 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /resources/views/raw_body.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | $content = $getRecord()?->raw_body; 3 | if (!empty($content)) { 4 | $attachmentsDisk = config('filament-email.attachments_disk', 'local'); 5 | try { 6 | if (\Illuminate\Support\Facades\Storage::disk($attachmentsDisk)->exists($content)) { 7 | $content = \Illuminate\Support\Facades\Storage::disk($attachmentsDisk)->get($content); 8 | } 9 | } catch (\Exception) {} 10 | } 11 | @endphp 12 | 13 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /screenshots/filters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickDBCN/filament-email/59df11e1ca658db09c73cceabfb5c39bf6e2f15c/screenshots/filters.png -------------------------------------------------------------------------------- /screenshots/logo-filament.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /screenshots/resend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickDBCN/filament-email/59df11e1ca658db09c73cceabfb5c39bf6e2f15c/screenshots/resend.png -------------------------------------------------------------------------------- /screenshots/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickDBCN/filament-email/59df11e1ca658db09c73cceabfb5c39bf6e2f15c/screenshots/table.png -------------------------------------------------------------------------------- /screenshots/update-and-resend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickDBCN/filament-email/59df11e1ca658db09c73cceabfb5c39bf6e2f15c/screenshots/update-and-resend.png -------------------------------------------------------------------------------- /screenshots/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RickDBCN/filament-email/59df11e1ca658db09c73cceabfb5c39bf6e2f15c/screenshots/view.png -------------------------------------------------------------------------------- /src/Commands/FilamentEmailCommand.php: -------------------------------------------------------------------------------- 1 | comment('All done'); 16 | 17 | return self::SUCCESS; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Facades/FilamentEmail.php: -------------------------------------------------------------------------------- 1 | hiddenLabel() 19 | ->icon('heroicon-o-arrow-right') 20 | ->outlined() 21 | ->size('sm') 22 | ->tooltip(__('filament-email::filament-email.next')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Filament/Resources/Actions/PreviousAction.php: -------------------------------------------------------------------------------- 1 | hiddenLabel() 19 | ->icon('heroicon-o-arrow-left') 20 | ->outlined() 21 | ->size('sm') 22 | ->tooltip(__('filament-email::filament-email.previous')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Filament/Resources/Concernes/CanPaginateViewRecord.php: -------------------------------------------------------------------------------- 1 | configureActionRecord($action); 16 | 17 | match (true) { 18 | $action instanceof PreviousAction => $this->configurePreviousAction($action), 19 | $action instanceof NextAction => $this->configureNextAction($action), 20 | default => parent::configureAction($action), 21 | }; 22 | } 23 | 24 | protected function configurePreviousAction(Action $action): void 25 | { 26 | if ($this->getPreviousRecord()) { 27 | $action->url(fn (): string => static::getResource()::getUrl('view', ['record' => $this->getPreviousRecord()])); 28 | } else { 29 | $action 30 | ->disabled() 31 | ->color('gray'); 32 | } 33 | } 34 | 35 | protected function configureNextAction(Action $action): void 36 | { 37 | if ($this->getNextRecord()) { 38 | $action->url(fn (): string => static::getResource()::getUrl('view', ['record' => $this->getNextRecord()])); 39 | } else { 40 | $action 41 | ->disabled() 42 | ->color('gray'); 43 | } 44 | } 45 | 46 | protected function getPreviousRecord(): ?Model 47 | { 48 | $query = $this 49 | ->getRecord() 50 | ->where('created_at', '<', $this->getRecord()->created_at) 51 | ->where('id', '<>', $this->getRecord()->id); 52 | 53 | $query = $this->addTenantToQuery($query); 54 | 55 | return $query->orderBy('created_at', 'desc') 56 | ->first(); 57 | } 58 | 59 | protected function getNextRecord(): ?Model 60 | { 61 | $query = $this 62 | ->getRecord() 63 | ->where('created_at', '>', $this->getRecord()->created_at) 64 | ->where('id', '<>', $this->getRecord()->id); 65 | 66 | $query = $this->addTenantToQuery($query); 67 | 68 | return $query->orderBy('created_at', 'asc') 69 | ->first(); 70 | } 71 | 72 | private function addTenantToQuery($query) 73 | { 74 | if (auth()->check() && Filament::getTenant()) { 75 | return $query->whereTeamId(Filament::getTenant()?->id); 76 | } 77 | 78 | return $query->whereTeamId(null); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Filament/Resources/EmailResource.php: -------------------------------------------------------------------------------- 1 | | null 55 | */ 56 | public static function getCluster(): ?string 57 | { 58 | return config('filament-email.resource.cluster'); 59 | } 60 | 61 | public static function getNavigationLabel(): string 62 | { 63 | return config('filament-email.label') ?? __('filament-email::filament-email.navigation_label'); 64 | } 65 | 66 | public static function getNavigationIcon(): string|Htmlable|null 67 | { 68 | return config('filament-email.resource.icon') ?? 'heroicon-o-envelope'; 69 | } 70 | 71 | public static function getNavigationGroup(): ?string 72 | { 73 | return config('filament-email.resource.group') ?? __('filament-email::filament-email.navigation_group'); 74 | } 75 | 76 | public static function getNavigationSort(): ?int 77 | { 78 | return config('filament-email.resource.sort') ?? parent::getNavigationSort(); 79 | } 80 | 81 | public static function getModel(): string 82 | { 83 | return config('filament-email.resource.model') ?? Email::class; 84 | } 85 | 86 | public static function hasTitleCaseModelLabel(): bool 87 | { 88 | return config('filament-email.resource.has_title_case_model_label', true); 89 | } 90 | 91 | public static function getModelLabel(): string 92 | { 93 | return __('filament-email::filament-email.model_label'); 94 | } 95 | 96 | public static function form(Schema $form): Schema 97 | { 98 | return $form 99 | ->schema([ 100 | Fieldset::make('Envelope') 101 | ->label('') 102 | ->schema([ 103 | TextInput::make('from') 104 | ->label(__('filament-email::filament-email.from')) 105 | ->columnSpan(2), 106 | TextInput::make('to') 107 | ->label(__('filament-email::filament-email.to')) 108 | ->columnSpan(2), 109 | TextInput::make('cc') 110 | ->label(__('filament-email::filament-email.cc')) 111 | ->columnSpan(2), 112 | TextInput::make('bcc') 113 | ->label(__('filament-email::filament-email.bcc')) 114 | ->columnSpan(2), 115 | TextInput::make('subject') 116 | ->label(__('filament-email::filament-email.subject')) 117 | ->columnSpan(3), 118 | DateTimePicker::make('created_at') 119 | ->format(config('filament-email.resource.datetime_format')) 120 | ->label(__('filament-email::filament-email.created_at')), 121 | ])->columns(4), 122 | Fieldset::make('attachments') 123 | ->hidden(fn (): bool => ! config('filament-email.store_attachments')) 124 | ->label(__('filament-email::filament-email.attachments')) 125 | ->schema([ 126 | View::make('filament-email::attachments') 127 | ->columnSpanFull(), 128 | ]), 129 | Tabs::make('Content')->tabs([ 130 | Tabs\Tab::make(__('filament-email::filament-email.html')) 131 | ->schema([ 132 | ViewField::make('html_body') 133 | ->label('') 134 | ->view('filament-email::html_view'), 135 | ]), 136 | Tabs\Tab::make(__('filament-email::filament-email.text')) 137 | ->schema([ 138 | Textarea::make('text_body') 139 | ->label('') 140 | ->rows(20), 141 | ]), 142 | Tabs\Tab::make(__('filament-email::filament-email.raw')) 143 | ->schema([ 144 | ViewField::make('raw_body') 145 | ->label('') 146 | ->view('filament-email::raw_body'), 147 | ]), 148 | Tabs\Tab::make(__('filament-email::filament-email.debug_info')) 149 | ->schema([ 150 | Textarea::make('sent_debug_info') 151 | ->label('') 152 | ->rows(20), 153 | ]), 154 | ])->columnSpan(2), 155 | ]); 156 | } 157 | 158 | public static function table(Table $table): Table 159 | { 160 | return $table 161 | ->defaultSort(config('filament-email.resource.default_sort_column'), config('filament-email.resource.default_sort_direction')) 162 | ->actions([ 163 | ViewEmailAction::make(), 164 | ResendEmailAction::make(), 165 | AdvancedResendEmailAction::make(), 166 | ]) 167 | ->columns([ 168 | TextColumn::make('from') 169 | ->prefix(__('filament-email::filament-email.from').': ') 170 | ->suffix(fn (Email $record): string => ! empty($record->attachments) ? ' ('.trans_choice('filament-email::filament-email.attachments_number', count($record->attachments)).')' : '') 171 | ->label(__('filament-email::filament-email.header')) 172 | ->description(fn (Email $record): string => Str::limit(__('filament-email::filament-email.to').': '.$record->to, 40)) 173 | ->searchable(), 174 | TextColumn::make('subject') 175 | ->label(__('filament-email::filament-email.subject')) 176 | ->limit(50), 177 | TextColumn::make('created_at') 178 | ->label(__('filament-email::filament-email.sent_at')) 179 | ->dateTime(config('filament-email.resource.datetime_format')) 180 | ->sortable(), 181 | 182 | ]) 183 | ->groupedBulkActions([ 184 | ResendEmailBulkAction::make(), 185 | DeleteBulkAction::make() 186 | ->requiresConfirmation(), 187 | ]) 188 | ->persistFiltersInSession() 189 | ->filters(self::getFilters()) 190 | ->filtersFormWidth(Width::ExtraLarge) 191 | ->paginationPageOptions(function (Table $table) { 192 | $options = config('filament-email.pagination_page_options'); 193 | 194 | return ! empty($options) && is_array($options) ? $options : $table->getPaginationPageOptions(); 195 | }); 196 | } 197 | 198 | private static function getFilters() 199 | { 200 | return [ 201 | Filter::make('headers-filter') 202 | ->schema([ 203 | TextInput::make('from') 204 | ->label(__('filament-email::filament-email.from')) 205 | ->email(), 206 | TextInput::make('to') 207 | ->label(__('filament-email::filament-email.to')) 208 | ->email(), 209 | TextInput::make('cc') 210 | ->label(__('filament-email::filament-email.cc')) 211 | ->email(), 212 | TextInput::make('bcc') 213 | ->label(__('filament-email::filament-email.bcc')) 214 | ->email(), 215 | DateRangePicker::make('created_at') 216 | ->label(__('filament-email::filament-email.sent_at')), 217 | Select::make('attachments') 218 | ->label(__('filament-email::filament-email.have_attachments')) 219 | ->options([ 220 | 'no' => ucfirst(__('filament-email::filament-email.no')), 221 | 'yes' => ucfirst(__('filament-email::filament-email.yes')), 222 | ]), 223 | ]) 224 | ->columns(2) 225 | ->indicateUsing(function (array $data): array { 226 | $indicators = []; 227 | foreach ($data as $field => $value) { 228 | if ($data[$field] ?? null) { 229 | if ($field === 'attachments') { 230 | $indicators[$field] = __('filament-email::filament-email.'.$field).': '.($value === 'yes' ? __('filament-email::filament-email.yes') : __('filament-email::filament-email.no')); 231 | } else { 232 | $indicators[$field] = __('filament-email::filament-email.'.$field).": $value"; 233 | } 234 | } 235 | } 236 | 237 | return $indicators; 238 | }) 239 | ->query(function (Builder $query, array $data): Builder { 240 | return $query 241 | ->when( 242 | $data['from'], 243 | fn (Builder $query, $value): Builder => $query->where('from', 'like', "%$value%"), 244 | ) 245 | ->when( 246 | $data['to'], 247 | fn (Builder $query, $value): Builder => $query->where('to', 'like', "%$value%"), 248 | ) 249 | ->when( 250 | $data['cc'], 251 | fn (Builder $query, $value): Builder => $query->where('cc', 'like', "%$value%"), 252 | ) 253 | ->when( 254 | $data['bcc'], 255 | fn (Builder $query, $value): Builder => $query->where('bcc', 'like', "%$value%"), 256 | ) 257 | ->when( 258 | $data['attachments'], 259 | // JSON_LENGTH 260 | fn (Builder $query, $value): Builder => $query->where(DB::raw('JSON_LENGTH(attachments)'), $value === 'yes' ? '>' : '=', 0), 261 | ) 262 | ->when( 263 | $data['created_at'], 264 | function (Builder $query, $value): Builder { 265 | [$start, $end] = explode(' - ', $value); 266 | $start = str_replace('/', '-', $start); 267 | $end = str_replace('/', '-', $end); 268 | 269 | return $query->whereBetween('created_at', [ 270 | Carbon::createFromTimestamp(strtotime($start)) 271 | ->format('Y-m-d'), 272 | Carbon::createFromTimestamp(strtotime($end)) 273 | ->format('Y-m-d'), 274 | ]); 275 | } 276 | ); 277 | }), 278 | ]; 279 | } 280 | 281 | public static function getPages(): array 282 | { 283 | return [ 284 | 'index' => ListEmails::route('/'), 285 | 'view' => ViewEmail::route('/{record}'), 286 | ]; 287 | } 288 | 289 | public static function canAccess(): bool 290 | { 291 | $user = auth()->user() ?? null; 292 | $roles = config('filament-email.can_access.role', []); 293 | 294 | if (! is_null($user) && method_exists($user, 'hasRole') && ! empty($roles)) { 295 | return $user->hasRole($roles); 296 | } 297 | 298 | return true; 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/Filament/Resources/EmailResource/Actions/AdvancedResendEmailAction.php: -------------------------------------------------------------------------------- 1 | label(false) 32 | ->icon('heroicon-o-envelope-open') 33 | ->iconSize(IconSize::Medium) 34 | ->tooltip(__('filament-email::filament-email.update_and_resend_email_heading')) 35 | ->modalHeading(__('filament-email::filament-email.update_and_resend_email_heading')) 36 | ->modalWidth('2xl') 37 | ->schema([ 38 | TagsInput::make('to') 39 | ->label(__('filament-email::filament-email.to')) 40 | ->placeholder(__('filament-email::filament-email.insert_multiple_email_placelholder')) 41 | ->nestedRecursiveRules([ 42 | 'email', 43 | ]) 44 | ->default(fn ($record): array => ! empty($record->to) ? explode(',', $record->to) : []) 45 | ->required(), 46 | TagsInput::make('cc') 47 | ->label(__('filament-email::filament-email.cc')) 48 | ->placeholder(__('filament-email::filament-email.insert_multiple_email_placelholder')) 49 | ->nestedRecursiveRules([ 50 | 'email', 51 | ]) 52 | ->default(fn ($record): array => ! empty($record->cc) ? explode(',', $record->cc) : []), 53 | TagsInput::make('bcc') 54 | ->label(__('filament-email::filament-email.bcc')) 55 | ->placeholder(__('filament-email::filament-email.insert_multiple_email_placelholder')) 56 | ->nestedRecursiveRules([ 57 | 'email', 58 | ]) 59 | ->default(fn ($record): array => ! empty($record->bcc) ? explode(',', $record->bcc) : []), 60 | Radio::make('attachments') 61 | ->label(__('filament-email::filament-email.add_attachments')) 62 | ->boolean() 63 | ->inline() 64 | ->inlineLabel(false) 65 | ->disabled(fn ($record): bool => empty($record->attachments)) 66 | ->default(fn ($record): bool => ! empty($record->attachments)) 67 | ->required(), 68 | ]) 69 | ->action(function (Email $record, array $data) { 70 | try { 71 | Mail::to($data['to']) 72 | ->cc($data['cc']) 73 | ->bcc($data['bcc']) 74 | ->send(new ResendMail($record, $data['attachments'] ?? false)); 75 | Notification::make() 76 | ->title(__('filament-email::filament-email.resend_email_success')) 77 | ->success() 78 | ->duration(5000) 79 | ->send(); 80 | } catch (\Exception $e) { 81 | Log::error($e->getMessage()); 82 | Notification::make() 83 | ->title(__('filament-email::filament-email.resend_email_error')) 84 | ->danger() 85 | ->duration(5000) 86 | ->send(); 87 | } 88 | }); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Filament/Resources/EmailResource/Actions/ResendEmailAction.php: -------------------------------------------------------------------------------- 1 | label(false) 29 | ->icon('heroicon-o-arrow-path') 30 | ->iconSize(IconSize::Medium) 31 | ->tooltip(__('filament-email::filament-email.resend_email_heading')) 32 | ->modalHeading(__('filament-email::filament-email.resend_email_heading')) 33 | ->modalDescription(__('filament-email::filament-email.resend_email_description')) 34 | ->modalIconColor('warning') 35 | ->requiresConfirmation() 36 | ->action(function ($record) { 37 | try { 38 | Mail::to($record->to) 39 | ->cc($record->cc) 40 | ->bcc($record->bcc) 41 | ->send(new ResendMail($record)); 42 | Notification::make() 43 | ->title(__('filament-email::filament-email.resend_email_success')) 44 | ->success() 45 | ->duration(5000) 46 | ->send(); 47 | } catch (\Exception $e) { 48 | Log::error($e->getMessage()); 49 | Notification::make() 50 | ->title(__('filament-email::filament-email.resend_email_error')) 51 | ->danger() 52 | ->duration(5000) 53 | ->send(); 54 | } 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Filament/Resources/EmailResource/Actions/ResendEmailBulkAction.php: -------------------------------------------------------------------------------- 1 | label(__('filament-email::filament-email.resend_email_heading')) 30 | ->icon('heroicon-o-arrow-path') 31 | ->color('primary') 32 | ->iconSize(IconSize::Medium) 33 | ->tooltip(__('filament-email::filament-email.resend_email_heading')) 34 | ->requiresConfirmation() 35 | ->modalHeading(__('filament-email::filament-email.resend_email_heading')) 36 | ->modalDescription(__('filament-email::filament-email.resend_email_description')) 37 | ->modalIconColor('warning') 38 | ->deselectRecordsAfterCompletion() 39 | ->action(function (Collection $records) { 40 | try { 41 | foreach ($records as $record) { 42 | Mail::to($record->to) 43 | ->cc($record->cc) 44 | ->bcc($record->bcc) 45 | ->send(new ResendMail($record)); 46 | } 47 | Notification::make() 48 | ->title(__('filament-email::filament-email.resend_email_success')) 49 | ->success() 50 | ->duration(5000) 51 | ->send(); 52 | } catch (\Exception $e) { 53 | Log::error($e->getMessage()); 54 | Notification::make() 55 | ->title(__('filament-email::filament-email.resend_email_error')) 56 | ->danger() 57 | ->duration(5000) 58 | ->send(); 59 | } 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Filament/Resources/EmailResource/Actions/ViewEmailAction.php: -------------------------------------------------------------------------------- 1 | label(false) 26 | ->icon('heroicon-o-eye') 27 | ->iconSize(IconSize::Medium) 28 | ->modalFooterActions( 29 | fn ($action): array => [ 30 | $action->getModalCancelAction(), 31 | ]) 32 | ->fillForm(function ($record) { 33 | $body = $record->html_body; 34 | 35 | return [ 36 | 'html_body' => $body, 37 | ]; 38 | }) 39 | ->schema([ 40 | ViewField::make('html_body') 41 | ->hiddenLabel() 42 | ->view('filament-email::html_view'), 43 | ]); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Filament/Resources/EmailResource/Pages/ListEmails.php: -------------------------------------------------------------------------------- 1 | getTableSearch())) { 22 | return $query->filter(['search' => $searchQuery]); 23 | } 24 | 25 | return $query; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Filament/Resources/EmailResource/Pages/ViewEmail.php: -------------------------------------------------------------------------------- 1 | label(__('filament-email::filament-email.download')) 31 | ->requiresConfirmation() 32 | ->icon('heroicon-c-arrow-down-tray') 33 | ->size(Size::ExtraSmall) 34 | ->action(function (array $arguments) { 35 | $fileExists = Storage::disk(config('filament-email.attachments_disk'))->exists($arguments['path']); 36 | if ($fileExists) { 37 | return Storage::disk(config('filament-email.attachments_disk'))->download($arguments['path'], $arguments['name']); 38 | } else { 39 | Notification::make() 40 | ->title(__('filament-email::filament-email.download_attachment_error')) 41 | ->danger() 42 | ->duration(5000) 43 | ->send(); 44 | } 45 | }); 46 | } 47 | 48 | protected function getHeaderActions(): array 49 | { 50 | return [ 51 | PreviousAction::make(), 52 | NextAction::make(), 53 | ]; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/FilamentEmail.php: -------------------------------------------------------------------------------- 1 | resources([ 19 | config('filament-email.resource.class', EmailResource::class), 20 | ]); 21 | } 22 | 23 | public function boot(Panel $panel): void {} 24 | 25 | public static function make(): static 26 | { 27 | return app(static::class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/FilamentEmailServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('filament-email') 28 | ->hasConfigFile('filament-email') 29 | ->hasTranslations() 30 | ->hasViews() 31 | ->hasMigrations([ 32 | 'create_filament_email_table', 33 | 'add_attachments_field_to_filament_email_log_table', 34 | 'add_team_id_field_to_filament_email_log_table', 35 | ]); 36 | 37 | $this->app->register(EmailMessageServiceProvider::class); 38 | } 39 | 40 | public function bootingPackage() 41 | { 42 | FilamentView::registerRenderHook( 43 | TablesRenderHook::TOOLBAR_SEARCH_BEFORE, 44 | fn (): string => Blade::render(''), 45 | scopes: ListEmails::class 46 | ); 47 | 48 | $pruneEnabled = config('filament-email.prune_enabled') ?? false; 49 | 50 | if ($pruneEnabled) { 51 | $this->callAfterResolving(Schedule::class, function (Schedule $schedule) { 52 | $runCrontab = config('filament-email.prune_crontab', '0 0 * * *'); 53 | $modelClass = config('filament-email.resource.model') ?? Email::class; 54 | $class = get_class(new $modelClass); 55 | if (class_exists($class)) { 56 | $schedule->command('model:prune --model="'.$class.'"') 57 | ->cron($runCrontab); 58 | } 59 | }); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Listeners/FilamentEmailLogger.php: -------------------------------------------------------------------------------- 1 | message; 28 | $rawMessage = $event->sent->getSymfonySentMessage(); 29 | $attachmentsDisk = config('filament-email.attachments_disk', 'local'); 30 | $storeAttachments = config('filament-email.store_attachments', true); 31 | 32 | $model = config('filament-email.resource.model') ?? Email::class; 33 | 34 | $attachments = []; 35 | $savePath = 'filament-email-log'.DIRECTORY_SEPARATOR. 36 | date('YmdHis').'_'.Str::random(5).DIRECTORY_SEPARATOR; 37 | 38 | if ($storeAttachments) { 39 | 40 | foreach ($email->getAttachments() as $attachment) { 41 | $filePath = $savePath.Str::random(5).'_'.$attachment->getFilename(); 42 | Storage::disk($attachmentsDisk) 43 | ->put($filePath, $attachment->getBody()); 44 | $attachments[] = [ 45 | 'name' => $attachment->getFilename(), 46 | 'contentType' => $attachment->getContentType(), 47 | 'path' => $filePath, 48 | ]; 49 | } 50 | 51 | $savePathRaw = $savePath.$rawMessage->getMessageId().'.eml'; 52 | 53 | Storage::disk($attachmentsDisk) 54 | ->put($savePathRaw, $rawMessage->getMessage()->toString()); 55 | 56 | } else { 57 | $savePathRaw = null; 58 | } 59 | 60 | $model::create([ 61 | 'team_id' => Filament::getTenant()?->id ?? null, 62 | 'from' => $this->recipientsToString($email->getFrom()), 63 | 'to' => $this->recipientsToString($email->getTo()), 64 | 'cc' => $this->recipientsToString($email->getCc()), 65 | 'bcc' => $this->recipientsToString($email->getBcc()), 66 | 'subject' => $email->getSubject(), 67 | 'html_body' => $email->getHtmlBody(), 68 | 'text_body' => $email->getTextBody(), 69 | 'raw_body' => $savePathRaw, 70 | 'sent_debug_info' => $rawMessage->getDebug(), 71 | 'attachments' => ! empty($attachments) ? $attachments : null, 72 | ]); 73 | 74 | } 75 | 76 | private function recipientsToString(array $recipients): string 77 | { 78 | return implode( 79 | ',', 80 | array_map(function ($email) { 81 | return "{$email->getAddress()}".($email->getName() ? " <{$email->getName()}>" : ''); 82 | }, $recipients) 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Mail/ResendMail.php: -------------------------------------------------------------------------------- 1 | email = $email; 23 | $this->addAttachments = $attachments; 24 | } 25 | 26 | public function envelope(): Envelope 27 | { 28 | return new Envelope( 29 | subject: $this->email->subject, 30 | ); 31 | } 32 | 33 | public function content(): Content 34 | { 35 | return new Content( 36 | htmlString: $this->email->html_body ?? $this->email->text_body, 37 | ); 38 | } 39 | 40 | public function attachments(): array 41 | { 42 | $attachments = []; 43 | $storageDisk = config('filament-email.attachments_disk', 'local'); 44 | 45 | if ($this->addAttachments) { 46 | $modelAttachments = $this->email->attachments; 47 | if (! empty($modelAttachments)) { 48 | foreach ($modelAttachments as $attachment) { 49 | $attachments[] = Attachment::fromStorageDisk($storageDisk, $attachment['path']) 50 | ->as($attachment['name']); 51 | } 52 | } 53 | } 54 | 55 | return $attachments; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Models/Email.php: -------------------------------------------------------------------------------- 1 | 'json', 44 | ]; 45 | 46 | public function team(): BelongsTo 47 | { 48 | return $this->belongsTo(config('filament-email.tenant_model'), 'team_id', 'id'); 49 | } 50 | 51 | public static function boot() 52 | { 53 | parent::boot(); 54 | 55 | self::deleting(function ($record) { 56 | $folderPath = ''; 57 | $storageDisk = config('filament-email.attachments_disk', 'local'); 58 | if (! empty($record->attachments)) { 59 | foreach ($record->attachments as $attachment) { 60 | $filePath = Storage::disk($storageDisk)->path($attachment['path']); 61 | if (empty($folderPath)) { 62 | $parts = explode(DIRECTORY_SEPARATOR, $attachment['path']); 63 | array_pop($parts); 64 | $folderPath = implode(DIRECTORY_SEPARATOR, $parts); 65 | } 66 | if (Storage::disk($storageDisk)->exists($attachment['path'])) { 67 | Storage::disk($storageDisk)->delete($attachment['path']); 68 | } 69 | } 70 | } 71 | if (! empty($record->raw_body) && count(explode(DIRECTORY_SEPARATOR, $record->raw_body)) === 3) { 72 | if (Storage::disk($storageDisk)->exists($record->raw_body)) { 73 | if (empty($folderPath)) { 74 | $parts = explode(DIRECTORY_SEPARATOR, $record->raw_body); 75 | array_pop($parts); 76 | $folderPath = implode(DIRECTORY_SEPARATOR, $parts); 77 | } 78 | Storage::disk($storageDisk)->delete($record->raw_body); 79 | } 80 | } 81 | if (! empty($folderPath) && Storage::disk($storageDisk)->directoryExists($folderPath)) { 82 | Storage::disk($storageDisk)->deleteDirectory($folderPath); 83 | } 84 | }); 85 | } 86 | 87 | public function prunable() 88 | { 89 | return static::where('created_at', '<=', now()->subDays(config('filament-email.keep_email_for_days', 60))); 90 | } 91 | 92 | private function getTableColumns() 93 | { 94 | return $this->getConnection()->getSchemaBuilder()->getColumnListing($this->getTable()); 95 | } 96 | 97 | private function getSearchableFields() 98 | { 99 | $columns = $this->getTableColumns(); 100 | $fields = config('filament-email.resource.table_search_fields', $this->defaultSearchFields); 101 | 102 | return Arr::where($fields, function ($value) use ($columns) { 103 | return in_array($value, $columns); 104 | }); 105 | } 106 | 107 | public function scopeFilter($query, array $filters) 108 | { 109 | $query->when($filters['search'] ?? null, function ($query, $search) { 110 | foreach ($this->getSearchableFields() as $key => $field) { 111 | $query->{$key > 0 ? 'orWhere' : 'where'}($field, 'LIKE', "%{$search}%"); 112 | } 113 | }); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Providers/EmailMessageServiceProvider.php: -------------------------------------------------------------------------------- 1 |