├── dist ├── css │ └── tool.css ├── mix-manifest.json └── js │ └── tool.js ├── resources ├── sass │ └── tool.scss ├── js │ ├── components │ │ └── Tool.vue │ └── tool.js └── views │ └── navigation.blade.php ├── tool.png ├── webpack.mix.js ├── .gitignore ├── routes └── api.php ├── src ├── NovaMailManager.php ├── Filters │ ├── SentOnOrBeforeFilter.php │ ├── SentOnOrAfterFilter.php │ ├── MailableFilter.php │ ├── StatusFilter.php │ └── QueuedFilter.php ├── Http │ └── Middleware │ │ └── Authorize.php ├── Metrics │ ├── EmailsByMailable.php │ └── EmailsPerDay.php ├── Actions │ └── ResendMail.php ├── NovaMailManagerServiceProvider.php └── Resources │ └── NovaMailResource.php ├── composer.json ├── package.json ├── LICENSE.md └── README.md /dist/css/tool.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/sass/tool.scss: -------------------------------------------------------------------------------- 1 | // Nova Tool CSS 2 | -------------------------------------------------------------------------------- /tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binarybuilds/nova-mail-manager/HEAD/tool.png -------------------------------------------------------------------------------- /dist/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/tool.js": "/js/tool.js", 3 | "/css/tool.css": "/css/tool.css" 4 | } -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | mix.setPublicPath('dist') 4 | .js('resources/js/tool.js', 'js') 5 | .sass('resources/sass/tool.scss', 'css') 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | /node_modules 4 | package-lock.json 5 | composer.phar 6 | composer.lock 7 | phpunit.xml 8 | .phpunit.result.cache 9 | .DS_Store 10 | Thumbs.db 11 | -------------------------------------------------------------------------------- /resources/js/components/Tool.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /resources/js/tool.js: -------------------------------------------------------------------------------- 1 | Nova.booting((Vue, router, store) => { 2 | router.addRoutes([ 3 | { 4 | name: 'nova-mail-manager', 5 | path: '/nova-mail-manager', 6 | component: require('./components/Tool'), 7 | }, 8 | ]) 9 | }) 10 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | {{ __('Mail Manager') }} 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/NovaMailManager.php: -------------------------------------------------------------------------------- 1 | =7.1.0", 15 | "binarybuilds/laravel-mail-manager" : "^1.0" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "BinaryBuilds\\NovaMailManager\\": "src/" 20 | } 21 | }, 22 | "config": { 23 | "sort-packages": true 24 | }, 25 | "minimum-stability": "dev", 26 | "prefer-stable": true 27 | } 28 | -------------------------------------------------------------------------------- /src/Filters/SentOnOrBeforeFilter.php: -------------------------------------------------------------------------------- 1 | where('created_at', '<=', $value ); 24 | } 25 | 26 | /** 27 | * @return array|null|string 28 | */ 29 | public function name() 30 | { 31 | return __('Filter By Date(On Or Before)'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Filters/SentOnOrAfterFilter.php: -------------------------------------------------------------------------------- 1 | where('created_at', '>=', $value ); 25 | } 26 | 27 | /** 28 | * @return array|null|string 29 | */ 30 | public function name() 31 | { 32 | return __('Filter By Date(On Or After)'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authorize.php: -------------------------------------------------------------------------------- 1 | first([$this, 'matchesTool']); 20 | 21 | return optional($tool)->authorize($request) ? $next($request) : abort(403); 22 | } 23 | 24 | /** 25 | * Determine whether this tool belongs to the package. 26 | * 27 | * @param \Laravel\Nova\Tool $tool 28 | * @return bool 29 | */ 30 | public function matchesTool($tool) 31 | { 32 | return $tool instanceof NovaMailManager; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "cross-env": "^5.0.0", 14 | "laravel-mix": "^1.0" 15 | }, 16 | "dependencies": { 17 | "vue": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Srinath Reddy Dudi 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. -------------------------------------------------------------------------------- /src/Metrics/EmailsByMailable.php: -------------------------------------------------------------------------------- 1 | count($request, MailManagerMail::class, 'mailable_name'); 22 | } 23 | 24 | /** 25 | * Determine for how many minutes the metric should be cached. 26 | * 27 | * @return \DateTimeInterface|\DateInterval|float|int 28 | */ 29 | public function cacheFor() 30 | { 31 | // return now()->addMinutes(5); 32 | } 33 | 34 | /** 35 | * Get the URI key for the metric. 36 | * 37 | * @return string 38 | */ 39 | public function uriKey() 40 | { 41 | return 'emails-by-mailable'; 42 | } 43 | 44 | /** 45 | * @return array|null|string 46 | */ 47 | public function name() 48 | { 49 | return __('Emails By Mailable'); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Filters/MailableFilter.php: -------------------------------------------------------------------------------- 1 | where('mailable_name', $value); 29 | } 30 | 31 | /** 32 | * Get the filter's available options. 33 | * 34 | * @param \Illuminate\Http\Request $request 35 | * @return array 36 | */ 37 | public function options(Request $request) 38 | { 39 | return MailManagerMail::groupBy('mailable_name')->pluck('mailable_name', 'mailable_name')->toArray(); 40 | } 41 | 42 | /** 43 | * @return array|null|string 44 | */ 45 | public function name() 46 | { 47 | return __('Filter By Mailable'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Actions/ResendMail.php: -------------------------------------------------------------------------------- 1 | count() . ' '. __('Mail(s) retried successfully')); 34 | } 35 | 36 | public function name() 37 | { 38 | return __('Resend Mail'); 39 | } 40 | 41 | /** 42 | * Get the fields available on the action. 43 | * 44 | * @return array 45 | */ 46 | public function fields() 47 | { 48 | return []; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Filters/StatusFilter.php: -------------------------------------------------------------------------------- 1 | where('is_sent', true ); 29 | } elseif ( $value === 'unsent') { 30 | return $query->where('is_sent', false ); 31 | } 32 | 33 | return $query; 34 | } 35 | 36 | /** 37 | * Get the filter's available options. 38 | * 39 | * @param \Illuminate\Http\Request $request 40 | * @return array 41 | */ 42 | public function options(Request $request) 43 | { 44 | return [ 45 | 'Sent Emails' => 'sent', 46 | 'Unsent Emails' => 'unsent' 47 | ]; 48 | } 49 | 50 | /** 51 | * @return array|null|string 52 | */ 53 | public function name() 54 | { 55 | return __('Filter By Status'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Filters/QueuedFilter.php: -------------------------------------------------------------------------------- 1 | where('is_queued', true ); 29 | } elseif ( $value === 'not-queued') { 30 | return $query->where('is_queued', false ); 31 | } 32 | 33 | return $query; 34 | } 35 | 36 | /** 37 | * Get the filter's available options. 38 | * 39 | * @param \Illuminate\Http\Request $request 40 | * @return array 41 | */ 42 | public function options(Request $request) 43 | { 44 | return [ 45 | 'Queued Emails' => 'queued', 46 | 'Not Queued Emails' => 'not-queued' 47 | ]; 48 | } 49 | 50 | /** 51 | * @return array|null|string 52 | */ 53 | public function name() 54 | { 55 | return __('Filter By Queued Status'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Metrics/EmailsPerDay.php: -------------------------------------------------------------------------------- 1 | countByDays($request, MailManagerMail::class)->showLatestValue(); 20 | } 21 | 22 | /** 23 | * Get the ranges available for the metric. 24 | * 25 | * @return array 26 | */ 27 | public function ranges() 28 | { 29 | return [ 30 | 7 => '1 '.__('Week'), 31 | 14 => '2 '.__('Weeks'), 32 | 30 => '30 '.__('Days'), 33 | 60 => '60 '.__('Days'), 34 | 90 => '90 '.__('Days'), 35 | ]; 36 | } 37 | 38 | /** 39 | * Determine for how many minutes the metric should be cached. 40 | * 41 | * @return \DateTimeInterface|\DateInterval|float|int 42 | */ 43 | public function cacheFor() 44 | { 45 | // return now()->addMinutes(5); 46 | } 47 | 48 | /** 49 | * Get the URI key for the metric. 50 | * 51 | * @return string 52 | */ 53 | public function uriKey() 54 | { 55 | return 'emails-per-day'; 56 | } 57 | 58 | /** 59 | * @return array|null|string 60 | */ 61 | public function name() 62 | { 63 | return __('Emails Per Day'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/NovaMailManagerServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../resources/views', 'nova-mail-manager'); 22 | 23 | $this->app->register( \BinaryBuilds\LaravelMailManager\LaravelMailManagerServiceProvider::class ); 24 | 25 | $this->app->booted(function () { 26 | $this->routes(); 27 | }); 28 | 29 | Nova::resources([ 30 | NovaMailResource::class 31 | ]); 32 | 33 | Nova::serving(function (ServingNova $event) { 34 | // 35 | }); 36 | } 37 | 38 | /** 39 | * Register the tool's routes. 40 | * 41 | * @return void 42 | */ 43 | protected function routes() 44 | { 45 | if ($this->app->routesAreCached()) { 46 | return; 47 | } 48 | 49 | Route::middleware(['nova', Authorize::class]) 50 | ->prefix('nova-vendor/nova-mail-manager') 51 | ->group(__DIR__.'/../routes/api.php'); 52 | } 53 | 54 | /** 55 | * Register any application services. 56 | * 57 | * @return void 58 | */ 59 | public function register() 60 | { 61 | // 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Resources/NovaMailResource.php: -------------------------------------------------------------------------------- 1 | onlyOnDetail(), 57 | 58 | Text::make(__('Mailable'), 'mailable_name'), 59 | 60 | Text::make(__('Subject'), 'subject'), 61 | 62 | Text::make(__('Recipients'), function (){ 63 | return implode(', ', $this->recipients); 64 | }), 65 | 66 | Boolean::make(__('Is Queued'), 'is_queued'), 67 | 68 | Boolean::make(__('Is Sent'), 'is_sent'), 69 | 70 | Number::make(__('Tries'), 'tries'), 71 | ]; 72 | } 73 | 74 | public function actions( Request $request ) 75 | { 76 | return [ 77 | new ResendMail 78 | ]; 79 | } 80 | 81 | public function filters( Request $request ) 82 | { 83 | return [ 84 | new MailableFilter, 85 | new StatusFilter, 86 | new QueuedFilter, 87 | new SentOnOrAfterFilter, 88 | new SentOnOrBeforeFilter 89 | ]; 90 | } 91 | 92 | public function cards( Request $request ) 93 | { 94 | return [ 95 | new EmailsPerDay, 96 | new EmailsByMailable 97 | ]; 98 | } 99 | 100 | public static function label() 101 | { 102 | return __('Mails'); 103 | } 104 | 105 | public static function singularLabel() 106 | { 107 | return __('Mail'); 108 | } 109 | 110 | public static function uriKey() 111 | { 112 | return 'nova-mail-manager'; 113 | } 114 | 115 | public static function authorizedToCreate(Request $request) 116 | { 117 | return false; 118 | } 119 | 120 | public function authorizedToUpdate( Request $request ) 121 | { 122 | return false; 123 | } 124 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nova Mail Manager 2 | ------------------------------------------------ 3 | A [Laravel Nova](https://nova.laravel.com/) tool to easily resend your system generated emails without going through entire application flow. 4 | 5 | > Internally this package uses [Laravel Mail Manager](https://github.com/binarybuilds/laravel-mail-manager) to record and resend emails. 6 | 7 | ![Tool Screenshot](tool.png "Tool Screenshot") 8 | 9 | ### Installation 10 | 11 | This package can be installed using composer. 12 | ```bash 13 | composer require binarybuilds/nova-mail-manager 14 | ``` 15 | Next you must register the package's service provider by adding the below line to `providers` array inside 16 | `config/app.php` file. 17 | 18 | ```php 19 | 'providers' => [ 20 | // 21 | \BinaryBuilds\NovaMailManager\NovaMailManagerServiceProvider::class 22 | ] 23 | ``` 24 | 25 | #### Conflicts With Laravel Telescope 26 | The dependent package([Laravel Mail Manager](https://github.com/binarybuilds/laravel-mail-manager)) currently conflicts 27 | with laravel telescope. If you are using laravel telescope in your application, 28 | Make sure you register the nova mail manager package's service provider after the telescope service provider is registered. 29 | 30 | If you are registering telescope using `config/app.php` file, Then add the service provider after the telescope service 31 | provider as shown below. 32 | 33 | ```php 34 | 'providers' => [ 35 | // 36 | App\Providers\TelescopeServiceProvider::class, 37 | \BinaryBuilds\NovaMailManager\NovaMailManagerServiceProvider::class 38 | ] 39 | ``` 40 | 41 | If you are registering telescope manually using `AppServiceProvider.php` file or any other service provider, Then register this package service 42 | provider after the telescope service provider is registered as shown below. 43 | 44 | ```php 45 | $this->app->register(TelescopeServiceProvider::class); 46 | $this->app->register(NovaMailManagerServiceProvider::class); 47 | ``` 48 | 49 | Next, Publish the package configuration file by running 50 | ```bash 51 | php artisan vendor:publish --tag=laravel-mail-manager-config 52 | ``` 53 | Run migrations to create the table required to store the emails. 54 | ``` 55 | php artisan migrate 56 | ``` 57 | This will create a table `mail_manager_mails`. You can configure the table name using the published configuration file 58 | located in `config/mail_manager.php` 59 | 60 | Next, Inside your `App/Providers/NovaServiceProvider.php` file, register the tool inside the `tools` method as shown below. 61 | 62 | ```php 63 | public function tools() 64 | { 65 | return [ 66 | // 67 | new \BinaryBuilds\NovaMailManager\NovaMailManager() 68 | ]; 69 | } 70 | ``` 71 | 72 | ### Usage 73 | After installation, You will see `Mail Manager` in the nova navigation menu. Clicking on the link will take you to the 74 | tool page where you can view and resend mails. 75 | 76 | >The dependent package([Laravel Mail Manager](https://github.com/binarybuilds/laravel-mail-manager)) has few commands 77 | which you can use to manage mails. Checkout the readme file of [Laravel Mail Manager](https://github.com/binarybuilds/laravel-mail-manager) 78 | package for details. 79 | 80 | ## Contributing 81 | 82 | Thank you for considering contributing to Nova mail manager! Please create a pull request with your contributions with 83 | detailed explanation of the changes you are proposing. 84 | 85 | ## License 86 | 87 | This package is open-sourced software licensed under the [MIT license](LICENSE.md). 88 | -------------------------------------------------------------------------------- /dist/js/tool.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){n(1),e.exports=n(11)},function(e,t,n){"use strict";Nova.booting(function(e,t,r){t.addRoutes([{name:"nova-mail-manager",path:"/nova-mail-manager",component:n(2)}])})},function(e,t,n){var r=n(8)(n(9),n(10),!1,function(e){n(3)},null,null);e.exports=r.exports},function(e,t,n){var r=n(4);"string"==typeof r&&(r=[[e.i,r,""]]),r.locals&&(e.exports=r.locals);n(6)("70658fb2",r,!0,{})},function(e,t,n){(e.exports=n(5)(!1)).push([e.i,"",""])},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var o=(s=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(s))))+" */"),i=r.sources.map(function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"});return[n].concat(i).concat([o]).join("\n")}var s;return[n].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},o=0;on.parts.length&&(r.parts.length=n.parts.length)}else{var s=[];for(o=0;o