From f6318409a740d576570a08c9d9b1027357e1a949 Mon Sep 17 00:00:00 2001 From: Joseph Cohen Date: Sat, 31 Oct 2015 15:33:11 -0600 Subject: [PATCH] Start working on the invite system for users --- app/Commands/User/InviteTeamMemberCommand.php | 43 +++++++++++++ app/Events/User/UserWasInvitedEvent.php | 34 ++++++++++ .../User/InviteTeamMemberCommandHandler.php | 37 +++++++++++ .../User/SendInviteUserEmailHandler.php | 62 +++++++++++++++++++ .../Controllers/Dashboard/TeamController.php | 33 ++++++++++ app/Http/Routes/DashboardRoutes.php | 5 ++ app/Models/Invite.php | 48 ++++++++++++++ app/Providers/EventServiceProvider.php | 3 + .../2015_10_31_211944_CreateInvitesTable.php | 35 +++++++++++ .../views/dashboard/team/index.blade.php | 11 +++- .../views/dashboard/team/invite.blade.php | 47 ++++++++++++++ .../views/emails/users/invite-html.blade.php | 13 ++++ .../views/emails/users/invite-text.blade.php | 5 ++ 13 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 app/Commands/User/InviteTeamMemberCommand.php create mode 100644 app/Events/User/UserWasInvitedEvent.php create mode 100644 app/Handlers/Commands/User/InviteTeamMemberCommandHandler.php create mode 100644 app/Handlers/Events/User/SendInviteUserEmailHandler.php create mode 100644 app/Models/Invite.php create mode 100644 database/migrations/2015_10_31_211944_CreateInvitesTable.php create mode 100644 resources/views/dashboard/team/invite.blade.php create mode 100644 resources/views/emails/users/invite-html.blade.php create mode 100644 resources/views/emails/users/invite-text.blade.php diff --git a/app/Commands/User/InviteTeamMemberCommand.php b/app/Commands/User/InviteTeamMemberCommand.php new file mode 100644 index 00000000..d97d9d2b --- /dev/null +++ b/app/Commands/User/InviteTeamMemberCommand.php @@ -0,0 +1,43 @@ + 'required|array|email', + ]; + + /** + * Create a new invite team member command instance. + * + * @param array $email + * + * @return void + */ + public function __construct($emails) + { + $this->emails = $emails; + } +} diff --git a/app/Events/User/UserWasInvitedEvent.php b/app/Events/User/UserWasInvitedEvent.php new file mode 100644 index 00000000..fa083f27 --- /dev/null +++ b/app/Events/User/UserWasInvitedEvent.php @@ -0,0 +1,34 @@ +invite = $invite; + } +} diff --git a/app/Handlers/Commands/User/InviteTeamMemberCommandHandler.php b/app/Handlers/Commands/User/InviteTeamMemberCommandHandler.php new file mode 100644 index 00000000..2692c546 --- /dev/null +++ b/app/Handlers/Commands/User/InviteTeamMemberCommandHandler.php @@ -0,0 +1,37 @@ +emails as $email) { + $invite = Invite::create([ + 'email' => $command->email, + ]); + + event(new UserWasInvitedEvent($invite)); + } + } +} diff --git a/app/Handlers/Events/User/SendInviteUserEmailHandler.php b/app/Handlers/Events/User/SendInviteUserEmailHandler.php new file mode 100644 index 00000000..d8ebd21d --- /dev/null +++ b/app/Handlers/Events/User/SendInviteUserEmailHandler.php @@ -0,0 +1,62 @@ +mailer = $mailer; + } + + /** + * Handle the event. + * + * @param \CachetHQ\Cachet\Events\UserWasInvitedEvent $event + * + * @return void + */ + public function handle(UserWasInvitedEvent $event) + { + $mail = [ + 'email' => $event->invite->email, + 'subject' => 'You have been invited.', + 'link' => route('invite.signup', ['code' => $event->invite->code]), + 'app_url' => env('APP_URL'), + ]; + + $this->mailer->queue([ + 'html' => 'emails.users.invite-html', + 'text' => 'emails.users.invite-text', + ], $mail, function (Message $message) use ($mail) { + $message->to($mail['email'])->subject($mail['subject']); + }); + } +} diff --git a/app/Http/Controllers/Dashboard/TeamController.php b/app/Http/Controllers/Dashboard/TeamController.php index 1635bb0e..8f6ff744 100644 --- a/app/Http/Controllers/Dashboard/TeamController.php +++ b/app/Http/Controllers/Dashboard/TeamController.php @@ -62,6 +62,17 @@ class TeamController extends Controller ->withPageTitle(trans('dashboard.team.add.title').' - '.trans('dashboard.dashboard')); } + /** + * Shows the invite team member view. + * + * @return \Illuminate\View\View + */ + public function showInviteTeamMemberView() + { + return View::make('dashboard.team.invite') + ->withPageTitle(trans('dashboard.team.invite.title').' - '.trans('dashboard.dashboard')); + } + /** * Creates a new team member. * @@ -111,6 +122,28 @@ class TeamController extends Controller ->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.team.edit.success'))); } + /** + * Creates a new team member. + * + * @return \Illuminate\Http\RedirectResponse + */ + public function postInviteUser() + { + try { + $this->dispatch(new InviteTeamMemberCommand( + array_unique(array_filter((array) Binput::get('emails'))) + )); + } catch (ValidationException $e) { + return Redirect::route('dashboard.team.invite') + ->withInput(Binput::except('password')) + ->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.team.invite.failure'))) + ->withErrors($e->getMessageBag()); + } + + return Redirect::route('dashboard.team.invite') + ->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.team.invite.success'))); + } + /** * Delete a user. * diff --git a/app/Http/Routes/DashboardRoutes.php b/app/Http/Routes/DashboardRoutes.php index da7c938e..1aa77721 100644 --- a/app/Http/Routes/DashboardRoutes.php +++ b/app/Http/Routes/DashboardRoutes.php @@ -202,8 +202,13 @@ class DashboardRoutes 'as' => 'add', 'uses' => 'TeamController@showAddTeamMemberView', ]); + $router->get('invite', [ + 'as' => 'invite', + 'uses' => 'TeamController@showInviteTeamMemberView', + ]); $router->get('{user}', 'TeamController@showTeamMemberView'); $router->post('add', 'TeamController@postAddUser'); + $router->post('invite', 'TeamController@postInviteUser'); $router->post('{user}', 'TeamController@postUpdateUser'); $router->delete('{user}/delete', 'TeamController@deleteUser'); }); diff --git a/app/Models/Invite.php b/app/Models/Invite.php new file mode 100644 index 00000000..8fcf38e7 --- /dev/null +++ b/app/Models/Invite.php @@ -0,0 +1,48 @@ + 'string', + ]; + + /** + * The fillable properties. + * + * @var string[] + */ + protected $fillable = ['email']; + + /** + * Overrides the models boot method. + */ + public static function boot() + { + parent::boot(); + + self::creating(function ($invite) { + if (!$invite->code) { + $invite->code = self::generateVerifyCode(); + } + }); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 89baec7e..c163b674 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -30,6 +30,9 @@ class EventServiceProvider extends ServiceProvider 'CachetHQ\Cachet\Events\Subscriber\SubscriberHasSubscribedEvent' => [ 'CachetHQ\Cachet\Handlers\Events\Subscriber\SendSubscriberVerificationEmailHandler', ], + 'CachetHQ\Cachet\Events\User\UserWasInvitedEvent' => [ + 'CachetHQ\Cachet\Handlers\Events\User\SendInviteEmailHandler', + ], 'CachetHQ\Cachet\Events\User\UserWasAddedEvent' => [ // ], diff --git a/database/migrations/2015_10_31_211944_CreateInvitesTable.php b/database/migrations/2015_10_31_211944_CreateInvitesTable.php new file mode 100644 index 00000000..e72bc657 --- /dev/null +++ b/database/migrations/2015_10_31_211944_CreateInvitesTable.php @@ -0,0 +1,35 @@ +engine = 'InnoDB'; + + $table->increments('id'); + $table->string('code')->unique(); + $table->string('email'); + $table->timestamp('claimed_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('invites'); + } +} diff --git a/resources/views/dashboard/team/index.blade.php b/resources/views/dashboard/team/index.blade.php index 4f39922e..960dfb4e 100644 --- a/resources/views/dashboard/team/index.blade.php +++ b/resources/views/dashboard/team/index.blade.php @@ -9,9 +9,14 @@ {{ trans('dashboard.team.team') }} @if($current_user->isAdmin) - - {{ trans('dashboard.team.add.title') }} - +
+ + {{ trans('dashboard.team.invite.title') }} + + + {{ trans('dashboard.team.add.title') }} + +
@endif
diff --git a/resources/views/dashboard/team/invite.blade.php b/resources/views/dashboard/team/invite.blade.php new file mode 100644 index 00000000..377548ee --- /dev/null +++ b/resources/views/dashboard/team/invite.blade.php @@ -0,0 +1,47 @@ +@extends('layout.dashboard') + +@section('content') +
+ + + {{ trans('dashboard.team.team') }} + +
+
+
+
+ @include('dashboard.partials.errors') +
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+@stop diff --git a/resources/views/emails/users/invite-html.blade.php b/resources/views/emails/users/invite-html.blade.php new file mode 100644 index 00000000..5b429e6d --- /dev/null +++ b/resources/views/emails/users/invite-html.blade.php @@ -0,0 +1,13 @@ +@extends('layout.emails') + +@section('preheader') +{!! trans('cachet.users.email.invite.html-preheader', ['app_name' => Setting::get('app_name')]) !!} +@stop + +@section('content') + {!! trans('cachet.users.email.invite.html', ['app_name' => Setting::get('app_name'), 'link' => $link]) !!} + + @if(Setting::get('show_support')) +

{!! trans('cachet.powered_by', ['app' => Setting::get('app_name')]) !!}

+ @endif +@stop diff --git a/resources/views/emails/users/invite-text.blade.php b/resources/views/emails/users/invite-text.blade.php new file mode 100644 index 00000000..04a4b68c --- /dev/null +++ b/resources/views/emails/users/invite-text.blade.php @@ -0,0 +1,5 @@ +{{ trans('cachet.users.email.invite.text', ['app_name' => Setting::get('app_name'), 'link' => $link]) }} + +@if(Setting::get('show_support')) +{!! trans('cachet.powered_by', ['app' => Setting::get('app_name')]) !!} +@endif