Dan Brown

Got standard form-based registration working

1 +<?php
2 +
3 +namespace Oxbow;
4 +
5 +use Illuminate\Database\Eloquent\Model;
6 +
7 +class EmailConfirmation extends Model
8 +{
9 + protected $fillable = ['user_id', 'token'];
10 +
11 + public function user()
12 + {
13 + return $this->belongsTo('Oxbow\User');
14 + }
15 +}
1 +<?php namespace Oxbow\Exceptions;
2 +
3 +
4 +class ConfirmationEmailException extends NotifyException
5 +{
6 +
7 +}
...\ No newline at end of file ...\ No newline at end of file
1 +<?php namespace Oxbow\Exceptions;
2 +
3 +
4 +class UserRegistrationException extends NotifyException
5 +{
6 +
7 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
2 2
3 namespace Oxbow\Http\Controllers\Auth; 3 namespace Oxbow\Http\Controllers\Auth;
4 4
5 +use Illuminate\Http\Request;
5 use Oxbow\Exceptions\SocialSignInException; 6 use Oxbow\Exceptions\SocialSignInException;
7 +use Oxbow\Exceptions\UserRegistrationException;
8 +use Oxbow\Repos\UserRepo;
9 +use Oxbow\Services\EmailConfirmationService;
10 +use Oxbow\Services\Facades\Setting;
6 use Oxbow\Services\SocialAuthService; 11 use Oxbow\Services\SocialAuthService;
7 use Oxbow\User; 12 use Oxbow\User;
8 use Validator; 13 use Validator;
...@@ -30,15 +35,22 @@ class AuthController extends Controller ...@@ -30,15 +35,22 @@ class AuthController extends Controller
30 protected $redirectAfterLogout = '/login'; 35 protected $redirectAfterLogout = '/login';
31 36
32 protected $socialAuthService; 37 protected $socialAuthService;
38 + protected $emailConfirmationService;
39 + protected $userRepo;
33 40
34 /** 41 /**
35 * Create a new authentication controller instance. 42 * Create a new authentication controller instance.
36 - * @param SocialAuthService $socialAuthService 43 + * @param SocialAuthService $socialAuthService
44 + * @param EmailConfirmationService $emailConfirmationService
45 + * @param UserRepo $userRepo
37 */ 46 */
38 - public function __construct(SocialAuthService $socialAuthService) 47 + public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
39 { 48 {
40 $this->middleware('guest', ['only' => ['getLogin', 'postLogin', 'getRegister']]); 49 $this->middleware('guest', ['only' => ['getLogin', 'postLogin', 'getRegister']]);
41 $this->socialAuthService = $socialAuthService; 50 $this->socialAuthService = $socialAuthService;
51 + $this->emailConfirmationService = $emailConfirmationService;
52 + $this->userRepo = $userRepo;
53 + parent::__construct();
42 } 54 }
43 55
44 /** 56 /**
...@@ -52,7 +64,7 @@ class AuthController extends Controller ...@@ -52,7 +64,7 @@ class AuthController extends Controller
52 return Validator::make($data, [ 64 return Validator::make($data, [
53 'name' => 'required|max:255', 65 'name' => 'required|max:255',
54 'email' => 'required|email|max:255|unique:users', 66 'email' => 'required|email|max:255|unique:users',
55 - 'password' => 'required|confirmed|min:6', 67 + 'password' => 'required|min:6',
56 ]); 68 ]);
57 } 69 }
58 70
...@@ -71,6 +83,13 @@ class AuthController extends Controller ...@@ -71,6 +83,13 @@ class AuthController extends Controller
71 ]); 83 ]);
72 } 84 }
73 85
86 + protected function checkRegistrationAllowed()
87 + {
88 + if(!\Setting::get('registration-enabled')) {
89 + throw new UserRegistrationException('Registrations are currently disabled.', '/login');
90 + }
91 + }
92 +
74 /** 93 /**
75 * Show the application registration form. 94 * Show the application registration form.
76 * 95 *
...@@ -78,11 +97,105 @@ class AuthController extends Controller ...@@ -78,11 +97,105 @@ class AuthController extends Controller
78 */ 97 */
79 public function getRegister() 98 public function getRegister()
80 { 99 {
100 + $this->checkRegistrationAllowed();
81 $socialDrivers = $this->socialAuthService->getActiveDrivers(); 101 $socialDrivers = $this->socialAuthService->getActiveDrivers();
82 return view('auth.register', ['socialDrivers' => $socialDrivers]); 102 return view('auth.register', ['socialDrivers' => $socialDrivers]);
83 } 103 }
84 104
85 /** 105 /**
106 + * Handle a registration request for the application.
107 + *
108 + * @param \Illuminate\Http\Request $request
109 + * @return \Illuminate\Http\Response
110 + * @throws UserRegistrationException
111 + */
112 + public function postRegister(Request $request)
113 + {
114 + $this->checkRegistrationAllowed();
115 + $validator = $this->validator($request->all());
116 +
117 + if ($validator->fails()) {
118 + $this->throwValidationException(
119 + $request, $validator
120 + );
121 + }
122 +
123 + if(\Setting::get('registration-restrict')) {
124 + $restrictedEmailDomains = explode(',', str_replace(' ', '', \Setting::get('registration-restrict')));
125 + $userEmailDomain = $domain = substr(strrchr($request->get('email'), "@"), 1);
126 + if(!in_array($userEmailDomain, $restrictedEmailDomains)) {
127 + throw new UserRegistrationException('That email domain does not have access to this application', '/register');
128 + }
129 + }
130 +
131 + $newUser = $this->create($request->all());
132 + $newUser->attachRoleId(\Setting::get('registration-role'), 1);
133 +
134 + if(\Setting::get('registration-confirmation') || \Setting::get('registration-restrict')) {
135 + $newUser->email_confirmed = false;
136 + $newUser->save();
137 + $this->emailConfirmationService->sendConfirmation($newUser);
138 + return redirect('/register/confirm');
139 + }
140 +
141 + auth()->login($newUser);
142 + return redirect($this->redirectPath());
143 + }
144 +
145 + /**
146 + * Show the page to tell the user to check thier email
147 + * and confirm their address.
148 + */
149 + public function getRegisterConfirmation()
150 + {
151 + return view('auth/register-confirm');
152 + }
153 +
154 + /**
155 + * Confirms an email via a token and logs the user into the system.
156 + * @param $token
157 + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
158 + * @throws UserRegistrationException
159 + */
160 + public function confirmEmail($token)
161 + {
162 + $confirmation = $this->emailConfirmationService->getEmailConfirmationFromToken($token);
163 + $user = $confirmation->user;
164 + $user->email_confirmed = true;
165 + $user->save();
166 + auth()->login($confirmation->user);
167 + session()->flash('success', 'Your email has been confirmed!');
168 + $this->emailConfirmationService->deleteConfirmationsByUser($user);
169 + return redirect($this->redirectPath);
170 + }
171 +
172 + /**
173 + * Shows a notice that a user's email address has not been confirmed,
174 + * Also has the option to re-send the confirmation email.
175 + * @return \Illuminate\View\View
176 + */
177 + public function showAwaitingConfirmation()
178 + {
179 + return view('auth/user-unconfirmed');
180 + }
181 +
182 + /**
183 + * Resend the confirmation email
184 + * @param Request $request
185 + * @return \Illuminate\View\View
186 + */
187 + public function resendConfirmation(Request $request)
188 + {
189 + $this->validate($request, [
190 + 'email' => 'required|email|exists:users,email'
191 + ]);
192 + $user = $this->userRepo->getByEmail($request->get('email'));
193 + $this->emailConfirmationService->sendConfirmation($user);
194 + \Session::flash('success', 'Confirmation email resent, Please check your inbox.');
195 + return redirect('/register/confirm');
196 + }
197 +
198 + /**
86 * Show the application login form. 199 * Show the application login form.
87 * 200 *
88 * @return \Illuminate\Http\Response 201 * @return \Illuminate\Http\Response
......
...@@ -38,6 +38,7 @@ class SettingController extends Controller ...@@ -38,6 +38,7 @@ class SettingController extends Controller
38 $key = str_replace('setting-', '', trim($name)); 38 $key = str_replace('setting-', '', trim($name));
39 Setting::put($key, $value); 39 Setting::put($key, $value);
40 } 40 }
41 + session()->flash('success', 'Settings Saved');
41 return redirect('/settings'); 42 return redirect('/settings');
42 } 43 }
43 44
......
...@@ -118,7 +118,6 @@ class UserController extends Controller ...@@ -118,7 +118,6 @@ class UserController extends Controller
118 } 118 }
119 119
120 if ($request->has('password') && $request->get('password') != '') { 120 if ($request->has('password') && $request->get('password') != '') {
121 - //dd('cat');
122 $password = $request->get('password'); 121 $password = $request->get('password');
123 $user->password = bcrypt($password); 122 $user->password = bcrypt($password);
124 } 123 }
......
...@@ -4,6 +4,7 @@ namespace Oxbow\Http\Middleware; ...@@ -4,6 +4,7 @@ namespace Oxbow\Http\Middleware;
4 4
5 use Closure; 5 use Closure;
6 use Illuminate\Contracts\Auth\Guard; 6 use Illuminate\Contracts\Auth\Guard;
7 +use Oxbow\Exceptions\UserRegistrationException;
7 use Setting; 8 use Setting;
8 9
9 class Authenticate 10 class Authenticate
...@@ -34,6 +35,9 @@ class Authenticate ...@@ -34,6 +35,9 @@ class Authenticate
34 */ 35 */
35 public function handle($request, Closure $next) 36 public function handle($request, Closure $next)
36 { 37 {
38 + if(auth()->check() && auth()->user()->email_confirmed == false) {
39 + return redirect()->guest('/register/confirm/awaiting');
40 + }
37 if ($this->auth->guest() && !Setting::get('app-public')) { 41 if ($this->auth->guest() && !Setting::get('app-public')) {
38 if ($request->ajax()) { 42 if ($request->ajax()) {
39 return response('Unauthorized.', 401); 43 return response('Unauthorized.', 401);
......
...@@ -88,6 +88,11 @@ Route::get('/login', 'Auth\AuthController@getLogin'); ...@@ -88,6 +88,11 @@ Route::get('/login', 'Auth\AuthController@getLogin');
88 Route::post('/login', 'Auth\AuthController@postLogin'); 88 Route::post('/login', 'Auth\AuthController@postLogin');
89 Route::get('/logout', 'Auth\AuthController@getLogout'); 89 Route::get('/logout', 'Auth\AuthController@getLogout');
90 Route::get('/register', 'Auth\AuthController@getRegister'); 90 Route::get('/register', 'Auth\AuthController@getRegister');
91 +Route::get('/register/confirm', 'Auth\AuthController@getRegisterConfirmation');
92 +Route::get('/register/confirm/awaiting', 'Auth\AuthController@showAwaitingConfirmation');
93 +Route::post('/register/confirm/resend', 'Auth\AuthController@resendConfirmation');
94 +Route::get('/register/confirm/{token}', 'Auth\AuthController@confirmEmail');
95 +Route::post('/register', 'Auth\AuthController@postRegister');
91 96
92 // Password reset link request routes... 97 // Password reset link request routes...
93 Route::get('/password/email', 'Auth\PasswordController@getEmail'); 98 Route::get('/password/email', 'Auth\PasswordController@getEmail');
......
...@@ -21,4 +21,9 @@ class UserRepo ...@@ -21,4 +21,9 @@ class UserRepo
21 public function getByEmail($email) { 21 public function getByEmail($email) {
22 return $this->user->where('email', '=', $email)->first(); 22 return $this->user->where('email', '=', $email)->first();
23 } 23 }
24 +
25 + public function getById($id)
26 + {
27 + return $this->user->findOrFail($id);
28 + }
24 } 29 }
...\ No newline at end of file ...\ No newline at end of file
......
1 +<?php namespace Oxbow\Services;
2 +
3 +
4 +use Carbon\Carbon;
5 +use Illuminate\Contracts\Mail\Mailer;
6 +use Illuminate\Mail\Message;
7 +use Oxbow\EmailConfirmation;
8 +use Oxbow\Exceptions\ConfirmationEmailException;
9 +use Oxbow\Exceptions\UserRegistrationException;
10 +use Oxbow\Repos\UserRepo;
11 +use Oxbow\Setting;
12 +use Oxbow\User;
13 +
14 +class EmailConfirmationService
15 +{
16 + protected $mailer;
17 + protected $emailConfirmation;
18 +
19 + /**
20 + * EmailConfirmationService constructor.
21 + * @param Mailer $mailer
22 + * @param EmailConfirmation $emailConfirmation
23 + */
24 + public function __construct(Mailer $mailer, EmailConfirmation $emailConfirmation)
25 + {
26 + $this->mailer = $mailer;
27 + $this->emailConfirmation = $emailConfirmation;
28 + }
29 +
30 + /**
31 + * Create new confirmation for a user,
32 + * Also removes any existing old ones.
33 + * @param User $user
34 + * @throws ConfirmationEmailException
35 + */
36 + public function sendConfirmation(User $user)
37 + {
38 + if($user->email_confirmed) {
39 + throw new ConfirmationEmailException('Email has already been confirmed, Try logging in.', '/login');
40 + }
41 + $this->deleteConfirmationsByUser($user);
42 + $token = $this->getToken();
43 + $this->emailConfirmation->create([
44 + 'user_id' => $user->id,
45 + 'token' => $token,
46 + ]);
47 + $this->mailer->send('emails/email-confirmation', ['token' => $token], function (Message $message) use ($user) {
48 + $appName = \Setting::get('app-name', 'BookStack');
49 + $message->to($user->email, $user->name)->subject('Confirm your email on ' . $appName . '.');
50 + });
51 + }
52 +
53 + /**
54 + * Gets an email confirmation by looking up the token,
55 + * Ensures the token has not expired.
56 + * @param string $token
57 + * @return EmailConfirmation
58 + * @throws UserRegistrationException
59 + */
60 + public function getEmailConfirmationFromToken($token)
61 + {
62 + $emailConfirmation = $this->emailConfirmation->where('token', '=', $token)->first();
63 + // If not found
64 + if ($emailConfirmation === null) {
65 + throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register');
66 + }
67 +
68 + // If more than a day old
69 + if(Carbon::now()->subDay()->gt($emailConfirmation->created_at)) {
70 + $this->sendConfirmation($emailConfirmation->user);
71 + throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm');
72 + }
73 +
74 + return $emailConfirmation;
75 + }
76 +
77 +
78 + /**
79 + * Delete all email confirmations that belong to a user.
80 + * @param User $user
81 + * @return mixed
82 + */
83 + public function deleteConfirmationsByUser(User $user)
84 + {
85 + return $this->emailConfirmation->where('user_id', '=', $user->id)->delete();
86 + }
87 +
88 + /**
89 + * Creates a unique token within the email confirmation database.
90 + * @return string
91 + */
92 + protected function getToken()
93 + {
94 + $token = str_random(24);
95 + while ($this->emailConfirmation->where('token', '=', $token)->exists()) {
96 + $token = str_random(25);
97 + }
98 + return $token;
99 + }
100 +
101 +
102 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -14,8 +14,8 @@ class CreateSocialAccountsTable extends Migration ...@@ -14,8 +14,8 @@ class CreateSocialAccountsTable extends Migration
14 { 14 {
15 Schema::create('social_accounts', function (Blueprint $table) { 15 Schema::create('social_accounts', function (Blueprint $table) {
16 $table->increments('id'); 16 $table->increments('id');
17 - $table->integer('user_id')->indexed(); 17 + $table->integer('user_id')->index();
18 - $table->string('driver')->indexed(); 18 + $table->string('driver')->index();
19 $table->string('driver_id'); 19 $table->string('driver_id');
20 $table->string('avatar'); 20 $table->string('avatar');
21 $table->timestamps(); 21 $table->timestamps();
......
1 +<?php
2 +
3 +use Illuminate\Database\Schema\Blueprint;
4 +use Illuminate\Database\Migrations\Migration;
5 +
6 +class AddEmailConfirmationTable extends Migration
7 +{
8 + /**
9 + * Run the migrations.
10 + *
11 + * @return void
12 + */
13 + public function up()
14 + {
15 + Schema::table('users', function (Blueprint $table) {
16 + $table->boolean('email_confirmed')->default(true);
17 + });
18 +
19 + Schema::create('email_confirmations', function (Blueprint $table) {
20 + $table->increments('id');
21 + $table->integer('user_id')->index();
22 + $table->string('token')->index();
23 + $table->timestamps();
24 + });
25 + }
26 +
27 + /**
28 + * Reverse the migrations.
29 + *
30 + * @return void
31 + */
32 + public function down()
33 + {
34 + Schema::table('users', function (Blueprint $table) {
35 + $table->dropColumn('email_confirmed');
36 + });
37 + Schema::drop('email_confirmations');
38 + }
39 +}
1 +@extends('public')
2 +
3 +@section('header-buttons')
4 + @if(!$signedIn)
5 + <a href="/login"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
6 + @endif
7 +@stop
8 +
9 +@section('content')
10 +
11 + <div class="text-center">
12 + <div class="center-box">
13 + <h2>Thanks for registering!</h2>
14 + <p>Please check your email and click the confirmation button to access {{ \Setting::get('app-name') }}.</p>
15 + </div>
16 + </div>
17 +
18 +
19 +@stop
...@@ -8,9 +8,9 @@ ...@@ -8,9 +8,9 @@
8 8
9 <div class="text-center"> 9 <div class="text-center">
10 <div class="center-box"> 10 <div class="center-box">
11 - <h1>Register</h1> 11 + <h1>Sign Up</h1>
12 12
13 - <form action="/login" method="POST"> 13 + <form action="/register" method="POST">
14 {!! csrf_field() !!} 14 {!! csrf_field() !!}
15 15
16 <div class="form-group"> 16 <div class="form-group">
...@@ -29,13 +29,14 @@ ...@@ -29,13 +29,14 @@
29 </div> 29 </div>
30 30
31 <div class="from-group"> 31 <div class="from-group">
32 - <button class="button block pos">Sign In</button> 32 + <button class="button block pos">Create Account</button>
33 </div> 33 </div>
34 </form> 34 </form>
35 35
36 @if(count($socialDrivers) > 0) 36 @if(count($socialDrivers) > 0)
37 <hr class="margin-top"> 37 <hr class="margin-top">
38 <h3 class="text-muted">Social Registration</h3> 38 <h3 class="text-muted">Social Registration</h3>
39 + <p class="text-small">Register and sign in using another service.</p>
39 @if(isset($socialDrivers['google'])) 40 @if(isset($socialDrivers['google']))
40 <a href="/register/service/google" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a> 41 <a href="/register/service/google" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
41 @endif 42 @endif
......
1 +@extends('public')
2 +
3 +@section('content')
4 +
5 + <div class="row">
6 + <div class="col-md-6 col-md-offset-3">
7 + <h2>Email Address not confirmed</h2>
8 + <p class="text-muted">Your email address has not yet been confirmed. <br>
9 + Please click the link in the email that was sent shortly after you registered. <br>
10 + If you cannot find the email you can re-send the confirmation email by submitting the form below.
11 + </p>
12 + <hr>
13 + <form action="/register/confirm/resend" method="POST">
14 + {!! csrf_field() !!}
15 + <div class="form-group">
16 + <label for="email">Email Address</label>
17 + @if(auth()->check())
18 + @include('form/text', ['name' => 'email', 'model' => auth()->user()])
19 + @else
20 + @include('form/text', ['name' => 'email'])
21 + @endif
22 + </div>
23 + <div class="form-group">
24 + <button type="submit" class="button pos">Resend Confirmation Email</button>
25 + </div>
26 + </form>
27 + </div>
28 + </div>
29 +
30 +@stop
...@@ -157,7 +157,7 @@ ...@@ -157,7 +157,7 @@
157 <table style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;"> 157 <table style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;">
158 <tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;"> 158 <tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
159 <td class="padding" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;padding-top:10px;padding-bottom:10px;padding-right:0;padding-left:0;"> 159 <td class="padding" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;padding-top:10px;padding-bottom:10px;padding-right:0;padding-left:0;">
160 - <p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;"><a class="btn-primary" href="{{ url('user/confirm/'.$token) }}" style="margin-top:0;margin-bottom:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;text-decoration:none;color:#FFF;background-color:#348eda;border-style:solid;border-color:#348eda;border-width:10px 20px;line-height:2;font-weight:bold;margin-right:10px;text-align:center;cursor:pointer;display:inline-block;border-radius:4px;">Confirm Email</a></p> 160 + <p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;"><a class="btn-primary" href="{{ url('/register/confirm/'.$token) }}" style="margin-top:0;margin-bottom:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;text-decoration:none;color:#FFF;background-color:#348eda;border-style:solid;border-color:#348eda;border-width:10px 20px;line-height:2;font-weight:bold;margin-right:10px;text-align:center;cursor:pointer;display:inline-block;border-radius:4px;">Confirm Email</a></p>
161 </td> 161 </td>
162 </tr> 162 </tr>
163 </table> 163 </table>
......
...@@ -37,6 +37,19 @@ ...@@ -37,6 +37,19 @@
37 <div class="links text-center"> 37 <div class="links text-center">
38 @yield('header-buttons') 38 @yield('header-buttons')
39 </div> 39 </div>
40 + @if($signedIn)
41 + <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
42 + <div class="dropdown-container" data-dropdown>
43 + <span class="user-name" data-dropdown-toggle>
44 + {{ $currentUser->name }} <i class="zmdi zmdi-caret-down"></i>
45 + </span>
46 + <ul>
47 + <li>
48 + <a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a>
49 + </li>
50 + </ul>
51 + </div>
52 + @endif
40 </div> 53 </div>
41 </div> 54 </div>
42 </div> 55 </div>
......