├── .gitignore ├── .travis.yml ├── README.md ├── Test ├── Events │ └── WildcardEventTest.php ├── Fakes │ └── FlexibleEvent.php └── Unit │ └── Service │ └── EmailContentTest.php ├── composer.json ├── database └── migrations │ ├── 2019_07_10_000000_create_email_events_table.php │ ├── 2019_10_06_000000_create_cc_bcc_fields.php │ └── 2019_10_07_000000_allow_multiple_recipients.php ├── dist ├── js │ └── boot.js └── mix-manifest.json ├── package.json ├── phpunit.xml.dist ├── resources └── js │ ├── boot.js │ ├── components │ └── EmailField │ │ ├── DetailsField.vue │ │ ├── FormField.vue │ │ └── IndexField.vue │ └── functions │ └── FormatEmailList.js ├── routes └── api.php ├── src ├── DataObjects │ └── ClassInfo.php ├── Events │ └── WildcardEvent.php ├── Mail │ └── DynamicMail.php ├── MailOnEventServiceProvider.php ├── Models │ └── EmailEvent.php ├── Nova │ ├── Fields │ │ └── EmailField.php │ └── Resources │ │ └── EmailEventsResource.php └── Service │ ├── EmailContent.php │ ├── EmailSender.php │ └── Files │ ├── ClassNameFinder.php │ └── EventFinder.php └── webpack.mix.js /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | package-lock.json 3 | composer.lock 4 | .phpunit.result.cache 5 | node_modules 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.1 5 | - 7.2 6 | - 7.3 7 | - 7.4snapshot 8 | 9 | before_script: 10 | - travis_retry composer self-update 11 | - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source 12 | 13 | script: 14 | - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover 15 | 16 | cache: 17 | directories: 18 | - $HOME/.composer/cache 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # E-mail on event [![Build Status](https://travis-ci.org/michielgerritsen/laravel-nova-mail-on-event.svg?branch=master)](https://travis-ci.org/michielgerritsen/laravel-nova-mail-on-event) 2 | 3 | This tool allows you to send e-mails from your Laravel Nova dashboard when an event happens in your application. This ensures that you can quickly respond to the needs of your business. 4 | 5 | ![Creation example](../images/create-example.png?raw=true) 6 | 7 | ## What is an event? 8 | 9 | An event can be just about anything: 10 | 11 | - A user just registered. 12 | - The user switched plans. 13 | - You get an API request. 14 | - etc. 15 | 16 | Laravel allows you to create event easily. Just run: 17 | 18 | ``` 19 | php artisan make:event MyCustomEvent 20 | ``` 21 | 22 | This will create a new class in `app/events`. Now you can throw the event: 23 | 24 | ``` 25 | event(new MyCustomEvent($user)); 26 | ``` 27 | 28 | ## What events are included? 29 | 30 | Only the events listed under `app_path('Events');`/`app/Events`. Need support for other events too? Create a pull request to add it. 31 | 32 | ## Installation 33 | 34 | Installing is simple. Just use Composer: 35 | 36 | ``` 37 | composer require michielgerritsen/laravel-nova-email-on-event 38 | ``` 39 | 40 | ## Can i use variables in my e-mails? 41 | 42 | Yes, you can. You can use all variables that are public available on the event. You can use them like this: 43 | 44 | ``` 45 | Hello {$user->name}, 46 | 47 | Order #{$order->id} just shipped to: 48 | 49 | {$order->shipment->address} 50 | ``` 51 | -------------------------------------------------------------------------------- /Test/Events/WildcardEventTest.php: -------------------------------------------------------------------------------- 1 | handle('eloquent.model.save', []); 36 | 37 | $this->assertNull($result); 38 | } 39 | 40 | public function testReturnsNullWhenThereAreNoRelevantEvents() 41 | { 42 | $eventFinderMock = $this->createMock(EventFinder::class); 43 | $eventFinderMock->expects($this->once())->method('hasEvent')->willReturn(false); 44 | 45 | /** @var WildcardEvent $instance */ 46 | $instance = app(WildcardEvent::class, [ 47 | 'eventFinder' => $eventFinderMock, 48 | ]); 49 | $result = $instance->handle('App\\Events\\CustomEvent', []); 50 | 51 | $this->assertNull($result); 52 | } 53 | 54 | public function testSendsEmailsWhenApplicable() 55 | { 56 | $eventFinderMock = $this->createMock(EventFinder::class); 57 | $eventFinderMock->expects($this->once())->method('hasEvent')->willReturn(true); 58 | 59 | $emailSenderMock = $this->createMock(EmailSender::class); 60 | $emailSenderMock->expects($this->once())->method('send'); 61 | 62 | $eventName = 'App\\Events\\CustomEvent'; 63 | $collection = collect([new EmailEvent()]); 64 | Cache::put('email-events.events.\\' . $eventName, $collection); 65 | 66 | /** @var WildcardEvent $instance */ 67 | $instance = app(WildcardEvent::class, [ 68 | 'eventFinder' => $eventFinderMock, 69 | 'emailSender' => $emailSenderMock, 70 | ]); 71 | 72 | $result = $instance->handle($eventName, [new FlexibleEvent]); 73 | $this->assertNull($result); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Test/Fakes/FlexibleEvent.php: -------------------------------------------------------------------------------- 1 | ['This is a {$type} for John Doe: Hello', 'This is a message for John Doe: Hello'], 32 | 'object' => ['This is a message for {$user->name}: Hello', 'This is a message for John Doe: Hello'], 33 | 'html encoded' => ['This is a message for {$user->name}: Hello', 'This is a message for John Doe: Hello'], 34 | 'nested-object' => ['Please send this to {$user->address->street}', 'Please send this to ExampleStreet'], 35 | ]; 36 | } 37 | 38 | /** 39 | * @dataProvider replacesTheContentProvider 40 | */ 41 | public function testReplacesTheContent($input, $output) 42 | { 43 | $user = new \stdClass(); 44 | $user->name = 'John Doe'; 45 | $user->address = new \stdClass(); 46 | $user->address->street = 'ExampleStreet'; 47 | 48 | $event = new FlexibleEvent; 49 | $event->user = $user; 50 | $event->type = 'message'; 51 | 52 | /** @var EmailContent $instance */ 53 | $instance = app(EmailContent::class); 54 | $result = $instance->prepare($input, $event); 55 | 56 | $this->assertEquals($output, $result); 57 | } 58 | 59 | public function testAllowsArrayValues() 60 | { 61 | $user = new \stdClass(); 62 | $user->name = 'John Doe'; 63 | $user->address = ['street' => 'ExampleStreet']; 64 | 65 | $event = new FlexibleEvent; 66 | $event->user = $user; 67 | $event->type = 'message'; 68 | 69 | /** @var EmailContent $instance */ 70 | $instance = app(EmailContent::class); 71 | $result = $instance->prepare('Please send this to {$user->address->street}', $event); 72 | 73 | $this->assertEquals('Please send this to ExampleStreet', $result); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "michielgerritsen/laravel-nova-email-on-event", 3 | "description": "Allow users to send emails when an event occurs.", 4 | "keywords": [ 5 | "laravel", 6 | "nova" 7 | ], 8 | "license": "MIT", 9 | "require": { 10 | "php": ">=7.1.0" 11 | }, 12 | "require-dev": { 13 | "phpunit/phpunit": "^5.7|6.2|^7.0|^8.0", 14 | "orchestra/testbench": "^3.8" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "MichielGerritsen\\LaravelNovaEmailOnEvent\\": "src/" 19 | } 20 | }, 21 | "autoload-dev": { 22 | "psr-4": { 23 | "MichielGerritsen\\LaravelNovaEmailOnEvent\\Test\\": "Test/" 24 | } 25 | }, 26 | "extra": { 27 | "laravel": { 28 | "providers": [ 29 | "MichielGerritsen\\LaravelNovaEmailOnEvent\\MailOnEventServiceProvider" 30 | ] 31 | } 32 | }, 33 | "minimum-stability": "dev", 34 | "prefer-stable": true 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2019_07_10_000000_create_email_events_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 29 | $table->string('event'); 30 | $table->string('to'); 31 | $table->string('from'); 32 | $table->string('subject'); 33 | $table->text('message'); 34 | $table->timestamps(); 35 | }); 36 | } 37 | } -------------------------------------------------------------------------------- /database/migrations/2019_10_06_000000_create_cc_bcc_fields.php: -------------------------------------------------------------------------------- 1 | json('cc')->after('from'); 29 | $table->json('bcc')->after('cc'); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2019_10_07_000000_allow_multiple_recipients.php: -------------------------------------------------------------------------------- 1 | to = json_encode($model->to); 32 | $model->save(); 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /dist/js/boot.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=2)}([function(e,t){e.exports=function(e,t,r,n,o,i){var u,s=e=e||{},a=typeof e.default;"object"!==a&&"function"!==a||(u=e,s=e.default);var c,f="function"==typeof s?s.options:s;if(t&&(f.render=t.render,f.staticRenderFns=t.staticRenderFns,f._compiled=!0),r&&(f.functional=!0),o&&(f._scopeId=o),i?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(i)},f._ssrRegister=c):n&&(c=n),c){var l=f.functional,p=l?f.render:f.beforeCreate;l?(f._injectStyles=c,f.render=function(e,t){return c.call(t),p(e,t)}):f.beforeCreate=p?[].concat(p,c):[c]}return{esModule:u,exports:s,options:f}}},function(e,t,r){"use strict";t.a=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:", ",r=e.join(t);if(e.length>3){var n=e.length;r=e.slice(0,2).join(t),r+=" and "+(n-2)+" more"}return r}},function(e,t,r){e.exports=r(3)},function(e,t,r){Nova.booting(function(e,t,n){e.component("index-email-on-event-email-field",r(4)),e.component("form-email-on-event-email-field",r(7)),e.component("detail-email-on-event-email-field",r(11))})},function(e,t,r){var n=r(0)(r(5),r(6),!1,null,null,null);e.exports=n.exports},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(1);t.default={props:["resourceName","field","value"],beforeMount:function(){var e=[];this.field.value&&(e=JSON.parse(this.field.value)),this.value=Object(n.a)(e)}}},function(e,t){e.exports={render:function(){var e=this.$createElement;return(this._self._c||e)("span",[this._v(this._s(this.value))])},staticRenderFns:[]}},function(e,t,r){var n=r(0)(r(8),r(10),!1,null,null,null);e.exports=n.exports},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(9);r.n(n);t.default={data:function(){return{emails:[{address:""}]}},mixins:[n.FormField,n.HandlesValidationErrors],props:["resourceName","resourceId","field"],methods:{setInitialValue:function(){this.field.value&&(this.emails=JSON.parse(this.field.value).map(function(e){return{address:e}})),this.emails.length||this.emails.push({address:""}),this.value=this.field.value||JSON.stringify([])},fill:function(e){e.append(this.field.attribute,this.value||"")},handleChange:function(e){this.value=e},addEmail:function(){this.emails.push({address:""})},updateValue:function(){var e=this.emails.map(function(e){return e.address});this.value=JSON.stringify(e)},deleteRow:function(e){this.emails.splice(e,1),this.emails.length||this.emails.push({address:""}),this.updateValue()}}}},function(e,t,r){var n;n=function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.i=function(e){return e},r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=47)}([function(e,t,r){"use strict";var n=r(46),o=r(156),i=Object.prototype.toString;function u(e){return"[object Array]"===i.call(e)}function s(e){return null!==e&&"object"==typeof e}function a(e){return"[object Function]"===i.call(e)}function c(e,t){if(null!==e&&void 0!==e)if("object"!=typeof e&&(e=[e]),u(e))for(var r=0,n=e.length;r=200&&e<300}};a.headers={common:{Accept:"application/json, text/plain, */*"}},n.forEach(["delete","get","head"],function(e){a.headers[e]={}}),n.forEach(["post","put","patch"],function(e){a.headers[e]=n.merge(i)}),e.exports=a}).call(t,r(75))},function(e,t,r){"use strict";t.__esModule=!0;var n,o=r(113),i=(n=o)&&n.__esModule?n:{default:n};t.default=function(e,t,r){return t in e?(0,i.default)(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,r){var n=r(9),o=r(1).document,i=n(o)&&n(o.createElement);e.exports=function(e){return i?o.createElement(e):{}}},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t){e.exports=!0},function(e,t,r){"use strict";var n=r(14);e.exports.f=function(e){return new function(e){var t,r;this.promise=new e(function(e,n){if(void 0!==t||void 0!==r)throw TypeError("Bad Promise constructor");t=e,r=n}),this.resolve=n(t),this.reject=n(r)}(e)}},function(e,t,r){var n=r(11).f,o=r(17),i=r(2)("toStringTag");e.exports=function(e,t,r){e&&!o(e=r?e:e.prototype,i)&&n(e,i,{configurable:!0,value:t})}},function(e,t,r){var n=r(60)("keys"),o=r(65);e.exports=function(e){return n[e]||(n[e]=o(e))}},function(e,t){var r=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:r)(e)}},function(e,t,r){var n=r(54),o=r(27);e.exports=function(e){return n(o(e))}},function(e,t,r){var n=r(12).Symbol;e.exports=n},function(e,t,r){var n=r(170),o=r(189);e.exports=function(e,t){var r=o(e,t);return n(r)?r:void 0}},function(e,t){e.exports=function(e,t){return e===t||e!=e&&t!=t}},function(e,t){e.exports=function(e){return e}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=["1/2","1/3","2/3","1/4","3/4","1/5","2/5","3/5","4/5","1/6","5/6"]},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(154);Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i(n).default}}),Object.defineProperty(t,"Form",{enumerable:!0,get:function(){return i(n).default}});var o=r(66);function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"Errors",{enumerable:!0,get:function(){return i(o).default}})},function(e,t,r){"use strict";(function(t){var n=r(0),o=r(101),i=r(104),u=r(110),s=r(108),a=r(45),c="undefined"!=typeof window&&window.btoa&&window.btoa.bind(window)||r(103);e.exports=function(e){return new Promise(function(f,l){var p=e.data,h=e.headers;n.isFormData(p)&&delete h["Content-Type"];var d=new XMLHttpRequest,v="onreadystatechange",y=!1;if("test"===t.env.NODE_ENV||"undefined"==typeof window||!window.XDomainRequest||"withCredentials"in d||s(e.url)||(d=new window.XDomainRequest,v="onload",y=!0,d.onprogress=function(){},d.ontimeout=function(){}),e.auth){var g=e.auth.username||"",m=e.auth.password||"";h.Authorization="Basic "+c(g+":"+m)}if(d.open(e.method.toUpperCase(),i(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d[v]=function(){if(d&&(4===d.readyState||y)&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var t="getAllResponseHeaders"in d?u(d.getAllResponseHeaders()):null,r={data:e.responseType&&"text"!==e.responseType?d.response:d.responseText,status:1223===d.status?204:d.status,statusText:1223===d.status?"No Content":d.statusText,headers:t,config:e,request:d};o(f,l,r),d=null}},d.onerror=function(){l(a("Network Error",e,null,d)),d=null},d.ontimeout=function(){l(a("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED",d)),d=null},n.isStandardBrowserEnv()){var x=r(106),b=(e.withCredentials||s(e.url))&&e.xsrfCookieName?x.read(e.xsrfCookieName):void 0;b&&(h[e.xsrfHeaderName]=b)}if("setRequestHeader"in d&&n.forEach(h,function(e,t){void 0===p&&"content-type"===t.toLowerCase()?delete h[t]:d.setRequestHeader(t,e)}),e.withCredentials&&(d.withCredentials=!0),e.responseType)try{d.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&d.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&d.upload&&d.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){d&&(d.abort(),l(e),d=null)}),void 0===p&&(p=null),d.send(p)})}}).call(t,r(75))},function(e,t,r){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,r){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,r){"use strict";var n=r(100);e.exports=function(e,t,r,o,i){var u=new Error(e);return n(u,t,r,o,i)}},function(e,t,r){"use strict";e.exports=function(e,t){return function(){for(var r=new Array(arguments.length),n=0;nr;)t.push(arguments[r++]);return g[++y]=function(){s("function"==typeof e?e:Function(e),t)},n(y),y},h=function(e){delete g[e]},"process"==r(15)(l)?n=function(e){l.nextTick(u(m,e,1))}:v&&v.now?n=function(e){v.now(u(m,e,1))}:d?(i=(o=new d).port2,o.port1.onmessage=x,n=u(i.postMessage,i,1)):f.addEventListener&&"function"==typeof postMessage&&!f.importScripts?(n=function(e){f.postMessage(e+"","*")},f.addEventListener("message",x,!1)):n="onreadystatechange"in c("script")?function(e){a.appendChild(c("script")).onreadystatechange=function(){a.removeChild(this),m.call(e)}}:function(e){setTimeout(u(m,e,1),0)}),e.exports={set:p,clear:h}},function(e,t,r){var n=r(34),o=Math.min;e.exports=function(e){return e>0?o(n(e),9007199254740991):0}},function(e,t,r){var n=r(27);e.exports=function(e){return Object(n(e))}},function(e,t){var r=0,n=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++r+n).toString(36))}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.record(t)}return n(e,[{key:"all",value:function(){return this.errors}},{key:"has",value:function(e){var t=this.errors.hasOwnProperty(e);t||(t=Object.keys(this.errors).filter(function(t){return t.startsWith(e+".")||t.startsWith(e+"[")}).length>0);return t}},{key:"first",value:function(e){return this.get(e)[0]}},{key:"get",value:function(e){return this.errors[e]||[]}},{key:"any",value:function(){return Object.keys(this.errors).length>0}},{key:"record",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.errors=e}},{key:"clear",value:function(e){if(e){var t=Object.assign({},this.errors);Object.keys(t).filter(function(t){return t===e||t.startsWith(e+".")||t.startsWith(e+"[")}).forEach(function(e){return delete t[e]}),this.errors=t}else this.errors={}}}]),e}();t.default=o},function(e,t,r){var n=r(177),o=r(229),i=r(13),u=r(230),s=r(70),a=r(231),c=Object.prototype.hasOwnProperty;e.exports=function(e,t){var r=i(e),f=!r&&o(e),l=!r&&!f&&u(e),p=!r&&!f&&!l&&a(e),h=r||f||l||p,d=h?n(e.length,String):[],v=d.length;for(var y in e)!t&&!c.call(e,y)||h&&("length"==y||l&&("offset"==y||"parent"==y)||p&&("buffer"==y||"byteLength"==y||"byteOffset"==y)||s(y,v))||d.push(y);return d}},function(e,t,r){(function(t){var r="object"==typeof t&&t&&t.Object===Object&&t;e.exports=r}).call(t,r(241))},function(e,t){var r=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");e.exports=function(e){return r.test(e)}},function(e,t){var r=9007199254740991,n=/^(?:0|[1-9]\d*)$/;e.exports=function(e,t){var o=typeof e;return!!(t=null==t?r:t)&&("number"==o||"symbol"!=o&&n.test(e))&&e>-1&&e%1==0&&e-1&&e%1==0&&e<=r}},function(e,t,r){var n=r(178);e.exports=function(e){return null==e?"":n(e)}},function(e,t){var r,n,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function u(){throw new Error("clearTimeout has not been defined")}function s(e){if(r===setTimeout)return setTimeout(e,0);if((r===i||!r)&&setTimeout)return r=setTimeout,setTimeout(e,0);try{return r(e,0)}catch(t){try{return r.call(null,e,0)}catch(t){return r.call(this,e,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:i}catch(e){r=i}try{n="function"==typeof clearTimeout?clearTimeout:u}catch(e){n=u}}();var a,c=[],f=!1,l=-1;function p(){f&&a&&(f=!1,a.length?c=a.concat(c):l=-1,c.length&&h())}function h(){if(!f){var e=s(p);f=!0;for(var t=c.length;t;){for(a=c,c=[];++l1)for(var r=1;r1&&void 0!==arguments[1]?arguments[1]:null;return this.viaManyToMany?this.detachResources(e):Nova.request({url:"/nova-api/"+this.resourceName,method:"delete",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(r||function(){t.deleteModalOpen=!1,t.getResources()})},deleteSelectedResources:function(){this.deleteResources(this.selectedResources)},deleteAllMatchingResources:function(){var e=this;return this.viaManyToMany?this.detachAllMatchingResources():Nova.request({url:this.deleteAllMatchingResourcesEndpoint,method:"delete",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.deleteModalOpen=!1,e.getResources()})},detachResources:function(e){var t=this;return Nova.request({url:"/nova-api/"+this.resourceName+"/detach",method:"delete",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(function(){t.deleteModalOpen=!1,t.getResources()})},detachAllMatchingResources:function(){var e=this;return Nova.request({url:"/nova-api/"+this.resourceName+"/detach",method:"delete",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.deleteModalOpen=!1,e.getResources()})},forceDeleteResources:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return Nova.request({url:"/nova-api/"+this.resourceName+"/force",method:"delete",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(r||function(){t.deleteModalOpen=!1,t.getResources()})},forceDeleteSelectedResources:function(){this.forceDeleteResources(this.selectedResources)},forceDeleteAllMatchingResources:function(){var e=this;return Nova.request({url:this.forceDeleteSelectedResourcesEndpoint,method:"delete",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.deleteModalOpen=!1,e.getResources()})},restoreResources:function(e){var t=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;return Nova.request({url:"/nova-api/"+this.resourceName+"/restore",method:"put",params:(0,i.default)({},this.queryString,{resources:u(e)})}).then(r||function(){t.restoreModalOpen=!1,t.getResources()})},restoreSelectedResources:function(){this.restoreResources(this.selectedResources)},restoreAllMatchingResources:function(){var e=this;return Nova.request({url:this.restoreAllMatchingResourcesEndpoint,method:"put",params:(0,i.default)({},this.queryString,{resources:"all"})}).then(function(){e.restoreModalOpen=!1,e.getResources()})}},computed:{deleteAllMatchingResourcesEndpoint:function(){return this.lens?"/nova-api/"+this.resourceName+"/lens/"+this.lens:"/nova-api/"+this.resourceName},forceDeleteSelectedResourcesEndpoint:function(){return this.lens?"/nova-api/"+this.resourceName+"/lens/"+this.lens+"/force":"/nova-api/"+this.resourceName+"/force"},restoreAllMatchingResourcesEndpoint:function(){return this.lens?"/nova-api/"+this.resourceName+"/lens/"+this.lens+"/restore":"/nova-api/"+this.resourceName+"/restore"},queryString:function(){return{search:this.currentSearch,filters:this.encodedFilters,trashed:this.currentTrashed,viaResource:this.viaResource,viaResourceId:this.viaResourceId,viaRelationship:this.viaRelationship}}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=u(r(50)),o=u(r(26)),i=u(r(49));u(r(226)),u(r(228));function u(e){return e&&e.__esModule?e:{default:e}}t.default={methods:{clearSelectedFilters:function(){var e=(0,i.default)(n.default.mark(function e(t){var r;return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!t){e.next=5;break}return e.next=3,this.$store.dispatch(this.resourceName+"/resetFilterState",{resourceName:this.resourceName,lens:t});case 3:e.next=7;break;case 5:return e.next=7,this.$store.dispatch(this.resourceName+"/resetFilterState",{resourceName:this.resourceName});case 7:this.updateQueryString((r={},(0,o.default)(r,this.pageParameter,1),(0,o.default)(r,this.filterParameter,""),r));case 8:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}(),filterChanged:function(){var e;this.updateQueryString((e={},(0,o.default)(e,this.pageParameter,1),(0,o.default)(e,this.filterParameter,this.$store.getters[this.resourceName+"/currentEncodedFilters"]),e))},initializeFilters:function(){var e=(0,i.default)(n.default.mark(function e(t){return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return this.$store.commit(this.resourceName+"/clearFilters"),e.next=3,this.$store.dispatch(this.resourceName+"/fetchFilters",{resourceName:this.resourceName,lens:t});case 3:return e.next=5,this.initializeState(t);case 5:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}(),initializeState:function(){var e=(0,i.default)(n.default.mark(function e(t){return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.initialEncodedFilters){e.next=5;break}return e.next=3,this.$store.dispatch(this.resourceName+"/initializeCurrentFilterValuesFromQueryString",this.initialEncodedFilters);case 3:e.next=7;break;case 5:return e.next=7,this.$store.dispatch(this.resourceName+"/resetFilterState",{resourceName:this.resourceName,lens:t});case 7:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}()},computed:{filterParameter:function(){return this.resourceName+"_filter"}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{resourceName:{},field:{}},data:function(){return{value:""}},mounted:function(){var e=this;this.setInitialValue(),this.field.fill=this.fill,Nova.$on(this.field.attribute+"-value",function(t){e.value=t})},destroyed:function(){Nova.$off(this.field.attribute+"-value")},methods:{setInitialValue:function(){this.value=void 0!==this.field.value&&null!==this.field.value?this.field.value:""},fill:function(e){e.append(this.field.attribute,String(this.value))},handleChange:function(e){this.value=e}},computed:{isReadonly:function(){return this.field.readonly||_.get(this.field,"extraAttributes.readonly")}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(41);t.default={props:{errors:{default:function(){return new n.Errors}}},data:function(){return{errorClass:"border-danger"}},computed:{errorClasses:function(){return this.hasError?[this.errorClass]:[]},fieldAttribute:function(){return this.field.attribute},hasError:function(){return this.errors.has(this.fieldAttribute)},firstError:function(){if(this.hasError)return this.errors.first(this.fieldAttribute)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=u(r(50)),o=u(r(49)),i=u(r(40));function u(e){return e&&e.__esModule?e:{default:e}}t.default={props:{loadCards:{type:Boolean,default:!0}},data:function(){return{cards:[]}},created:function(){this.fetchCards()},watch:{cardsEndpoint:function(){this.fetchCards()}},methods:{fetchCards:function(){var e=(0,o.default)(n.default.mark(function e(){var t,r;return n.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.loadCards){e.next=6;break}return e.next=3,Nova.request().get(this.cardsEndpoint,{params:this.extraCardParams});case 3:t=e.sent,r=t.data,this.cards=r;case 6:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},computed:{shouldShowCards:function(){return this.cards.length>0},smallCards:function(){return _.filter(this.cards,function(e){return-1!==i.default.indexOf(e.width)})},largeCards:function(){return _.filter(this.cards,function(e){return"full"==e.width})},extraCardParams:function(){return null}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={methods:{toAppTimezone:function(e){return e?moment.tz(e,this.userTimezone).clone().tz(Nova.config.timezone).format("YYYY-MM-DD HH:mm:ss"):e},fromAppTimezone:function(e){return e?moment.tz(e,Nova.config.timezone).clone().tz(this.userTimezone).format("YYYY-MM-DD HH:mm:ss"):e},localizeDateTimeField:function(e){if(!e.value)return e.value;var t=moment.tz(e.value,Nova.config.timezone).clone().tz(this.userTimezone);return e.format?t.format(e.format):this.usesTwelveHourTime?t.format("YYYY-MM-DD h:mm:ss A"):t.format("YYYY-MM-DD HH:mm:ss")},localizeDateField:function(e){if(!e.value)return e.value;var t=moment.tz(e.value,Nova.config.timezone).clone().tz(this.userTimezone);return e.format?t.format(e.format):t.format("YYYY-MM-DD")}},computed:{userTimezone:function(){return Nova.config.userTimezone?Nova.config.userTimezone:moment.tz.guess()},usesTwelveHourTime:function(){return _.endsWith((new Date).toLocaleString(),"AM")||_.endsWith((new Date).toLocaleString(),"PM")}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(225),i=(n=o)&&n.__esModule?n:{default:n};t.default={methods:{updateQueryString:function(e){this.$router.push({query:(0,i.default)(e,this.$route.query)})}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={computed:{resourceInformation:function(){var e=this;return _.find(Nova.config.resources,function(t){return t.uriKey==e.resourceName})},viaResourceInformation:function(){var e=this;if(this.viaResource)return _.find(Nova.config.resources,function(t){return t.uriKey==e.viaResource})},authorizedToCreate:function(){return this.resourceInformation.authorizedToCreate}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(26),i=(n=o)&&n.__esModule?n:{default:n};t.default={methods:{selectPreviousPage:function(){this.updateQueryString((0,i.default)({},this.pageParameter,this.currentPage-1))},selectNextPage:function(){this.updateQueryString((0,i.default)({},this.pageParameter,this.currentPage+1))}},computed:{currentPage:function(){return parseInt(this.$route.query[this.pageParameter]||1)}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(26),i=(n=o)&&n.__esModule?n:{default:n};t.default={data:function(){return{perPage:25}},methods:{initializePerPageFromQueryString:function(){this.perPage=this.currentPerPage},perPageChanged:function(){this.updateQueryString((0,i.default)({},this.perPageParameter,this.perPage))}},computed:{currentPerPage:function(){return this.$route.query[this.perPageParameter]||25}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(224),i=(n=o)&&n.__esModule?n:{default:n};t.default={data:function(){return{search:"",selectedResource:"",availableResources:[]}},methods:{selectResource:function(e){this.selectedResource=e},handleSearchCleared:function(){this.availableResources=[]},clearSelection:function(){this.selectedResource="",this.availableResources=[]},performSearch:function(e){var t=this;this.search=e;var r=e.trim();""!=r?this.debouncer(function(){t.selectedResource="",t.getAvailableResources(r)},500):this.clearSelection()},debouncer:(0,i.default)(function(e){return e()},500)}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={data:function(){return{withTrashed:!1}},methods:{toggleWithTrashed:function(){this.withTrashed=!this.withTrashed},enableWithTrashed:function(){this.withTrashed=!0},disableWithTrashed:function(){this.withTrashed=!1}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return(0,i.default)(e)};var n,o=r(238),i=(n=o)&&n.__esModule?n:{default:n}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(48),i=(n=o)&&n.__esModule?n:{default:n};t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100;return i.default.all([e,new i.default(function(e){setTimeout(function(){return e()},t)})]).then(function(e){return e[0]})}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return e>1||0==e?n.Inflector.pluralize(t):n.Inflector.singularize(t)};var n=r(47)},function(e,t,r){"use strict";var n={uncountableWords:["equipment","information","rice","money","species","series","fish","sheep","moose","deer","news"],pluralRules:[[new RegExp("(m)an$","gi"),"$1en"],[new RegExp("(pe)rson$","gi"),"$1ople"],[new RegExp("(child)$","gi"),"$1ren"],[new RegExp("^(ox)$","gi"),"$1en"],[new RegExp("(ax|test)is$","gi"),"$1es"],[new RegExp("(octop|vir)us$","gi"),"$1i"],[new RegExp("(alias|status)$","gi"),"$1es"],[new RegExp("(bu)s$","gi"),"$1ses"],[new RegExp("(buffal|tomat|potat)o$","gi"),"$1oes"],[new RegExp("([ti])um$","gi"),"$1a"],[new RegExp("sis$","gi"),"ses"],[new RegExp("(?:([^f])fe|([lr])f)$","gi"),"$1$2ves"],[new RegExp("(hive)$","gi"),"$1s"],[new RegExp("([^aeiouy]|qu)y$","gi"),"$1ies"],[new RegExp("(x|ch|ss|sh)$","gi"),"$1es"],[new RegExp("(matr|vert|ind)ix|ex$","gi"),"$1ices"],[new RegExp("([m|l])ouse$","gi"),"$1ice"],[new RegExp("(quiz)$","gi"),"$1zes"],[new RegExp("s$","gi"),"s"],[new RegExp("$","gi"),"s"]],singularRules:[[new RegExp("(m)en$","gi"),"$1an"],[new RegExp("(pe)ople$","gi"),"$1rson"],[new RegExp("(child)ren$","gi"),"$1"],[new RegExp("([ti])a$","gi"),"$1um"],[new RegExp("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$","gi"),"$1$2sis"],[new RegExp("(hive)s$","gi"),"$1"],[new RegExp("(tive)s$","gi"),"$1"],[new RegExp("(curve)s$","gi"),"$1"],[new RegExp("([lr])ves$","gi"),"$1f"],[new RegExp("([^fo])ves$","gi"),"$1fe"],[new RegExp("([^aeiouy]|qu)ies$","gi"),"$1y"],[new RegExp("(s)eries$","gi"),"$1eries"],[new RegExp("(m)ovies$","gi"),"$1ovie"],[new RegExp("(x|ch|ss|sh)es$","gi"),"$1"],[new RegExp("([m|l])ice$","gi"),"$1ouse"],[new RegExp("(bus)es$","gi"),"$1"],[new RegExp("(o)es$","gi"),"$1"],[new RegExp("(shoe)s$","gi"),"$1"],[new RegExp("(cris|ax|test)es$","gi"),"$1is"],[new RegExp("(octop|vir)i$","gi"),"$1us"],[new RegExp("(alias|status)es$","gi"),"$1"],[new RegExp("^(ox)en","gi"),"$1"],[new RegExp("(vert|ind)ices$","gi"),"$1ex"],[new RegExp("(matr)ices$","gi"),"$1ix"],[new RegExp("(quiz)zes$","gi"),"$1"],[new RegExp("s$","gi"),""]],nonTitlecasedWords:["and","or","nor","a","an","the","so","but","to","of","at","by","from","into","on","onto","off","out","in","over","with","for"],idSuffix:new RegExp("(_ids|_id)$","g"),underbar:new RegExp("_","g"),spaceOrUnderbar:new RegExp("[ _]","g"),uppercase:new RegExp("([A-Z])","g"),underbarPrefix:new RegExp("^_"),applyRules:function(e,t,r,n){if(n)e=n;else if(!(r.indexOf(e.toLowerCase())>-1))for(var o=0;o>8-s%1*8)){if((r=i.charCodeAt(s+=.75))>255)throw new o;t=t<<8|r}return u}},function(e,t,r){"use strict";var n=r(0);function o(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}e.exports=function(e,t,r){if(!t)return e;var i;if(r)i=r(t);else if(n.isURLSearchParams(t))i=t.toString();else{var u=[];n.forEach(t,function(e,t){null!==e&&void 0!==e&&(n.isArray(e)?t+="[]":e=[e],n.forEach(e,function(e){n.isDate(e)?e=e.toISOString():n.isObject(e)&&(e=JSON.stringify(e)),u.push(o(t)+"="+o(e))}))}),i=u.join("&")}return i&&(e+=(-1===e.indexOf("?")?"?":"&")+i),e}},function(e,t,r){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,r){"use strict";var n=r(0);e.exports=n.isStandardBrowserEnv()?{write:function(e,t,r,o,i,u){var s=[];s.push(e+"="+encodeURIComponent(t)),n.isNumber(r)&&s.push("expires="+new Date(r).toGMTString()),n.isString(o)&&s.push("path="+o),n.isString(i)&&s.push("domain="+i),!0===u&&s.push("secure"),document.cookie=s.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,r){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,r){"use strict";var n=r(0);e.exports=n.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a");function o(e){var n=e;return t&&(r.setAttribute("href",n),n=r.href),r.setAttribute("href",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:"/"===r.pathname.charAt(0)?r.pathname:"/"+r.pathname}}return e=o(window.location.href),function(t){var r=n.isString(t)?o(t):t;return r.protocol===e.protocol&&r.host===e.host}}():function(){return!0}},function(e,t,r){"use strict";var n=r(0);e.exports=function(e,t){n.forEach(e,function(r,n){n!==t&&n.toUpperCase()===t.toUpperCase()&&(e[t]=r,delete e[n])})}},function(e,t,r){"use strict";var n=r(0),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,r,i,u={};return e?(n.forEach(e.split("\n"),function(e){if(i=e.indexOf(":"),t=n.trim(e.substr(0,i)).toLowerCase(),r=n.trim(e.substr(i+1)),t){if(u[t]&&o.indexOf(t)>=0)return;u[t]="set-cookie"===t?(u[t]?u[t]:[]).concat([r]):u[t]?u[t]+", "+r:r}}),u):u}},function(e,t,r){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,r){e.exports={default:r(115),__esModule:!0}},function(e,t,r){e.exports={default:r(116),__esModule:!0}},function(e,t,r){"use strict";t.__esModule=!0;var n,o=r(112),i=(n=o)&&n.__esModule?n:{default:n};t.default=i.default||function(e){for(var t=1;tf;)if((s=a[f++])!=s)return!0}else for(;c>f;f++)if((e||f in a)&&a[f]===r)return e||f||0;return!e&&-1}}},function(e,t,r){var n=r(16),o=r(125),i=r(124),u=r(4),s=r(63),a=r(144),c={},f={};(t=e.exports=function(e,t,r,l,p){var h,d,v,y,g=p?function(){return e}:a(e),m=n(r,l,t?2:1),x=0;if("function"!=typeof g)throw TypeError(e+" is not iterable!");if(i(g)){for(h=s(e.length);h>x;x++)if((y=t?m(u(d=e[x])[0],d[1]):m(e[x]))===c||y===f)return y}else for(v=g.call(e);!(d=v.next()).done;)if((y=o(v,m,d.value,t))===c||y===f)return y}).BREAK=c,t.RETURN=f},function(e,t,r){e.exports=!r(5)&&!r(29)(function(){return 7!=Object.defineProperty(r(28)("div"),"a",{get:function(){return 7}}).a})},function(e,t){e.exports=function(e,t,r){var n=void 0===r;switch(t.length){case 0:return n?e():e.call(r);case 1:return n?e(t[0]):e.call(r,t[0]);case 2:return n?e(t[0],t[1]):e.call(r,t[0],t[1]);case 3:return n?e(t[0],t[1],t[2]):e.call(r,t[0],t[1],t[2]);case 4:return n?e(t[0],t[1],t[2],t[3]):e.call(r,t[0],t[1],t[2],t[3])}return e.apply(r,t)}},function(e,t,r){var n=r(10),o=r(2)("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(n.Array===e||i[o]===e)}},function(e,t,r){var n=r(4);e.exports=function(e,t,r,o){try{return o?t(n(r)[0],r[1]):t(r)}catch(t){var i=e.return;throw void 0!==i&&n(i.call(e)),t}}},function(e,t,r){"use strict";var n=r(131),o=r(59),i=r(32),u={};r(7)(u,r(2)("iterator"),function(){return this}),e.exports=function(e,t,r){e.prototype=n(u,{next:o(1,r)}),i(e,t+" Iterator")}},function(e,t,r){var n=r(2)("iterator"),o=!1;try{var i=[7][n]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var r=!1;try{var i=[7],u=i[n]();u.next=function(){return{done:r=!0}},i[n]=function(){return u},e(i)}catch(e){}return r}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,r){var n=r(1),o=r(62).set,i=n.MutationObserver||n.WebKitMutationObserver,u=n.process,s=n.Promise,a="process"==r(15)(u);e.exports=function(){var e,t,r,c=function(){var n,o;for(a&&(n=u.domain)&&n.exit();e;){o=e.fn,e=e.next;try{o()}catch(n){throw e?r():t=void 0,n}}t=void 0,n&&n.enter()};if(a)r=function(){u.nextTick(c)};else if(!i||n.navigator&&n.navigator.standalone)if(s&&s.resolve){var f=s.resolve(void 0);r=function(){f.then(c)}}else r=function(){o.call(n,c)};else{var l=!0,p=document.createTextNode("");new i(c).observe(p,{characterData:!0}),r=function(){p.data=l=!l}}return function(n){var o={fn:n,next:void 0};t&&(t.next=o),e||(e=o,r()),t=o}}},function(e,t,r){"use strict";var n=r(56),o=r(133),i=r(136),u=r(64),s=r(54),a=Object.assign;e.exports=!a||r(29)(function(){var e={},t={},r=Symbol(),n="abcdefghijklmnopqrst";return e[r]=7,n.split("").forEach(function(e){t[e]=e}),7!=a({},e)[r]||Object.keys(a({},t)).join("")!=n})?function(e,t){for(var r=u(e),a=arguments.length,c=1,f=o.f,l=i.f;a>c;)for(var p,h=s(arguments[c++]),d=f?n(h).concat(f(h)):n(h),v=d.length,y=0;v>y;)l.call(h,p=d[y++])&&(r[p]=h[p]);return r}:a},function(e,t,r){var n=r(4),o=r(132),i=r(52),u=r(33)("IE_PROTO"),s=function(){},a=function(){var e,t=r(28)("iframe"),n=i.length;for(t.style.display="none",r(53).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" 21 | -------------------------------------------------------------------------------- /resources/js/components/EmailField/FormField.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 102 | -------------------------------------------------------------------------------- /resources/js/components/EmailField/IndexField.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * ______ __ __ 3 | * / ____/___ ____ / /__________ / / 4 | * / / / __ \/ __ \/ __/ ___/ __ \/ / 5 | * / /___/ /_/ / / / / /_/ / / /_/ / / 6 | * \______________/_/\__/_/ \____/_/ 7 | * / | / / /_ 8 | * / /| | / / __/ 9 | * / ___ |/ / /_ 10 | * /_/ _|||_/\__/ __ __ 11 | * / __ \___ / /__ / /____ 12 | * / / / / _ \/ / _ \/ __/ _ \ 13 | * / /_/ / __/ / __/ /_/ __/ 14 | * /_____/\___/_/\___/\__/\___/ 15 | * 16 | */ 17 | 20 | 21 | 38 | -------------------------------------------------------------------------------- /resources/js/functions/FormatEmailList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ______ __ __ 3 | * / ____/___ ____ / /__________ / / 4 | * / / / __ \/ __ \/ __/ ___/ __ \/ / 5 | * / /___/ /_/ / / / / /_/ / / /_/ / / 6 | * \______________/_/\__/_/ \____/_/ 7 | * / | / / /_ 8 | * / /| | / / __/ 9 | * / ___ |/ / /_ 10 | * /_/ _|||_/\__/ __ __ 11 | * / __ \___ / /__ / /____ 12 | * / / / / _ \/ / _ \/ __/ _ \ 13 | * / /_/ / __/ / __/ /_/ __/ 14 | * /_____/\___/_/\___/\__/\___/ 15 | * 16 | */ 17 | 18 | export function FormatEmailList(emails, glue = ', ') { 19 | let value = emails.join(glue); 20 | 21 | if (emails.length > 3) { 22 | let length = emails.length; 23 | value = emails.slice(0, 2).join(glue); 24 | value += ' and ' + (length - 2) + ' more'; 25 | } 26 | 27 | return value; 28 | } 29 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | namespace = $namespace; 38 | $this->name = $name; 39 | } 40 | 41 | /** 42 | * @return string 43 | */ 44 | public function getNamespace(): string 45 | { 46 | return $this->namespace; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getName(): string 53 | { 54 | return $this->name; 55 | } 56 | } -------------------------------------------------------------------------------- /src/Events/WildcardEvent.php: -------------------------------------------------------------------------------- 1 | eventFinder = $eventFinder; 54 | $this->emailSender = $emailSender; 55 | } 56 | 57 | public function handle($eventName, $data) 58 | { 59 | if (Str::startsWith($eventName, $this->skipped)) { 60 | return null; 61 | } 62 | 63 | $eventName = '\\' . $eventName; 64 | if (!$this->eventFinder->hasEvent($eventName)) { 65 | return null; 66 | } 67 | 68 | $mails = Cache::remember('email-events.events.' . $eventName, 60 * 60, function () use ($eventName) { 69 | return EmailEvent::where('event', $eventName)->get(); 70 | }); 71 | 72 | foreach ($mails as $mail) { 73 | $this->emailSender->send($mail, $data[0]); 74 | } 75 | 76 | return null; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Mail/DynamicMail.php: -------------------------------------------------------------------------------- 1 | shop = $shop; 28 | $this->data = $data; 29 | } 30 | 31 | /** 32 | * Build the message. 33 | * 34 | * @return $this 35 | */ 36 | public function build() 37 | { 38 | return $this->markdown('email.contact', $this->data + ['shop' => $this->shop]); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/MailOnEventServiceProvider.php: -------------------------------------------------------------------------------- 1 | loadMigrationsFrom(__DIR__ . '/../database/migrations'); 29 | 30 | \Event::listen('*', WildcardEvent::class); 31 | } 32 | 33 | /** 34 | * Register any application services. 35 | * 36 | * @return void 37 | */ 38 | public function register() 39 | { 40 | // 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Models/EmailEvent.php: -------------------------------------------------------------------------------- 1 | event); 40 | }); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/Nova/Fields/EmailField.php: -------------------------------------------------------------------------------- 1 | options(app(EventFinder::class)->find())->required(), 52 | Text::make('From')->required(), 53 | EmailField::make('To')->required(), 54 | EmailField::make('Cc')->hideFromIndex(), 55 | EmailField::make('Bcc')->hideFromIndex(), 56 | Text::make('Subject')->required(), 57 | Trix::make('Message')->hideFromIndex()->required(), 58 | ]; 59 | } 60 | 61 | /** 62 | * Get the cards available for the request. 63 | * 64 | * @param \Illuminate\Http\Request $request 65 | * @return array 66 | */ 67 | public function cards(Request $request) 68 | { 69 | return []; 70 | } 71 | 72 | /** 73 | * Get the filters available for the resource. 74 | * 75 | * @param \Illuminate\Http\Request $request 76 | * @return array 77 | */ 78 | public function filters(Request $request) 79 | { 80 | return []; 81 | } 82 | 83 | /** 84 | * Get the lenses available for the resource. 85 | * 86 | * @param \Illuminate\Http\Request $request 87 | * @return array 88 | */ 89 | public function lenses(Request $request) 90 | { 91 | return []; 92 | } 93 | 94 | /** 95 | * Get the actions available for the resource. 96 | * 97 | * @param \Illuminate\Http\Request $request 98 | * @return array 99 | */ 100 | public function actions(Request $request) 101 | { 102 | return []; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Service/EmailContent.php: -------------------------------------------------------------------------------- 1 | variables = get_object_vars($event); 34 | 35 | return preg_replace_callback('#{(.*?)}#', [$this, 'replaceVariable'], $message); 36 | } 37 | 38 | private function replaceVariable($match) 39 | { 40 | $parts = explode('->', str_replace('->', '->', $match[1])); 41 | 42 | $first = substr(Arr::first($parts), 1); 43 | 44 | if (!isset($this->variables[$first])) { 45 | return null; 46 | } 47 | 48 | $value = $this->variables[$first]; 49 | foreach (array_splice($parts, 1) as $part) { 50 | if (is_array($value) && array_key_exists($part, $value)) { 51 | $value = $value[$part]; 52 | continue; 53 | } 54 | 55 | if ((is_object($value) && property_exists($value, $part)) || ($value instanceof Model)) { 56 | $value = $value->$part; 57 | continue; 58 | } 59 | 60 | break; 61 | } 62 | 63 | return $value; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Service/EmailSender.php: -------------------------------------------------------------------------------- 1 | emailContent = $emailContent; 47 | } 48 | 49 | public function send(EmailEvent $email, $event) 50 | { 51 | $this->email = $email; 52 | $this->event = $event; 53 | 54 | $content = $this->emailContent->prepare($this->email->message, $this->event); 55 | Mail::html($content, [$this, 'composeMessage']); 56 | } 57 | 58 | public function composeMessage(Message $message) 59 | { 60 | $this->addRecipients($message, 'to', $this->event); 61 | $this->addRecipients($message, 'cc', $this->event); 62 | $this->addRecipients($message, 'bcc', $this->event); 63 | 64 | $subject = $this->emailContent->prepare($this->email->subject, $this->event); 65 | $from = $this->emailContent->prepare($this->email->from, $this->event); 66 | 67 | $message->from($from); 68 | $message->subject($subject); 69 | } 70 | 71 | private function addRecipients(Message $message, string $recipientType, $event) 72 | { 73 | if (!in_array($recipientType, ['to', 'cc', 'bcc'])) { 74 | throw new \Exception(sprintf('Unknown recipient type: %1', $recipientType)); 75 | } 76 | 77 | $recipients = json_decode($this->email->$recipientType, JSON_OBJECT_AS_ARRAY); 78 | 79 | $recipients = array_map(function ($recipient) { 80 | return $this->emailContent->prepare($recipient, $this->event); 81 | }, $recipients); 82 | 83 | $recipients = array_filter($recipients, function ($recipient) { 84 | return filter_var($recipient, FILTER_VALIDATE_EMAIL); 85 | }); 86 | 87 | if (!$recipients) { 88 | return; 89 | } 90 | 91 | $message->{'set' . ucfirst($recipientType)}($recipients); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Service/Files/ClassNameFinder.php: -------------------------------------------------------------------------------- 1 | getRealPath(), 'r'); 35 | $class = $namespace = $buffer = ''; 36 | while (!$class) { 37 | if (feof($fp)) break; 38 | 39 | $buffer .= fread($fp, 512); 40 | $tokens = token_get_all($buffer); 41 | 42 | if (strpos($buffer, '{') === false) continue; 43 | 44 | for (;$iclassNameFinder = $classNameFinder; 41 | } 42 | 43 | public function find() 44 | { 45 | $this->findEventsInPath(app_path('Events')); 46 | 47 | return $this->files; 48 | } 49 | 50 | public function hasEvent($eventName) 51 | { 52 | /** @var Collection $events */ 53 | $events = \Cache::remember('email-events.event-list', 60, function () { 54 | return EmailEvent::groupBy('event')->pluck('event'); 55 | }); 56 | 57 | return $events->contains($eventName); 58 | } 59 | 60 | private function findEventsInPath($path) 61 | { 62 | $iterator = new \RecursiveDirectoryIterator($path); 63 | 64 | /** @var \SplFileInfo $file */ 65 | foreach (new \RecursiveIteratorIterator($iterator) as $file) { 66 | if (!Str::endsWith($file->getRealPath(), '.php')) { 67 | continue; 68 | } 69 | 70 | $classInfo = $this->classNameFinder->fromFile($file); 71 | 72 | $this->files[$classInfo->getNamespace()] = $this->getReadableName($classInfo->getName()); 73 | } 74 | } 75 | 76 | private function getReadableName($name) 77 | { 78 | preg_match_all('/((?:^|[A-Z])[a-z]+)/', $name,$matches); 79 | 80 | return implode(' ', $matches[0]); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ______ __ __ 3 | * / ____/___ ____ / /__________ / / 4 | * / / / __ \/ __ \/ __/ ___/ __ \/ / 5 | * / /___/ /_/ / / / / /_/ / / /_/ / / 6 | * \______________/_/\__/_/ \____/_/ 7 | * / | / / /_ 8 | * / /| | / / __/ 9 | * / ___ |/ / /_ 10 | * /_/ _|||_/\__/ __ __ 11 | * / __ \___ / /__ / /____ 12 | * / / / / _ \/ / _ \/ __/ _ \ 13 | * / /_/ / __/ / __/ /_/ __/ 14 | * /_____/\___/_/\___/\__/\___/ 15 | * 16 | */ 17 | let mix = require('laravel-mix'); 18 | 19 | mix.setPublicPath('dist') 20 | .js('resources/js/boot.js', 'js'); 21 | // .sass('resources/sass/tool.scss', 'css') 22 | --------------------------------------------------------------------------------