Dan Brown
Committed by GitHub

Laravel 5.3 upgrade (#189)

* Started move to laravel 5.3

* Started updating login & registration flows for laravel 5.3 update

* Updated app emails to notification system

* Fixed registations bugs and removed email confirmation model

* Fixed large portion of laravel post-upgrade issues

* Fixed and tested LDAP process
Showing 44 changed files with 909 additions and 541 deletions
...@@ -10,4 +10,5 @@ Homestead.yaml ...@@ -10,4 +10,5 @@ Homestead.yaml
10 /public/bower 10 /public/bower
11 /storage/images 11 /storage/images
12 _ide_helper.php 12 _ide_helper.php
13 -/storage/debugbar
...\ No newline at end of file ...\ No newline at end of file
13 +/storage/debugbar
14 +.phpstorm.meta.php
...\ No newline at end of file ...\ No newline at end of file
......
1 -<?php namespace BookStack;
2 -
3 -class EmailConfirmation extends Model
4 -{
5 - protected $fillable = ['user_id', 'token'];
6 -
7 - /**
8 - * Get the user that this confirmation is attached to.
9 - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
10 - */
11 - public function user()
12 - {
13 - return $this->belongsTo(User::class);
14 - }
15 -
16 -}
1 -<?php
2 -
3 -namespace BookStack\Events;
4 -
5 -abstract class Event
6 -{
7 - //
8 -}
...@@ -87,4 +87,20 @@ class Handler extends ExceptionHandler ...@@ -87,4 +87,20 @@ class Handler extends ExceptionHandler
87 } while ($e = $e->getPrevious()); 87 } while ($e = $e->getPrevious());
88 return $message; 88 return $message;
89 } 89 }
90 +
91 + /**
92 + * Convert an authentication exception into an unauthenticated response.
93 + *
94 + * @param \Illuminate\Http\Request $request
95 + * @param \Illuminate\Auth\AuthenticationException $exception
96 + * @return \Illuminate\Http\Response
97 + */
98 + protected function unauthenticated($request, AuthenticationException $exception)
99 + {
100 + if ($request->expectsJson()) {
101 + return response()->json(['error' => 'Unauthenticated.'], 401);
102 + }
103 +
104 + return redirect()->guest('login');
105 + }
90 } 106 }
......
1 +<?php
2 +
3 +namespace BookStack\Http\Controllers\Auth;
4 +
5 +use BookStack\Http\Controllers\Controller;
6 +use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
7 +
8 +class ForgotPasswordController extends Controller
9 +{
10 + /*
11 + |--------------------------------------------------------------------------
12 + | Password Reset Controller
13 + |--------------------------------------------------------------------------
14 + |
15 + | This controller is responsible for handling password reset emails and
16 + | includes a trait which assists in sending these notifications from
17 + | your application to your users. Feel free to explore this trait.
18 + |
19 + */
20 +
21 + use SendsPasswordResetEmails;
22 +
23 + /**
24 + * Create a new controller instance.
25 + *
26 + * @return void
27 + */
28 + public function __construct()
29 + {
30 + $this->middleware('guest');
31 + parent::__construct();
32 + }
33 +}
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +
3 +namespace BookStack\Http\Controllers\Auth;
4 +
5 +use BookStack\Http\Controllers\Controller;
6 +use BookStack\Repos\UserRepo;
7 +use BookStack\Services\SocialAuthService;
8 +use Illuminate\Contracts\Auth\Authenticatable;
9 +use Illuminate\Foundation\Auth\AuthenticatesUsers;
10 +use Illuminate\Http\Request;
11 +
12 +class LoginController extends Controller
13 +{
14 + /*
15 + |--------------------------------------------------------------------------
16 + | Login Controller
17 + |--------------------------------------------------------------------------
18 + |
19 + | This controller handles authenticating users for the application and
20 + | redirecting them to your home screen. The controller uses a trait
21 + | to conveniently provide its functionality to your applications.
22 + |
23 + */
24 +
25 + use AuthenticatesUsers;
26 +
27 + /**
28 + * Where to redirect users after login.
29 + *
30 + * @var string
31 + */
32 + protected $redirectTo = '/';
33 +
34 + protected $redirectPath = '/';
35 + protected $redirectAfterLogout = '/login';
36 +
37 + protected $socialAuthService;
38 + protected $userRepo;
39 +
40 + /**
41 + * Create a new controller instance.
42 + *
43 + * @param SocialAuthService $socialAuthService
44 + * @param UserRepo $userRepo
45 + */
46 + public function __construct(SocialAuthService $socialAuthService, UserRepo $userRepo)
47 + {
48 + $this->middleware('guest', ['only' => ['getLogin', 'postLogin']]);
49 + $this->socialAuthService = $socialAuthService;
50 + $this->userRepo = $userRepo;
51 + $this->redirectPath = baseUrl('/');
52 + $this->redirectAfterLogout = baseUrl('/login');
53 + parent::__construct();
54 + }
55 +
56 + public function username()
57 + {
58 + return config('auth.method') === 'standard' ? 'email' : 'username';
59 + }
60 +
61 + /**
62 + * Overrides the action when a user is authenticated.
63 + * If the user authenticated but does not exist in the user table we create them.
64 + * @param Request $request
65 + * @param Authenticatable $user
66 + * @return \Illuminate\Http\RedirectResponse
67 + * @throws AuthException
68 + */
69 + protected function authenticated(Request $request, Authenticatable $user)
70 + {
71 + // Explicitly log them out for now if they do no exist.
72 + if (!$user->exists) auth()->logout($user);
73 +
74 + if (!$user->exists && $user->email === null && !$request->has('email')) {
75 + $request->flash();
76 + session()->flash('request-email', true);
77 + return redirect('/login');
78 + }
79 +
80 + if (!$user->exists && $user->email === null && $request->has('email')) {
81 + $user->email = $request->get('email');
82 + }
83 +
84 + if (!$user->exists) {
85 +
86 + // Check for users with same email already
87 + $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
88 + if ($alreadyUser) {
89 + throw new AuthException('A user with the email ' . $user->email . ' already exists but with different credentials.');
90 + }
91 +
92 + $user->save();
93 + $this->userRepo->attachDefaultRole($user);
94 + auth()->login($user);
95 + }
96 +
97 + $path = session()->pull('url.intended', '/');
98 + $path = baseUrl($path, true);
99 + return redirect($path);
100 + }
101 +
102 + /**
103 + * Show the application login form.
104 + * @return \Illuminate\Http\Response
105 + */
106 + public function getLogin()
107 + {
108 + $socialDrivers = $this->socialAuthService->getActiveDrivers();
109 + $authMethod = config('auth.method');
110 + return view('auth/login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]);
111 + }
112 +
113 + /**
114 + * Redirect to the relevant social site.
115 + * @param $socialDriver
116 + * @return \Symfony\Component\HttpFoundation\RedirectResponse
117 + */
118 + public function getSocialLogin($socialDriver)
119 + {
120 + session()->put('social-callback', 'login');
121 + return $this->socialAuthService->startLogIn($socialDriver);
122 + }
123 +}
...\ No newline at end of file ...\ No newline at end of file
1 -<?php namespace BookStack\Http\Controllers\Auth; 1 +<?php
2 2
3 -use BookStack\Exceptions\AuthException; 3 +namespace BookStack\Http\Controllers\Auth;
4 -use Illuminate\Contracts\Auth\Authenticatable; 4 +
5 -use Illuminate\Http\Request; 5 +use BookStack\Exceptions\ConfirmationEmailException;
6 -use BookStack\Exceptions\SocialSignInException;
7 use BookStack\Exceptions\UserRegistrationException; 6 use BookStack\Exceptions\UserRegistrationException;
8 use BookStack\Repos\UserRepo; 7 use BookStack\Repos\UserRepo;
9 use BookStack\Services\EmailConfirmationService; 8 use BookStack\Services\EmailConfirmationService;
10 use BookStack\Services\SocialAuthService; 9 use BookStack\Services\SocialAuthService;
11 -use BookStack\SocialAccount; 10 +use BookStack\User;
11 +use Illuminate\Http\Request;
12 +use Illuminate\Http\Response;
12 use Validator; 13 use Validator;
13 use BookStack\Http\Controllers\Controller; 14 use BookStack\Http\Controllers\Controller;
14 -use Illuminate\Foundation\Auth\ThrottlesLogins; 15 +use Illuminate\Foundation\Auth\RegistersUsers;
15 -use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
16 16
17 -class AuthController extends Controller 17 +class RegisterController extends Controller
18 { 18 {
19 /* 19 /*
20 |-------------------------------------------------------------------------- 20 |--------------------------------------------------------------------------
21 - | Registration & Login Controller 21 + | Register Controller
22 |-------------------------------------------------------------------------- 22 |--------------------------------------------------------------------------
23 | 23 |
24 - | This controller handles the registration of new users, as well as the 24 + | This controller handles the registration of new users as well as their
25 - | authentication of existing users. By default, this controller uses 25 + | validation and creation. By default this controller uses a trait to
26 - | a simple trait to add these behaviors. Why don't you explore it? 26 + | provide this functionality without requiring any additional code.
27 | 27 |
28 */ 28 */
29 29
30 - use AuthenticatesAndRegistersUsers, ThrottlesLogins; 30 + use RegistersUsers;
31 -
32 - protected $redirectPath = '/';
33 - protected $redirectAfterLogout = '/login';
34 - protected $username = 'email';
35 31
36 protected $socialAuthService; 32 protected $socialAuthService;
37 protected $emailConfirmationService; 33 protected $emailConfirmationService;
38 protected $userRepo; 34 protected $userRepo;
39 35
40 /** 36 /**
41 - * Create a new authentication controller instance. 37 + * Where to redirect users after login / registration.
38 + *
39 + * @var string
40 + */
41 + protected $redirectTo = '/';
42 + protected $redirectPath = '/';
43 +
44 + /**
45 + * Create a new controller instance.
46 + *
42 * @param SocialAuthService $socialAuthService 47 * @param SocialAuthService $socialAuthService
43 * @param EmailConfirmationService $emailConfirmationService 48 * @param EmailConfirmationService $emailConfirmationService
44 * @param UserRepo $userRepo 49 * @param UserRepo $userRepo
45 */ 50 */
46 public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo) 51 public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
47 { 52 {
48 - $this->middleware('guest', ['only' => ['getLogin', 'postLogin', 'getRegister', 'postRegister']]); 53 + $this->middleware('guest');
49 $this->socialAuthService = $socialAuthService; 54 $this->socialAuthService = $socialAuthService;
50 $this->emailConfirmationService = $emailConfirmationService; 55 $this->emailConfirmationService = $emailConfirmationService;
51 $this->userRepo = $userRepo; 56 $this->userRepo = $userRepo;
57 + $this->redirectTo = baseUrl('/');
52 $this->redirectPath = baseUrl('/'); 58 $this->redirectPath = baseUrl('/');
53 - $this->redirectAfterLogout = baseUrl('/login');
54 $this->username = config('auth.method') === 'standard' ? 'email' : 'username'; 59 $this->username = config('auth.method') === 'standard' ? 'email' : 'username';
55 parent::__construct(); 60 parent::__construct();
56 } 61 }
57 62
58 /** 63 /**
59 * Get a validator for an incoming registration request. 64 * Get a validator for an incoming registration request.
65 + *
60 * @param array $data 66 * @param array $data
61 * @return \Illuminate\Contracts\Validation\Validator 67 * @return \Illuminate\Contracts\Validation\Validator
62 */ 68 */
...@@ -69,6 +75,10 @@ class AuthController extends Controller ...@@ -69,6 +75,10 @@ class AuthController extends Controller
69 ]); 75 ]);
70 } 76 }
71 77
78 + /**
79 + * Check whether or not registrations are allowed in the app settings.
80 + * @throws UserRegistrationException
81 + */
72 protected function checkRegistrationAllowed() 82 protected function checkRegistrationAllowed()
73 { 83 {
74 if (!setting('registration-enabled')) { 84 if (!setting('registration-enabled')) {
...@@ -78,7 +88,7 @@ class AuthController extends Controller ...@@ -78,7 +88,7 @@ class AuthController extends Controller
78 88
79 /** 89 /**
80 * Show the application registration form. 90 * Show the application registration form.
81 - * @return \Illuminate\Http\Response 91 + * @return Response
82 */ 92 */
83 public function getRegister() 93 public function getRegister()
84 { 94 {
...@@ -89,9 +99,10 @@ class AuthController extends Controller ...@@ -89,9 +99,10 @@ class AuthController extends Controller
89 99
90 /** 100 /**
91 * Handle a registration request for the application. 101 * Handle a registration request for the application.
92 - * @param \Illuminate\Http\Request $request 102 + * @param Request|\Illuminate\Http\Request $request
93 - * @return \Illuminate\Http\Response 103 + * @return Response
94 * @throws UserRegistrationException 104 * @throws UserRegistrationException
105 + * @throws \Illuminate\Foundation\Validation\ValidationException
95 */ 106 */
96 public function postRegister(Request $request) 107 public function postRegister(Request $request)
97 { 108 {
...@@ -108,66 +119,18 @@ class AuthController extends Controller ...@@ -108,66 +119,18 @@ class AuthController extends Controller
108 return $this->registerUser($userData); 119 return $this->registerUser($userData);
109 } 120 }
110 121
111 -
112 - /**
113 - * Overrides the action when a user is authenticated.
114 - * If the user authenticated but does not exist in the user table we create them.
115 - * @param Request $request
116 - * @param Authenticatable $user
117 - * @return \Illuminate\Http\RedirectResponse
118 - * @throws AuthException
119 - */
120 - protected function authenticated(Request $request, Authenticatable $user)
121 - {
122 - // Explicitly log them out for now if they do no exist.
123 - if (!$user->exists) auth()->logout($user);
124 -
125 - if (!$user->exists && $user->email === null && !$request->has('email')) {
126 - $request->flash();
127 - session()->flash('request-email', true);
128 - return redirect('/login');
129 - }
130 -
131 - if (!$user->exists && $user->email === null && $request->has('email')) {
132 - $user->email = $request->get('email');
133 - }
134 -
135 - if (!$user->exists) {
136 -
137 - // Check for users with same email already
138 - $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
139 - if ($alreadyUser) {
140 - throw new AuthException('A user with the email ' . $user->email . ' already exists but with different credentials.');
141 - }
142 -
143 - $user->save();
144 - $this->userRepo->attachDefaultRole($user);
145 - auth()->login($user);
146 - }
147 -
148 - $path = session()->pull('url.intended', '/');
149 - $path = baseUrl($path, true);
150 - return redirect($path);
151 - }
152 -
153 /** 122 /**
154 - * Register a new user after a registration callback. 123 + * Create a new user instance after a valid registration.
155 - * @param $socialDriver 124 + * @param array $data
156 - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 125 + * @return User
157 - * @throws UserRegistrationException
158 */ 126 */
159 - protected function socialRegisterCallback($socialDriver) 127 + protected function create(array $data)
160 { 128 {
161 - $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver); 129 + return User::create([
162 - $socialAccount = $this->socialAuthService->fillSocialAccount($socialDriver, $socialUser); 130 + 'name' => $data['name'],
163 - 131 + 'email' => $data['email'],
164 - // Create an array of the user data to create a new user instance 132 + 'password' => bcrypt($data['password']),
165 - $userData = [ 133 + ]);
166 - 'name' => $socialUser->getName(),
167 - 'email' => $socialUser->getEmail(),
168 - 'password' => str_random(30)
169 - ];
170 - return $this->registerUser($userData, $socialAccount);
171 } 134 }
172 135
173 /** 136 /**
...@@ -176,7 +139,7 @@ class AuthController extends Controller ...@@ -176,7 +139,7 @@ class AuthController extends Controller
176 * @param bool|false|SocialAccount $socialAccount 139 * @param bool|false|SocialAccount $socialAccount
177 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 140 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
178 * @throws UserRegistrationException 141 * @throws UserRegistrationException
179 - * @throws \BookStack\Exceptions\ConfirmationEmailException 142 + * @throws ConfirmationEmailException
180 */ 143 */
181 protected function registerUser(array $userData, $socialAccount = false) 144 protected function registerUser(array $userData, $socialAccount = false)
182 { 145 {
...@@ -214,18 +177,6 @@ class AuthController extends Controller ...@@ -214,18 +177,6 @@ class AuthController extends Controller
214 } 177 }
215 178
216 /** 179 /**
217 - * View the confirmation email as a standard web page.
218 - * @param $token
219 - * @return \Illuminate\View\View
220 - * @throws UserRegistrationException
221 - */
222 - public function viewConfirmEmail($token)
223 - {
224 - $confirmation = $this->emailConfirmationService->getEmailConfirmationFromToken($token);
225 - return view('emails/email-confirmation', ['token' => $confirmation->token]);
226 - }
227 -
228 - /**
229 * Confirms an email via a token and logs the user into the system. 180 * Confirms an email via a token and logs the user into the system.
230 * @param $token 181 * @param $token
231 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 182 * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
...@@ -237,7 +188,7 @@ class AuthController extends Controller ...@@ -237,7 +188,7 @@ class AuthController extends Controller
237 $user = $confirmation->user; 188 $user = $confirmation->user;
238 $user->email_confirmed = true; 189 $user->email_confirmed = true;
239 $user->save(); 190 $user->save();
240 - auth()->login($confirmation->user); 191 + auth()->login($user);
241 session()->flash('success', 'Your email has been confirmed!'); 192 session()->flash('success', 'Your email has been confirmed!');
242 $this->emailConfirmationService->deleteConfirmationsByUser($user); 193 $this->emailConfirmationService->deleteConfirmationsByUser($user);
243 return redirect($this->redirectPath); 194 return redirect($this->redirectPath);
...@@ -270,28 +221,6 @@ class AuthController extends Controller ...@@ -270,28 +221,6 @@ class AuthController extends Controller
270 } 221 }
271 222
272 /** 223 /**
273 - * Show the application login form.
274 - * @return \Illuminate\Http\Response
275 - */
276 - public function getLogin()
277 - {
278 - $socialDrivers = $this->socialAuthService->getActiveDrivers();
279 - $authMethod = config('auth.method');
280 - return view('auth/login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]);
281 - }
282 -
283 - /**
284 - * Redirect to the relevant social site.
285 - * @param $socialDriver
286 - * @return \Symfony\Component\HttpFoundation\RedirectResponse
287 - */
288 - public function getSocialLogin($socialDriver)
289 - {
290 - session()->put('social-callback', 'login');
291 - return $this->socialAuthService->startLogIn($socialDriver);
292 - }
293 -
294 - /**
295 * Redirect to the social site for authentication intended to register. 224 * Redirect to the social site for authentication intended to register.
296 * @param $socialDriver 225 * @param $socialDriver
297 * @return mixed 226 * @return mixed
...@@ -334,4 +263,25 @@ class AuthController extends Controller ...@@ -334,4 +263,25 @@ class AuthController extends Controller
334 return $this->socialAuthService->detachSocialAccount($socialDriver); 263 return $this->socialAuthService->detachSocialAccount($socialDriver);
335 } 264 }
336 265
337 -} 266 + /**
267 + * Register a new user after a registration callback.
268 + * @param $socialDriver
269 + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
270 + * @throws UserRegistrationException
271 + */
272 + protected function socialRegisterCallback($socialDriver)
273 + {
274 + $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver);
275 + $socialAccount = $this->socialAuthService->fillSocialAccount($socialDriver, $socialUser);
276 +
277 + // Create an array of the user data to create a new user instance
278 + $userData = [
279 + 'name' => $socialUser->getName(),
280 + 'email' => $socialUser->getEmail(),
281 + 'password' => str_random(30)
282 + ];
283 + return $this->registerUser($userData, $socialAccount);
284 + }
285 +
286 +
287 +}
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -5,7 +5,7 @@ namespace BookStack\Http\Controllers\Auth; ...@@ -5,7 +5,7 @@ namespace BookStack\Http\Controllers\Auth;
5 use BookStack\Http\Controllers\Controller; 5 use BookStack\Http\Controllers\Controller;
6 use Illuminate\Foundation\Auth\ResetsPasswords; 6 use Illuminate\Foundation\Auth\ResetsPasswords;
7 7
8 -class PasswordController extends Controller 8 +class ResetPasswordController extends Controller
9 { 9 {
10 /* 10 /*
11 |-------------------------------------------------------------------------- 11 |--------------------------------------------------------------------------
...@@ -20,13 +20,14 @@ class PasswordController extends Controller ...@@ -20,13 +20,14 @@ class PasswordController extends Controller
20 20
21 use ResetsPasswords; 21 use ResetsPasswords;
22 22
23 - protected $redirectTo = '/';
24 -
25 /** 23 /**
26 - * Create a new password controller instance. 24 + * Create a new controller instance.
25 + *
26 + * @return void
27 */ 27 */
28 public function __construct() 28 public function __construct()
29 { 29 {
30 $this->middleware('guest'); 30 $this->middleware('guest');
31 + parent::__construct();
31 } 32 }
32 -} 33 +}
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -30,17 +30,22 @@ abstract class Controller extends BaseController ...@@ -30,17 +30,22 @@ abstract class Controller extends BaseController
30 */ 30 */
31 public function __construct() 31 public function __construct()
32 { 32 {
33 - // Get a user instance for the current user 33 + $this->middleware(function ($request, $next) {
34 - $user = auth()->user();
35 - if (!$user) $user = User::getDefault();
36 34
37 - // Share variables with views 35 + // Get a user instance for the current user
38 - view()->share('signedIn', auth()->check()); 36 + $user = auth()->user();
39 - view()->share('currentUser', $user); 37 + if (!$user) $user = User::getDefault();
40 38
41 - // Share variables with controllers 39 + // Share variables with views
42 - $this->currentUser = $user; 40 + view()->share('signedIn', auth()->check());
43 - $this->signedIn = auth()->check(); 41 + view()->share('currentUser', $user);
42 +
43 + // Share variables with controllers
44 + $this->currentUser = $user;
45 + $this->signedIn = auth()->check();
46 +
47 + return $next($request);
48 + });
44 } 49 }
45 50
46 /** 51 /**
......
...@@ -9,15 +9,32 @@ class Kernel extends HttpKernel ...@@ -9,15 +9,32 @@ class Kernel extends HttpKernel
9 /** 9 /**
10 * The application's global HTTP middleware stack. 10 * The application's global HTTP middleware stack.
11 * 11 *
12 + * These middleware are run during every request to your application.
13 + *
12 * @var array 14 * @var array
13 */ 15 */
14 protected $middleware = [ 16 protected $middleware = [
15 \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, 17 \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
16 - \BookStack\Http\Middleware\EncryptCookies::class, 18 + ];
17 - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 19 +
18 - \Illuminate\Session\Middleware\StartSession::class, 20 + /**
19 - \Illuminate\View\Middleware\ShareErrorsFromSession::class, 21 + * The application's route middleware groups.
20 - \BookStack\Http\Middleware\VerifyCsrfToken::class, 22 + *
23 + * @var array
24 + */
25 + protected $middlewareGroups = [
26 + 'web' => [
27 + \BookStack\Http\Middleware\EncryptCookies::class,
28 + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
29 + \Illuminate\Session\Middleware\StartSession::class,
30 + \Illuminate\View\Middleware\ShareErrorsFromSession::class,
31 + \BookStack\Http\Middleware\VerifyCsrfToken::class,
32 + \Illuminate\Routing\Middleware\SubstituteBindings::class,
33 + ],
34 + 'api' => [
35 + 'throttle:60,1',
36 + 'bindings',
37 + ],
21 ]; 38 ];
22 39
23 /** 40 /**
...@@ -26,6 +43,7 @@ class Kernel extends HttpKernel ...@@ -26,6 +43,7 @@ class Kernel extends HttpKernel
26 * @var array 43 * @var array
27 */ 44 */
28 protected $routeMiddleware = [ 45 protected $routeMiddleware = [
46 + 'can' => \Illuminate\Auth\Middleware\Authorize::class,
29 'auth' => \BookStack\Http\Middleware\Authenticate::class, 47 'auth' => \BookStack\Http\Middleware\Authenticate::class,
30 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 48 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
31 'guest' => \BookStack\Http\Middleware\RedirectIfAuthenticated::class, 49 'guest' => \BookStack\Http\Middleware\RedirectIfAuthenticated::class,
......
...@@ -33,7 +33,7 @@ class Authenticate ...@@ -33,7 +33,7 @@ class Authenticate
33 public function handle($request, Closure $next) 33 public function handle($request, Closure $next)
34 { 34 {
35 if ($this->auth->check() && setting('registration-confirmation') && !$this->auth->user()->email_confirmed) { 35 if ($this->auth->check() && setting('registration-confirmation') && !$this->auth->user()->email_confirmed) {
36 - return redirect()->guest(baseUrl('/register/confirm/awaiting')); 36 + return redirect(baseUrl('/register/confirm/awaiting'));
37 } 37 }
38 38
39 if ($this->auth->guest() && !setting('app-public')) { 39 if ($this->auth->guest() && !setting('app-public')) {
......
...@@ -34,7 +34,8 @@ class RedirectIfAuthenticated ...@@ -34,7 +34,8 @@ class RedirectIfAuthenticated
34 */ 34 */
35 public function handle($request, Closure $next) 35 public function handle($request, Closure $next)
36 { 36 {
37 - if ($this->auth->check()) { 37 + $requireConfirmation = setting('registration-confirmation');
38 + if ($this->auth->check() && (!$requireConfirmation || ($requireConfirmation && $this->auth->user()->email_confirmed))) {
38 return redirect('/'); 39 return redirect('/');
39 } 40 }
40 41
......
1 -<?php
2 -
3 -namespace BookStack\Jobs;
4 -
5 -use Illuminate\Bus\Queueable;
6 -
7 -abstract class Job
8 -{
9 - /*
10 - |--------------------------------------------------------------------------
11 - | Queueable Jobs
12 - |--------------------------------------------------------------------------
13 - |
14 - | This job base class provides a central location to place any logic that
15 - | is shared across all of your jobs. The trait included with the class
16 - | provides access to the "queueOn" and "delay" queue helper methods.
17 - |
18 - */
19 -
20 - use Queueable;
21 -}
1 +<?php
2 +
3 +namespace BookStack\Notifications;
4 +
5 +use Illuminate\Notifications\Notification;
6 +use Illuminate\Notifications\Messages\MailMessage;
7 +
8 +class ConfirmEmail extends Notification
9 +{
10 +
11 + public $token;
12 +
13 + /**
14 + * Create a new notification instance.
15 + * @param string $token
16 + */
17 + public function __construct($token)
18 + {
19 + $this->token = $token;
20 + }
21 +
22 + /**
23 + * Get the notification's delivery channels.
24 + *
25 + * @param mixed $notifiable
26 + * @return array
27 + */
28 + public function via($notifiable)
29 + {
30 + return ['mail'];
31 + }
32 +
33 + /**
34 + * Get the mail representation of the notification.
35 + *
36 + * @param mixed $notifiable
37 + * @return \Illuminate\Notifications\Messages\MailMessage
38 + */
39 + public function toMail($notifiable)
40 + {
41 + return (new MailMessage)
42 + ->subject('Confirm your email on ' . session('app-name'))
43 + ->greeting('Thanks for joining ' . setting('app-name') . '!')
44 + ->line('Please confirm your email address by clicking the button below:')
45 + ->action('Confirm Email', baseUrl('/register/confirm/' . $this->token));
46 + }
47 +
48 +}
1 +<?php
2 +
3 +namespace BookStack\Notifications;
4 +
5 +use Illuminate\Notifications\Notification;
6 +use Illuminate\Notifications\Messages\MailMessage;
7 +
8 +class ResetPassword extends Notification
9 +{
10 + /**
11 + * The password reset token.
12 + *
13 + * @var string
14 + */
15 + public $token;
16 +
17 + /**
18 + * Create a notification instance.
19 + *
20 + * @param string $token
21 + */
22 + public function __construct($token)
23 + {
24 + $this->token = $token;
25 + }
26 +
27 + /**
28 + * Get the notification's channels.
29 + *
30 + * @param mixed $notifiable
31 + * @return array|string
32 + */
33 + public function via($notifiable)
34 + {
35 + return ['mail'];
36 + }
37 +
38 + /**
39 + * Build the mail representation of the notification.
40 + *
41 + * @return \Illuminate\Notifications\Messages\MailMessage
42 + */
43 + public function toMail()
44 + {
45 + return (new MailMessage)
46 + ->line('You are receiving this email because we received a password reset request for your account.')
47 + ->action('Reset Password', baseUrl('password/reset/' . $this->token))
48 + ->line('If you did not request a password reset, no further action is required.');
49 + }
50 +}
1 +<?php
2 +
3 +namespace BookStack\Providers;
4 +
5 +use Illuminate\Support\ServiceProvider;
6 +use Illuminate\Support\Facades\Broadcast;
7 +
8 +class BroadcastServiceProvider extends ServiceProvider
9 +{
10 + /**
11 + * Bootstrap any application services.
12 + *
13 + * @return void
14 + */
15 + public function boot()
16 + {
17 +// Broadcast::routes();
18 +//
19 +// /*
20 +// * Authenticate the user's personal channel...
21 +// */
22 +// Broadcast::channel('BookStack.User.*', function ($user, $userId) {
23 +// return (int) $user->id === (int) $userId;
24 +// });
25 + }
26 +}
...@@ -21,13 +21,10 @@ class EventServiceProvider extends ServiceProvider ...@@ -21,13 +21,10 @@ class EventServiceProvider extends ServiceProvider
21 /** 21 /**
22 * Register any other events for your application. 22 * Register any other events for your application.
23 * 23 *
24 - * @param \Illuminate\Contracts\Events\Dispatcher $events
25 * @return void 24 * @return void
26 */ 25 */
27 - public function boot(DispatcherContract $events) 26 + public function boot()
28 { 27 {
29 - parent::boot($events); 28 + parent::boot();
30 -
31 - //
32 } 29 }
33 } 30 }
......
1 <?php namespace BookStack\Providers; 1 <?php namespace BookStack\Providers;
2 2
3 3
4 -use Illuminate\Support\ServiceProvider; 4 +use Illuminate\Pagination\PaginationServiceProvider as IlluminatePaginationServiceProvider;
5 use Illuminate\Pagination\Paginator; 5 use Illuminate\Pagination\Paginator;
6 6
7 -class PaginationServiceProvider extends ServiceProvider 7 +class PaginationServiceProvider extends IlluminatePaginationServiceProvider
8 { 8 {
9 +
9 /** 10 /**
10 * Register the service provider. 11 * Register the service provider.
11 * 12 *
...@@ -13,6 +14,10 @@ class PaginationServiceProvider extends ServiceProvider ...@@ -13,6 +14,10 @@ class PaginationServiceProvider extends ServiceProvider
13 */ 14 */
14 public function register() 15 public function register()
15 { 16 {
17 + Paginator::viewFactoryResolver(function () {
18 + return $this->app['view'];
19 + });
20 +
16 Paginator::currentPathResolver(function () { 21 Paginator::currentPathResolver(function () {
17 return baseUrl($this->app['request']->path()); 22 return baseUrl($this->app['request']->path());
18 }); 23 });
......
...@@ -4,6 +4,7 @@ namespace BookStack\Providers; ...@@ -4,6 +4,7 @@ namespace BookStack\Providers;
4 4
5 use Illuminate\Routing\Router; 5 use Illuminate\Routing\Router;
6 use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; 6 use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
7 +use Route;
7 8
8 class RouteServiceProvider extends ServiceProvider 9 class RouteServiceProvider extends ServiceProvider
9 { 10 {
...@@ -19,26 +20,54 @@ class RouteServiceProvider extends ServiceProvider ...@@ -19,26 +20,54 @@ class RouteServiceProvider extends ServiceProvider
19 /** 20 /**
20 * Define your route model bindings, pattern filters, etc. 21 * Define your route model bindings, pattern filters, etc.
21 * 22 *
22 - * @param \Illuminate\Routing\Router $router
23 * @return void 23 * @return void
24 */ 24 */
25 - public function boot(Router $router) 25 + public function boot()
26 { 26 {
27 - // 27 + parent::boot();
28 -
29 - parent::boot($router);
30 } 28 }
31 29
32 /** 30 /**
33 * Define the routes for the application. 31 * Define the routes for the application.
34 * 32 *
35 - * @param \Illuminate\Routing\Router $router
36 * @return void 33 * @return void
37 */ 34 */
38 - public function map(Router $router) 35 + public function map()
36 + {
37 + $this->mapWebRoutes();
38 +// $this->mapApiRoutes();
39 + }
40 + /**
41 + * Define the "web" routes for the application.
42 + *
43 + * These routes all receive session state, CSRF protection, etc.
44 + *
45 + * @return void
46 + */
47 + protected function mapWebRoutes()
48 + {
49 + Route::group([
50 + 'middleware' => 'web',
51 + 'namespace' => $this->namespace,
52 + ], function ($router) {
53 + require base_path('routes/web.php');
54 + });
55 + }
56 + /**
57 + * Define the "api" routes for the application.
58 + *
59 + * These routes are typically stateless.
60 + *
61 + * @return void
62 + */
63 + protected function mapApiRoutes()
39 { 64 {
40 - $router->group(['namespace' => $this->namespace], function ($router) { 65 + Route::group([
41 - require app_path('Http/routes.php'); 66 + 'middleware' => 'api',
67 + 'namespace' => $this->namespace,
68 + 'prefix' => 'api',
69 + ], function ($router) {
70 + require base_path('routes/api.php');
42 }); 71 });
43 } 72 }
44 } 73 }
......
...@@ -111,31 +111,6 @@ class PageRepo extends EntityRepo ...@@ -111,31 +111,6 @@ class PageRepo extends EntityRepo
111 } 111 }
112 112
113 /** 113 /**
114 - * Save a new page into the system.
115 - * Input validation must be done beforehand.
116 - * @param array $input
117 - * @param Book $book
118 - * @param int $chapterId
119 - * @return Page
120 - */
121 - public function saveNew(array $input, Book $book, $chapterId = null)
122 - {
123 - $page = $this->newFromInput($input);
124 - $page->slug = $this->findSuitableSlug($page->name, $book->id);
125 -
126 - if ($chapterId) $page->chapter_id = $chapterId;
127 -
128 - $page->html = $this->formatHtml($input['html']);
129 - $page->text = strip_tags($page->html);
130 - $page->created_by = auth()->user()->id;
131 - $page->updated_by = auth()->user()->id;
132 -
133 - $book->pages()->save($page);
134 - return $page;
135 - }
136 -
137 -
138 - /**
139 * Publish a draft page to make it a normal page. 114 * Publish a draft page to make it a normal page.
140 * Sets the slug and updates the content. 115 * Sets the slug and updates the content.
141 * @param Page $draftPage 116 * @param Page $draftPage
...@@ -371,7 +346,7 @@ class PageRepo extends EntityRepo ...@@ -371,7 +346,7 @@ class PageRepo extends EntityRepo
371 */ 346 */
372 public function saveRevision(Page $page, $summary = null) 347 public function saveRevision(Page $page, $summary = null)
373 { 348 {
374 - $revision = $this->pageRevision->fill($page->toArray()); 349 + $revision = $this->pageRevision->newInstance($page->toArray());
375 if (setting('app-editor') !== 'markdown') $revision->markdown = ''; 350 if (setting('app-editor') !== 'markdown') $revision->markdown = '';
376 $revision->page_id = $page->id; 351 $revision->page_id = $page->id;
377 $revision->slug = $page->slug; 352 $revision->slug = $page->slug;
...@@ -381,11 +356,13 @@ class PageRepo extends EntityRepo ...@@ -381,11 +356,13 @@ class PageRepo extends EntityRepo
381 $revision->type = 'version'; 356 $revision->type = 'version';
382 $revision->summary = $summary; 357 $revision->summary = $summary;
383 $revision->save(); 358 $revision->save();
359 +
384 // Clear old revisions 360 // Clear old revisions
385 if ($this->pageRevision->where('page_id', '=', $page->id)->count() > 50) { 361 if ($this->pageRevision->where('page_id', '=', $page->id)->count() > 50) {
386 $this->pageRevision->where('page_id', '=', $page->id) 362 $this->pageRevision->where('page_id', '=', $page->id)
387 ->orderBy('created_at', 'desc')->skip(50)->take(5)->delete(); 363 ->orderBy('created_at', 'desc')->skip(50)->take(5)->delete();
388 } 364 }
365 +
389 return $revision; 366 return $revision;
390 } 367 }
391 368
......
1 <?php namespace BookStack\Services; 1 <?php namespace BookStack\Services;
2 2
3 - 3 +use BookStack\Notifications\ConfirmEmail;
4 +use BookStack\Repos\UserRepo;
4 use Carbon\Carbon; 5 use Carbon\Carbon;
5 -use Illuminate\Contracts\Mail\Mailer;
6 -use Illuminate\Mail\Message;
7 -use BookStack\EmailConfirmation;
8 use BookStack\Exceptions\ConfirmationEmailException; 6 use BookStack\Exceptions\ConfirmationEmailException;
9 use BookStack\Exceptions\UserRegistrationException; 7 use BookStack\Exceptions\UserRegistrationException;
10 -use BookStack\Repos\UserRepo;
11 -use BookStack\Setting;
12 use BookStack\User; 8 use BookStack\User;
9 +use Illuminate\Database\Connection as Database;
13 10
14 class EmailConfirmationService 11 class EmailConfirmationService
15 { 12 {
16 - protected $mailer; 13 + protected $db;
17 - protected $emailConfirmation; 14 + protected $users;
18 15
19 /** 16 /**
20 * EmailConfirmationService constructor. 17 * EmailConfirmationService constructor.
21 - * @param Mailer $mailer 18 + * @param Database $db
22 - * @param EmailConfirmation $emailConfirmation 19 + * @param UserRepo $users
23 */ 20 */
24 - public function __construct(Mailer $mailer, EmailConfirmation $emailConfirmation) 21 + public function __construct(Database $db, UserRepo $users)
25 { 22 {
26 - $this->mailer = $mailer; 23 + $this->db = $db;
27 - $this->emailConfirmation = $emailConfirmation; 24 + $this->users = $users;
28 } 25 }
29 26
30 /** 27 /**
...@@ -38,16 +35,28 @@ class EmailConfirmationService ...@@ -38,16 +35,28 @@ class EmailConfirmationService
38 if ($user->email_confirmed) { 35 if ($user->email_confirmed) {
39 throw new ConfirmationEmailException('Email has already been confirmed, Try logging in.', '/login'); 36 throw new ConfirmationEmailException('Email has already been confirmed, Try logging in.', '/login');
40 } 37 }
38 +
41 $this->deleteConfirmationsByUser($user); 39 $this->deleteConfirmationsByUser($user);
40 + $token = $this->createEmailConfirmation($user);
41 +
42 + $user->notify(new ConfirmEmail($token));
43 + }
44 +
45 + /**
46 + * Creates a new email confirmation in the database and returns the token.
47 + * @param User $user
48 + * @return string
49 + */
50 + public function createEmailConfirmation(User $user)
51 + {
42 $token = $this->getToken(); 52 $token = $this->getToken();
43 - $this->emailConfirmation->create([ 53 + $this->db->table('email_confirmations')->insert([
44 'user_id' => $user->id, 54 'user_id' => $user->id,
45 - 'token' => $token, 55 + 'token' => $token,
56 + 'created_at' => Carbon::now(),
57 + 'updated_at' => Carbon::now()
46 ]); 58 ]);
47 - $this->mailer->send('emails/email-confirmation', ['token' => $token], function (Message $message) use ($user) { 59 + return $token;
48 - $appName = setting('app-name', 'BookStack');
49 - $message->to($user->email, $user->name)->subject('Confirm your email on ' . $appName . '.');
50 - });
51 } 60 }
52 61
53 /** 62 /**
...@@ -59,22 +68,24 @@ class EmailConfirmationService ...@@ -59,22 +68,24 @@ class EmailConfirmationService
59 */ 68 */
60 public function getEmailConfirmationFromToken($token) 69 public function getEmailConfirmationFromToken($token)
61 { 70 {
62 - $emailConfirmation = $this->emailConfirmation->where('token', '=', $token)->first(); 71 + $emailConfirmation = $this->db->table('email_confirmations')->where('token', '=', $token)->first();
63 - // If not found 72 +
73 + // If not found show error
64 if ($emailConfirmation === null) { 74 if ($emailConfirmation === null) {
65 throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register'); 75 throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register');
66 } 76 }
67 77
68 // If more than a day old 78 // If more than a day old
69 - if (Carbon::now()->subDay()->gt($emailConfirmation->created_at)) { 79 + if (Carbon::now()->subDay()->gt(new Carbon($emailConfirmation->created_at))) {
70 - $this->sendConfirmation($emailConfirmation->user); 80 + $user = $this->users->getById($emailConfirmation->user_id);
81 + $this->sendConfirmation($user);
71 throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm'); 82 throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm');
72 } 83 }
73 84
85 + $emailConfirmation->user = $this->users->getById($emailConfirmation->user_id);
74 return $emailConfirmation; 86 return $emailConfirmation;
75 } 87 }
76 88
77 -
78 /** 89 /**
79 * Delete all email confirmations that belong to a user. 90 * Delete all email confirmations that belong to a user.
80 * @param User $user 91 * @param User $user
...@@ -82,7 +93,7 @@ class EmailConfirmationService ...@@ -82,7 +93,7 @@ class EmailConfirmationService
82 */ 93 */
83 public function deleteConfirmationsByUser(User $user) 94 public function deleteConfirmationsByUser(User $user)
84 { 95 {
85 - return $this->emailConfirmation->where('user_id', '=', $user->id)->delete(); 96 + return $this->db->table('email_confirmations')->where('user_id', '=', $user->id)->delete();
86 } 97 }
87 98
88 /** 99 /**
...@@ -92,7 +103,7 @@ class EmailConfirmationService ...@@ -92,7 +103,7 @@ class EmailConfirmationService
92 protected function getToken() 103 protected function getToken()
93 { 104 {
94 $token = str_random(24); 105 $token = str_random(24);
95 - while ($this->emailConfirmation->where('token', '=', $token)->exists()) { 106 + while ($this->db->table('email_confirmations')->where('token', '=', $token)->exists()) {
96 $token = str_random(25); 107 $token = str_random(25);
97 } 108 }
98 return $token; 109 return $token;
......
...@@ -9,14 +9,15 @@ use BookStack\Page; ...@@ -9,14 +9,15 @@ use BookStack\Page;
9 use BookStack\Role; 9 use BookStack\Role;
10 use BookStack\User; 10 use BookStack\User;
11 use Illuminate\Support\Collection; 11 use Illuminate\Support\Collection;
12 +use Illuminate\Support\Facades\Log;
12 13
13 class PermissionService 14 class PermissionService
14 { 15 {
15 16
16 - protected $userRoles;
17 - protected $isAdmin;
18 protected $currentAction; 17 protected $currentAction;
19 - protected $currentUser; 18 + protected $isAdminUser;
19 + protected $userRoles = false;
20 + protected $currentUserModel = false;
20 21
21 public $book; 22 public $book;
22 public $chapter; 23 public $chapter;
...@@ -37,12 +38,6 @@ class PermissionService ...@@ -37,12 +38,6 @@ class PermissionService
37 */ 38 */
38 public function __construct(JointPermission $jointPermission, Book $book, Chapter $chapter, Page $page, Role $role) 39 public function __construct(JointPermission $jointPermission, Book $book, Chapter $chapter, Page $page, Role $role)
39 { 40 {
40 - $this->currentUser = auth()->user();
41 - $userSet = $this->currentUser !== null;
42 - $this->userRoles = false;
43 - $this->isAdmin = $userSet ? $this->currentUser->hasRole('admin') : false;
44 - if (!$userSet) $this->currentUser = new User();
45 -
46 $this->jointPermission = $jointPermission; 41 $this->jointPermission = $jointPermission;
47 $this->role = $role; 42 $this->role = $role;
48 $this->book = $book; 43 $this->book = $book;
...@@ -117,7 +112,7 @@ class PermissionService ...@@ -117,7 +112,7 @@ class PermissionService
117 } 112 }
118 113
119 114
120 - foreach ($this->currentUser->roles as $role) { 115 + foreach ($this->currentUser()->roles as $role) {
121 $roles[] = $role->id; 116 $roles[] = $role->id;
122 } 117 }
123 return $roles; 118 return $roles;
...@@ -389,7 +384,11 @@ class PermissionService ...@@ -389,7 +384,11 @@ class PermissionService
389 */ 384 */
390 public function checkOwnableUserAccess(Ownable $ownable, $permission) 385 public function checkOwnableUserAccess(Ownable $ownable, $permission)
391 { 386 {
392 - if ($this->isAdmin) return true; 387 + if ($this->isAdmin()) {
388 + $this->clean();
389 + return true;
390 + }
391 +
393 $explodedPermission = explode('-', $permission); 392 $explodedPermission = explode('-', $permission);
394 393
395 $baseQuery = $ownable->where('id', '=', $ownable->id); 394 $baseQuery = $ownable->where('id', '=', $ownable->id);
...@@ -400,10 +399,10 @@ class PermissionService ...@@ -400,10 +399,10 @@ class PermissionService
400 399
401 // Handle non entity specific jointPermissions 400 // Handle non entity specific jointPermissions
402 if (in_array($explodedPermission[0], $nonJointPermissions)) { 401 if (in_array($explodedPermission[0], $nonJointPermissions)) {
403 - $allPermission = $this->currentUser && $this->currentUser->can($permission . '-all'); 402 + $allPermission = $this->currentUser() && $this->currentUser()->can($permission . '-all');
404 - $ownPermission = $this->currentUser && $this->currentUser->can($permission . '-own'); 403 + $ownPermission = $this->currentUser() && $this->currentUser()->can($permission . '-own');
405 $this->currentAction = 'view'; 404 $this->currentAction = 'view';
406 - $isOwner = $this->currentUser && $this->currentUser->id === $ownable->created_by; 405 + $isOwner = $this->currentUser() && $this->currentUser()->id === $ownable->created_by;
407 return ($allPermission || ($isOwner && $ownPermission)); 406 return ($allPermission || ($isOwner && $ownPermission));
408 } 407 }
409 408
...@@ -413,7 +412,9 @@ class PermissionService ...@@ -413,7 +412,9 @@ class PermissionService
413 } 412 }
414 413
415 414
416 - return $this->entityRestrictionQuery($baseQuery)->count() > 0; 415 + $q = $this->entityRestrictionQuery($baseQuery)->count() > 0;
416 + $this->clean();
417 + return $q;
417 } 418 }
418 419
419 /** 420 /**
...@@ -443,7 +444,7 @@ class PermissionService ...@@ -443,7 +444,7 @@ class PermissionService
443 */ 444 */
444 protected function entityRestrictionQuery($query) 445 protected function entityRestrictionQuery($query)
445 { 446 {
446 - return $query->where(function ($parentQuery) { 447 + $q = $query->where(function ($parentQuery) {
447 $parentQuery->whereHas('jointPermissions', function ($permissionQuery) { 448 $parentQuery->whereHas('jointPermissions', function ($permissionQuery) {
448 $permissionQuery->whereIn('role_id', $this->getRoles()) 449 $permissionQuery->whereIn('role_id', $this->getRoles())
449 ->where('action', '=', $this->currentAction) 450 ->where('action', '=', $this->currentAction)
...@@ -451,11 +452,13 @@ class PermissionService ...@@ -451,11 +452,13 @@ class PermissionService
451 $query->where('has_permission', '=', true) 452 $query->where('has_permission', '=', true)
452 ->orWhere(function ($query) { 453 ->orWhere(function ($query) {
453 $query->where('has_permission_own', '=', true) 454 $query->where('has_permission_own', '=', true)
454 - ->where('created_by', '=', $this->currentUser->id); 455 + ->where('created_by', '=', $this->currentUser()->id);
455 }); 456 });
456 }); 457 });
457 }); 458 });
458 }); 459 });
460 + $this->clean();
461 + return $q;
459 } 462 }
460 463
461 /** 464 /**
...@@ -469,9 +472,9 @@ class PermissionService ...@@ -469,9 +472,9 @@ class PermissionService
469 // Prevent drafts being visible to others. 472 // Prevent drafts being visible to others.
470 $query = $query->where(function ($query) { 473 $query = $query->where(function ($query) {
471 $query->where('draft', '=', false); 474 $query->where('draft', '=', false);
472 - if ($this->currentUser) { 475 + if ($this->currentUser()) {
473 $query->orWhere(function ($query) { 476 $query->orWhere(function ($query) {
474 - $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser->id); 477 + $query->where('draft', '=', true)->where('created_by', '=', $this->currentUser()->id);
475 }); 478 });
476 } 479 }
477 }); 480 });
...@@ -509,7 +512,10 @@ class PermissionService ...@@ -509,7 +512,10 @@ class PermissionService
509 */ 512 */
510 public function enforceEntityRestrictions($query, $action = 'view') 513 public function enforceEntityRestrictions($query, $action = 'view')
511 { 514 {
512 - if ($this->isAdmin) return $query; 515 + if ($this->isAdmin()) {
516 + $this->clean();
517 + return $query;
518 + }
513 $this->currentAction = $action; 519 $this->currentAction = $action;
514 return $this->entityRestrictionQuery($query); 520 return $this->entityRestrictionQuery($query);
515 } 521 }
...@@ -524,11 +530,15 @@ class PermissionService ...@@ -524,11 +530,15 @@ class PermissionService
524 */ 530 */
525 public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn) 531 public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn)
526 { 532 {
527 - if ($this->isAdmin) return $query; 533 + if ($this->isAdmin()) {
534 + $this->clean();
535 + return $query;
536 + }
537 +
528 $this->currentAction = 'view'; 538 $this->currentAction = 'view';
529 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn]; 539 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
530 540
531 - return $query->where(function ($query) use ($tableDetails) { 541 + $q = $query->where(function ($query) use ($tableDetails) {
532 $query->whereExists(function ($permissionQuery) use (&$tableDetails) { 542 $query->whereExists(function ($permissionQuery) use (&$tableDetails) {
533 $permissionQuery->select('id')->from('joint_permissions') 543 $permissionQuery->select('id')->from('joint_permissions')
534 ->whereRaw('joint_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn']) 544 ->whereRaw('joint_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
...@@ -538,12 +548,12 @@ class PermissionService ...@@ -538,12 +548,12 @@ class PermissionService
538 ->where(function ($query) { 548 ->where(function ($query) {
539 $query->where('has_permission', '=', true)->orWhere(function ($query) { 549 $query->where('has_permission', '=', true)->orWhere(function ($query) {
540 $query->where('has_permission_own', '=', true) 550 $query->where('has_permission_own', '=', true)
541 - ->where('created_by', '=', $this->currentUser->id); 551 + ->where('created_by', '=', $this->currentUser()->id);
542 }); 552 });
543 }); 553 });
544 }); 554 });
545 }); 555 });
546 - 556 + return $q;
547 } 557 }
548 558
549 /** 559 /**
...@@ -555,11 +565,15 @@ class PermissionService ...@@ -555,11 +565,15 @@ class PermissionService
555 */ 565 */
556 public function filterRelatedPages($query, $tableName, $entityIdColumn) 566 public function filterRelatedPages($query, $tableName, $entityIdColumn)
557 { 567 {
558 - if ($this->isAdmin) return $query; 568 + if ($this->isAdmin()) {
569 + $this->clean();
570 + return $query;
571 + }
572 +
559 $this->currentAction = 'view'; 573 $this->currentAction = 'view';
560 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn]; 574 $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn];
561 575
562 - return $query->where(function ($query) use ($tableDetails) { 576 + $q = $query->where(function ($query) use ($tableDetails) {
563 $query->where(function ($query) use (&$tableDetails) { 577 $query->where(function ($query) use (&$tableDetails) {
564 $query->whereExists(function ($permissionQuery) use (&$tableDetails) { 578 $query->whereExists(function ($permissionQuery) use (&$tableDetails) {
565 $permissionQuery->select('id')->from('joint_permissions') 579 $permissionQuery->select('id')->from('joint_permissions')
...@@ -570,12 +584,50 @@ class PermissionService ...@@ -570,12 +584,50 @@ class PermissionService
570 ->where(function ($query) { 584 ->where(function ($query) {
571 $query->where('has_permission', '=', true)->orWhere(function ($query) { 585 $query->where('has_permission', '=', true)->orWhere(function ($query) {
572 $query->where('has_permission_own', '=', true) 586 $query->where('has_permission_own', '=', true)
573 - ->where('created_by', '=', $this->currentUser->id); 587 + ->where('created_by', '=', $this->currentUser()->id);
574 }); 588 });
575 }); 589 });
576 }); 590 });
577 })->orWhere($tableDetails['entityIdColumn'], '=', 0); 591 })->orWhere($tableDetails['entityIdColumn'], '=', 0);
578 }); 592 });
593 + $this->clean();
594 + return $q;
595 + }
596 +
597 + /**
598 + * Check if the current user is an admin.
599 + * @return bool
600 + */
601 + private function isAdmin()
602 + {
603 + if ($this->isAdminUser === null) {
604 + $this->isAdminUser = ($this->currentUser()->id !== null) ? $this->currentUser()->hasRole('admin') : false;
605 + }
606 +
607 + return $this->isAdminUser;
608 + }
609 +
610 + /**
611 + * Get the current user
612 + * @return User
613 + */
614 + private function currentUser()
615 + {
616 + if ($this->currentUserModel === false) {
617 + $this->currentUserModel = auth()->user() ? auth()->user() : new User();
618 + }
619 +
620 + return $this->currentUserModel;
621 + }
622 +
623 + /**
624 + * Clean the cached user elements.
625 + */
626 + private function clean()
627 + {
628 + $this->currentUserModel = false;
629 + $this->userRoles = false;
630 + $this->isAdminUser = null;
579 } 631 }
580 632
581 } 633 }
...\ No newline at end of file ...\ No newline at end of file
......
1 <?php namespace BookStack; 1 <?php namespace BookStack;
2 2
3 +use BookStack\Notifications\ResetPassword;
3 use Illuminate\Auth\Authenticatable; 4 use Illuminate\Auth\Authenticatable;
4 use Illuminate\Auth\Passwords\CanResetPassword; 5 use Illuminate\Auth\Passwords\CanResetPassword;
5 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; 6 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
6 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; 7 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
8 +use Illuminate\Notifications\Notifiable;
7 9
8 class User extends Model implements AuthenticatableContract, CanResetPasswordContract 10 class User extends Model implements AuthenticatableContract, CanResetPasswordContract
9 { 11 {
10 - use Authenticatable, CanResetPassword; 12 + use Authenticatable, CanResetPassword, Notifiable;
11 13
12 /** 14 /**
13 * The database table used by the model. 15 * The database table used by the model.
...@@ -183,4 +185,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon ...@@ -183,4 +185,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
183 185
184 return ''; 186 return '';
185 } 187 }
188 +
189 + /**
190 + * Send the password reset notification.
191 + * @param string $token
192 + * @return void
193 + */
194 + public function sendPasswordResetNotification($token)
195 + {
196 + $this->notify(new ResetPassword($token));
197 + }
186 } 198 }
......
...@@ -63,7 +63,7 @@ function userCan($permission, Ownable $ownable = null) ...@@ -63,7 +63,7 @@ function userCan($permission, Ownable $ownable = null)
63 */ 63 */
64 function setting($key, $default = false) 64 function setting($key, $default = false)
65 { 65 {
66 - $settingService = app('BookStack\Services\SettingService'); 66 + $settingService = app(\BookStack\Services\SettingService::class);
67 return $settingService->get($key, $default); 67 return $settingService->get($key, $default);
68 } 68 }
69 69
...@@ -79,11 +79,17 @@ function baseUrl($path, $forceAppDomain = false) ...@@ -79,11 +79,17 @@ function baseUrl($path, $forceAppDomain = false)
79 if ($isFullUrl && !$forceAppDomain) return $path; 79 if ($isFullUrl && !$forceAppDomain) return $path;
80 $path = trim($path, '/'); 80 $path = trim($path, '/');
81 81
82 + // Remove non-specified domain if forced and we have a domain
82 if ($isFullUrl && $forceAppDomain) { 83 if ($isFullUrl && $forceAppDomain) {
83 $explodedPath = explode('/', $path); 84 $explodedPath = explode('/', $path);
84 $path = implode('/', array_splice($explodedPath, 3)); 85 $path = implode('/', array_splice($explodedPath, 3));
85 } 86 }
86 87
88 + // Return normal url path if not specified in config
89 + if (config('app.url') === '') {
90 + return url($path);
91 + }
92 +
87 return rtrim(config('app.url'), '/') . '/' . $path; 93 return rtrim(config('app.url'), '/') . '/' . $path;
88 } 94 }
89 95
......
...@@ -5,23 +5,22 @@ ...@@ -5,23 +5,22 @@
5 "license": "MIT", 5 "license": "MIT",
6 "type": "project", 6 "type": "project",
7 "require": { 7 "require": {
8 - "php": ">=5.5.9", 8 + "php": ">=5.6.4",
9 - "laravel/framework": "5.2.*", 9 + "laravel/framework": "^5.3.4",
10 "intervention/image": "^2.3", 10 "intervention/image": "^2.3",
11 "laravel/socialite": "^2.0", 11 "laravel/socialite": "^2.0",
12 "barryvdh/laravel-ide-helper": "^2.1", 12 "barryvdh/laravel-ide-helper": "^2.1",
13 - "barryvdh/laravel-debugbar": "^2.0", 13 + "barryvdh/laravel-debugbar": "^2.2.3",
14 "league/flysystem-aws-s3-v3": "^1.0", 14 "league/flysystem-aws-s3-v3": "^1.0",
15 - "barryvdh/laravel-dompdf": "0.6.*", 15 + "barryvdh/laravel-dompdf": "^0.7",
16 - "predis/predis": "^1.0" 16 + "predis/predis": "^1.1"
17 }, 17 },
18 "require-dev": { 18 "require-dev": {
19 "fzaninotto/faker": "~1.4", 19 "fzaninotto/faker": "~1.4",
20 "mockery/mockery": "0.9.*", 20 "mockery/mockery": "0.9.*",
21 - "phpunit/phpunit": "~4.0", 21 + "phpunit/phpunit": "~5.0",
22 - "phpspec/phpspec": "~2.1", 22 + "symfony/css-selector": "3.1.*",
23 - "symfony/dom-crawler": "~3.0", 23 + "symfony/dom-crawler": "3.1.*"
24 - "symfony/css-selector": "~3.0"
25 }, 24 },
26 "autoload": { 25 "autoload": {
27 "classmap": [ 26 "classmap": [
...@@ -37,21 +36,19 @@ ...@@ -37,21 +36,19 @@
37 ] 36 ]
38 }, 37 },
39 "scripts": { 38 "scripts": {
39 + "post-root-package-install": [
40 + "php -r \"file_exists('.env') || copy('.env.example', '.env');\""
41 + ],
42 + "post-create-project-cmd": [
43 + "php artisan key:generate"
44 + ],
40 "post-install-cmd": [ 45 "post-install-cmd": [
41 - "php artisan clear-compiled", 46 + "Illuminate\\Foundation\\ComposerScripts::postInstall",
42 "php artisan optimize" 47 "php artisan optimize"
43 ], 48 ],
44 - "pre-update-cmd": [
45 - "php artisan clear-compiled"
46 - ],
47 "post-update-cmd": [ 49 "post-update-cmd": [
50 + "Illuminate\\Foundation\\ComposerScripts::postUpdate",
48 "php artisan optimize" 51 "php artisan optimize"
49 - ],
50 - "post-root-package-install": [
51 - "php -r \"copy('.env.example', '.env');\""
52 - ],
53 - "post-create-project-cmd": [
54 - "php artisan key:generate"
55 ] 52 ]
56 }, 53 },
57 "config": { 54 "config": {
......
This diff could not be displayed because it is too large.
...@@ -138,6 +138,7 @@ return [ ...@@ -138,6 +138,7 @@ return [
138 Illuminate\Translation\TranslationServiceProvider::class, 138 Illuminate\Translation\TranslationServiceProvider::class,
139 Illuminate\Validation\ValidationServiceProvider::class, 139 Illuminate\Validation\ValidationServiceProvider::class,
140 Illuminate\View\ViewServiceProvider::class, 140 Illuminate\View\ViewServiceProvider::class,
141 + Illuminate\Notifications\NotificationServiceProvider::class,
141 Laravel\Socialite\SocialiteServiceProvider::class, 142 Laravel\Socialite\SocialiteServiceProvider::class,
142 143
143 /** 144 /**
...@@ -156,6 +157,7 @@ return [ ...@@ -156,6 +157,7 @@ return [
156 157
157 BookStack\Providers\AuthServiceProvider::class, 158 BookStack\Providers\AuthServiceProvider::class,
158 BookStack\Providers\AppServiceProvider::class, 159 BookStack\Providers\AppServiceProvider::class,
160 + BookStack\Providers\BroadcastServiceProvider::class,
159 BookStack\Providers\EventServiceProvider::class, 161 BookStack\Providers\EventServiceProvider::class,
160 BookStack\Providers\RouteServiceProvider::class, 162 BookStack\Providers\RouteServiceProvider::class,
161 BookStack\Providers\CustomFacadeProvider::class, 163 BookStack\Providers\CustomFacadeProvider::class,
...@@ -194,6 +196,7 @@ return [ ...@@ -194,6 +196,7 @@ return [
194 'Lang' => Illuminate\Support\Facades\Lang::class, 196 'Lang' => Illuminate\Support\Facades\Lang::class,
195 'Log' => Illuminate\Support\Facades\Log::class, 197 'Log' => Illuminate\Support\Facades\Log::class,
196 'Mail' => Illuminate\Support\Facades\Mail::class, 198 'Mail' => Illuminate\Support\Facades\Mail::class,
199 + 'Notification' => Illuminate\Support\Facades\Notification::class,
197 'Password' => Illuminate\Support\Facades\Password::class, 200 'Password' => Illuminate\Support\Facades\Password::class,
198 'Queue' => Illuminate\Support\Facades\Queue::class, 201 'Queue' => Illuminate\Support\Facades\Queue::class,
199 'Redirect' => Illuminate\Support\Facades\Redirect::class, 202 'Redirect' => Illuminate\Support\Facades\Redirect::class,
......
...@@ -129,7 +129,7 @@ class AddRolesAndPermissions extends Migration ...@@ -129,7 +129,7 @@ class AddRolesAndPermissions extends Migration
129 129
130 // Set all current users as admins 130 // Set all current users as admins
131 // (At this point only the initially create user should be an admin) 131 // (At this point only the initially create user should be an admin)
132 - $users = DB::table('users')->get(); 132 + $users = DB::table('users')->get()->all();
133 foreach ($users as $user) { 133 foreach ($users as $user) {
134 DB::table('role_user')->insert([ 134 DB::table('role_user')->insert([
135 'role_id' => $adminId, 135 'role_id' => $adminId,
......
1 -suites:
2 - main:
3 - namespace: BookStack
4 - psr4_prefix: BookStack
5 - src_path: app
...\ No newline at end of file ...\ No newline at end of file
1 +<?php
2 +return [
3 + /*
4 + |--------------------------------------------------------------------------
5 + | Authentication Language Lines
6 + |--------------------------------------------------------------------------
7 + |
8 + | The following language lines are used during authentication for various
9 + | messages that we need to display to the user. You are free to modify
10 + | these language lines according to your application's requirements.
11 + |
12 + */
13 + 'failed' => 'These credentials do not match our records.',
14 + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
15 +];
...\ No newline at end of file ...\ No newline at end of file
1 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 -<html xmlns="http://www.w3.org/1999/xhtml"
4 - 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;">
5 -
6 -<head 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;">
7 - <meta name="viewport" content="width=device-width"
8 - 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;"/>
9 - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"
10 - 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;"/>
11 - <title>Confirm Your Email At {{ setting('app-name')}}</title>
12 - <style 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;">
13 - * {
14 - margin: 0;
15 - padding: 0;
16 - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif;
17 - font-size: 100%;
18 - line-height: 1.6;
19 - }
20 -
21 - img {
22 - max-width: 100%;
23 - }
24 -
25 - body {
26 - -webkit-font-smoothing: antialiased;
27 - -webkit-text-size-adjust: none;
28 - width: 100% !important;
29 - height: 100%;
30 - }
31 -
32 - a {
33 - color: #348eda;
34 - }
35 -
36 - .btn-primary {
37 - text-decoration: none;
38 - color: #FFF;
39 - background-color: #348eda;
40 - border: solid #348eda;
41 - border-width: 10px 20px;
42 - line-height: 2;
43 - font-weight: bold;
44 - margin-right: 10px;
45 - text-align: center;
46 - cursor: pointer;
47 - display: inline-block;
48 - border-radius: 4px;
49 - }
50 -
51 - .btn-secondary {
52 - text-decoration: none;
53 - color: #FFF;
54 - background-color: #aaa;
55 - border: solid #aaa;
56 - border-width: 10px 20px;
57 - line-height: 2;
58 - font-weight: bold;
59 - margin-right: 10px;
60 - text-align: center;
61 - cursor: pointer;
62 - display: inline-block;
63 - border-radius: 25px;
64 - }
65 -
66 - .last {
67 - margin-bottom: 0;
68 - }
69 -
70 - .first {
71 - margin-top: 0;
72 - }
73 -
74 - .padding {
75 - padding: 10px 0;
76 - }
77 -
78 - table.body-wrap {
79 - width: 100%;
80 - padding: 20px;
81 - }
82 -
83 - table.body-wrap .container {
84 - border: 1px solid #f0f0f0;
85 - }
86 -
87 - h1,
88 - h2,
89 - h3 {
90 - font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
91 - color: #444;
92 - margin: 10px 0 10px;
93 - line-height: 1.2;
94 - font-weight: 200;
95 - }
96 -
97 - h1 {
98 - font-size: 36px;
99 - }
100 -
101 - h2 {
102 - font-size: 28px;
103 - }
104 -
105 - h3 {
106 - font-size: 22px;
107 - }
108 -
109 - p,
110 - ul,
111 - ol {
112 - margin-bottom: 10px;
113 - font-weight: normal;
114 - font-size: 14px;
115 - color: #888888;
116 - }
117 -
118 - ul li,
119 - ol li {
120 - margin-left: 5px;
121 - list-style-position: inside;
122 - }
123 -
124 - .container {
125 - display: block !important;
126 - max-width: 600px !important;
127 - margin: 0 auto !important;
128 - clear: both !important;
129 - }
130 -
131 - .body-wrap .container {
132 - padding: 20px;
133 - }
134 -
135 - .content {
136 - max-width: 600px;
137 - margin: 0 auto;
138 - display: block;
139 - }
140 -
141 - .content table {
142 - width: 100%;
143 - }
144 - </style>
145 -</head>
146 -
147 -<body bgcolor="#f6f6f6"
148 - 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;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;width:100%!important;height:100%;">
149 -<!-- body -->
150 -<table class="body-wrap" bgcolor="#f6f6f6"
151 - 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;width:100%;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;">
152 - <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;">
153 - <td 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;"></td>
154 - <td class="container" bgcolor="#FFFFFF"
155 - style="font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;display:block!important;max-width:600px!important;margin-top:0 !important;margin-bottom:0 !important;margin-right:auto !important;margin-left:auto !important;clear:both!important;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;border-width:1px;border-style:solid;border-color:#f0f0f0;">
156 - <!-- content -->
157 - <div class="content"
158 - style="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;max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;display:block;">
159 - <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%;">
160 - <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;">
161 - <td 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;">
162 - <h1 style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;color:#444;margin-top:10px;margin-bottom:10px;margin-right:0;margin-left:0;line-height:1.2;font-weight:200;font-size:36px;">
163 - Email Confirmation</h1>
164 - <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;">
165 - Thanks for joining <a href="{{ baseUrl('/') }}">{{ setting('app-name')}}</a>. <br/>
166 - Please confirm your email address by clicking the button below.</p>
167 - <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%;">
168 - <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;">
169 - <td class="padding"
170 - 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;">
171 - <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;">
172 - <a class="btn-primary" href="{{ baseUrl('/register/confirm/' . $token) }}"
173 - 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
174 - Email</a></p>
175 - </td>
176 - </tr>
177 - </table>
178 - </td>
179 - </tr>
180 - </table>
181 - </div>
182 - <!-- /content -->
183 - </td>
184 - <td 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;"></td>
185 - </tr>
186 -</table>
187 -<!-- /body -->
188 -</body>
189 -
190 -</html>
1 -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" 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;"> <head 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;"> <meta name="viewport" content="width=device-width" 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;"/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" 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;"/> <title>Password Reset From {{ setting('app-name')}}</title> <style 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;"> * { margin: 0; padding: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 100%; line-height: 1.6; } img { max-width: 100%; } body { -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; } a { color: #348eda; } .btn-primary { text-decoration: none; color: #FFF; background-color: #348eda; border: solid #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; } .btn-secondary { text-decoration: none; color: #FFF; background-color: #aaa; border: solid #aaa; border-width: 10px 20px; line-height: 2; font-weight: bold; margin-right: 10px; text-align: center; cursor: pointer; display: inline-block; border-radius: 25px; } .last { margin-bottom: 0; } .first { margin-top: 0; } .padding { padding: 10px 0; } table.body-wrap { width: 100%; padding: 20px; } table.body-wrap .container { border: 1px solid #f0f0f0; } h1, h2, h3 { font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; color: #444; margin: 10px 0 10px; line-height: 1.2; font-weight: 200; } h1 { font-size: 36px; } h2 { font-size: 28px; } h3 { font-size: 22px; } p, ul, ol { margin-bottom: 10px; font-weight: normal; font-size: 14px; color: #888888; } ul li, ol li { margin-left: 5px; list-style-position: inside; } .container { display: block !important; max-width: 600px !important; margin: 0 auto !important; clear: both !important; } .body-wrap .container { padding: 20px; } .content { max-width: 600px; margin: 0 auto; display: block; } .content table { width: 100%; } </style></head> <body bgcolor="#f6f6f6" 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;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;width:100%!important;height:100%;"><!-- body --><table class="body-wrap" bgcolor="#f6f6f6" 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;width:100%;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;"> <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;"> <td 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;"></td> <td class="container" bgcolor="#FFFFFF" style="font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;display:block!important;max-width:600px!important;margin-top:0 !important;margin-bottom:0 !important;margin-right:auto !important;margin-left:auto !important;clear:both!important;padding-top:20px;padding-bottom:20px;padding-right:20px;padding-left:20px;border-width:1px;border-style:solid;border-color:#f0f0f0;"> <!-- content --> <div class="content" style="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;max-width:600px;margin-top:0;margin-bottom:0;margin-right:auto;margin-left:auto;display:block;"> <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%;"> <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;"> <td 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;"> <h1 style="padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif;color:#444;margin-top:10px;margin-bottom:10px;margin-right:0;margin-left:0;line-height:1.2;font-weight:200;font-size:36px;"> Password Reset</h1> <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 password reset was requested for this email address on <a href="{{ baseUrl('/') }}">{{ setting('app-name')}}</a>. If you did not request a password change please ignore this email.</p> <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%;"> <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;"> <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;"> <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="{{ baseUrl('/password/reset/' . $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;">Click here to reset your password</a></p> </td> </tr> </table> </td> </tr> </table> </div> <!-- /content --> </td> <td 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;"></td> </tr></table><!-- /body --></body> </html>
...\ No newline at end of file ...\ No newline at end of file
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
22 <div class="row"> 22 <div class="row">
23 <div class="col-sm-8"> 23 <div class="col-sm-8">
24 <div class="compact"> 24 <div class="compact">
25 - {!! $users->links() !!} 25 + {{ $users->links() }}
26 </div> 26 </div>
27 </div> 27 </div>
28 <div class="col-sm-4"> 28 <div class="col-sm-4">
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
76 </table> 76 </table>
77 77
78 <div> 78 <div>
79 - {!! $users->links() !!} 79 + {{ $users->links() }}
80 </div> 80 </div>
81 </div> 81 </div>
82 82
......
1 +<?php
2 +
3 +if (! empty($greeting)) {
4 + echo $greeting, "\n\n";
5 +} else {
6 + echo $level == 'error' ? 'Whoops!' : 'Hello!', "\n\n";
7 +}
8 +
9 +if (! empty($introLines)) {
10 + echo implode("\n", $introLines), "\n\n";
11 +}
12 +
13 +if (isset($actionText)) {
14 + echo "{$actionText}: {$actionUrl}", "\n\n";
15 +}
16 +
17 +if (! empty($outroLines)) {
18 + echo implode("\n", $outroLines), "\n\n";
19 +}
20 +
21 +echo 'Regards,', "\n";
22 +echo config('app.name'), "\n";
1 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 +<html>
3 +<head>
4 + <meta name="viewport" content="width=device-width, initial-scale=1.0" />
5 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6 +
7 + <style type="text/css" rel="stylesheet" media="all">
8 + /* Media Queries */
9 + @media only screen and (max-width: 500px) {
10 + .button {
11 + width: 100% !important;
12 + }
13 + }
14 +
15 + @media only screen and (max-width: 600px) {
16 + .button {
17 + width: 100% !important;
18 + }
19 + .mobile {
20 + max-width: 100%;
21 + display: block;
22 + width: 100%;
23 + }
24 + }
25 + </style>
26 +</head>
27 +
28 +<?php
29 +
30 +$style = [
31 + /* Layout ------------------------------ */
32 +
33 + 'body' => 'margin: 0; padding: 0; width: 100%; background-color: #F2F4F6;',
34 + 'email-wrapper' => 'width: 100%; margin: 0; padding: 0; background-color: #F2F4F6;',
35 +
36 + /* Masthead ----------------------- */
37 +
38 + 'email-masthead' => 'padding: 25px 0; text-align: center;',
39 + 'email-masthead_name' => 'font-size: 24px; font-weight: 400; color: #2F3133; text-decoration: none; text-shadow: 0 1px 0 white;',
40 +
41 + 'email-body' => 'width: 100%; margin: 0; padding: 0; border-top: 4px solid '.setting('app-color').'; border-bottom: 1px solid #EDEFF2; background-color: #FFF;',
42 + 'email-body_inner' => 'width: auto; max-width: 100%; margin: 0 auto; padding: 0;',
43 + 'email-body_cell' => 'padding: 35px;',
44 +
45 + 'email-footer' => 'width: auto; max-width: 570px; margin: 0 auto; padding: 0; text-align: center;',
46 + 'email-footer_cell' => 'color: #AEAEAE; padding: 35px; text-align: center;',
47 +
48 + /* Body ------------------------------ */
49 +
50 + 'body_action' => 'width: 100%; margin: 30px auto; padding: 0; text-align: center;',
51 + 'body_sub' => 'margin-top: 25px; padding-top: 25px; border-top: 1px solid #EDEFF2;',
52 +
53 + /* Type ------------------------------ */
54 +
55 + 'anchor' => 'color: '.setting('app-color').';overflow-wrap: break-word;word-wrap: break-word;word-break: break-all;word-break:break-word;',
56 + 'header-1' => 'margin-top: 0; color: #2F3133; font-size: 19px; font-weight: bold; text-align: left;',
57 + 'paragraph' => 'margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;',
58 + 'paragraph-sub' => 'margin-top: 0; color: #74787E; font-size: 12px; line-height: 1.5em;',
59 + 'paragraph-center' => 'text-align: center;',
60 +
61 + /* Buttons ------------------------------ */
62 +
63 + 'button' => 'display: block; display: inline-block; width: 200px; min-height: 20px; padding: 10px;
64 + background-color: #3869D4; border-radius: 3px; color: #ffffff; font-size: 15px; line-height: 25px;
65 + text-align: center; text-decoration: none; -webkit-text-size-adjust: none;',
66 +
67 + 'button--green' => 'background-color: #22BC66;',
68 + 'button--red' => 'background-color: #dc4d2f;',
69 + 'button--blue' => 'background-color: '.setting('app-color').';',
70 +];
71 +?>
72 +
73 +<?php $fontFamily = 'font-family: Arial, \'Helvetica Neue\', Helvetica, sans-serif;'; ?>
74 +
75 +<body style="{{ $style['body'] }}">
76 + <table width="100%" cellpadding="0" cellspacing="0">
77 + <tr>
78 + <td align="center" class="mobile">
79 + <table width="600" style="max-width: 100%; padding: 12px;text-align: left;" cellpadding="0" cellspacing="0" class="mobile">
80 + <tr>
81 + <td style="{{ $style['email-wrapper'] }}" align="center">
82 + <table width="100%" cellpadding="0" cellspacing="0">
83 + <!-- Logo -->
84 + <tr>
85 + <td style="{{ $style['email-masthead'] }}">
86 + <a style="{{ $fontFamily }} {{ $style['email-masthead_name'] }}" href="{{ baseUrl('/') }}" target="_blank">
87 + {{ setting('app-name') }}
88 + </a>
89 + </td>
90 + </tr>
91 +
92 + <!-- Email Body -->
93 + <tr>
94 + <td style="{{ $style['email-body'] }}" width="100%">
95 + <table style="{{ $style['email-body_inner'] }}" align="center" width="100%" cellpadding="0" cellspacing="0">
96 + <tr>
97 + <td style="{{ $fontFamily }} {{ $style['email-body_cell'] }}">
98 +
99 + <!-- Greeting -->
100 + @if (!empty($greeting) || $level == 'error')
101 + <h1 style="{{ $style['header-1'] }}">
102 + @if (! empty($greeting))
103 + {{ $greeting }}
104 + @else
105 + @if ($level == 'error')
106 + Whoops!
107 + @endif
108 + @endif
109 + </h1>
110 + @endif
111 +
112 + <!-- Intro -->
113 + @foreach ($introLines as $line)
114 + <p style="{{ $style['paragraph'] }}">
115 + {{ $line }}
116 + </p>
117 + @endforeach
118 +
119 + <!-- Action Button -->
120 + @if (isset($actionText))
121 + <table style="{{ $style['body_action'] }}" align="center" width="100%" cellpadding="0" cellspacing="0">
122 + <tr>
123 + <td align="center">
124 + <?php
125 + switch ($level) {
126 + case 'success':
127 + $actionColor = 'button--green';
128 + break;
129 + case 'error':
130 + $actionColor = 'button--red';
131 + break;
132 + default:
133 + $actionColor = 'button--blue';
134 + }
135 + ?>
136 +
137 + <a href="{{ $actionUrl }}"
138 + style="{{ $fontFamily }} {{ $style['button'] }} {{ $style[$actionColor] }}"
139 + class="button"
140 + target="_blank">
141 + {{ $actionText }}
142 + </a>
143 + </td>
144 + </tr>
145 + </table>
146 + @endif
147 +
148 + <!-- Outro -->
149 + @foreach ($outroLines as $line)
150 + <p style="{{ $style['paragraph'] }}">
151 + {{ $line }}
152 + </p>
153 + @endforeach
154 +
155 +
156 + <!-- Sub Copy -->
157 + @if (isset($actionText))
158 + <table style="{{ $style['body_sub'] }}">
159 + <tr>
160 + <td style="{{ $fontFamily }}">
161 + <p style="{{ $style['paragraph-sub'] }}">
162 + If you’re having trouble clicking the "{{ $actionText }}" button,
163 + copy and paste the URL below into your web browser:
164 + </p>
165 +
166 + <p style="{{ $style['paragraph-sub'] }}">
167 + <a style="{{ $style['anchor'] }}" href="{{ $actionUrl }}" target="_blank">
168 + {{ $actionUrl }}
169 + </a>
170 + </p>
171 + </td>
172 + </tr>
173 + </table>
174 + @endif
175 +
176 + </td>
177 + </tr>
178 + </table>
179 + </td>
180 + </tr>
181 +
182 + <!-- Footer -->
183 + <tr>
184 + <td>
185 + <table style="{{ $style['email-footer'] }}" align="center" width="100%" cellpadding="0" cellspacing="0">
186 + <tr>
187 + <td style="{{ $fontFamily }} {{ $style['email-footer_cell'] }}">
188 + <p style="{{ $style['paragraph-sub'] }}">
189 + &copy; {{ date('Y') }}
190 + <a style="{{ $style['anchor'] }}" href="{{ baseUrl('/') }}" target="_blank">{{ setting('app-name') }}</a>.
191 + All rights reserved.
192 + </p>
193 + </td>
194 + </tr>
195 + </table>
196 + </td>
197 + </tr>
198 + </table>
199 + </td>
200 + </tr>
201 + </table>
202 + </td>
203 + </tr>
204 + </table>
205 +</body>
206 +</html>
...@@ -139,27 +139,27 @@ Route::group(['middleware' => 'auth'], function () { ...@@ -139,27 +139,27 @@ Route::group(['middleware' => 'auth'], function () {
139 139
140 }); 140 });
141 141
142 -// Login using social authentication 142 +// Social auth routes
143 -Route::get('/login/service/{socialDriver}', 'Auth\AuthController@getSocialLogin'); 143 +Route::get('/login/service/{socialDriver}', 'Auth\RegisterController@getSocialLogin');
144 -Route::get('/login/service/{socialDriver}/callback', 'Auth\AuthController@socialCallback'); 144 +Route::get('/login/service/{socialDriver}/callback', 'Auth\RegisterController@socialCallback');
145 -Route::get('/login/service/{socialDriver}/detach', 'Auth\AuthController@detachSocialAccount'); 145 +Route::get('/login/service/{socialDriver}/detach', 'Auth\RegisterController@detachSocialAccount');
146 +Route::get('/register/service/{socialDriver}', 'Auth\RegisterController@socialRegister');
146 147
147 // Login/Logout routes 148 // Login/Logout routes
148 -Route::get('/login', 'Auth\AuthController@getLogin'); 149 +Route::get('/login', 'Auth\LoginController@getLogin');
149 -Route::post('/login', 'Auth\AuthController@postLogin'); 150 +Route::post('/login', 'Auth\LoginController@login');
150 -Route::get('/logout', 'Auth\AuthController@getLogout'); 151 +Route::get('/logout', 'Auth\LoginController@logout');
151 -Route::get('/register', 'Auth\AuthController@getRegister'); 152 +Route::get('/register', 'Auth\RegisterController@getRegister');
152 -Route::get('/register/confirm', 'Auth\AuthController@getRegisterConfirmation'); 153 +Route::get('/register/confirm', 'Auth\RegisterController@getRegisterConfirmation');
153 -Route::get('/register/confirm/awaiting', 'Auth\AuthController@showAwaitingConfirmation'); 154 +Route::get('/register/confirm/awaiting', 'Auth\RegisterController@showAwaitingConfirmation');
154 -Route::post('/register/confirm/resend', 'Auth\AuthController@resendConfirmation'); 155 +Route::post('/register/confirm/resend', 'Auth\RegisterController@resendConfirmation');
155 -Route::get('/register/confirm/{token}', 'Auth\AuthController@confirmEmail'); 156 +Route::get('/register/confirm/{token}', 'Auth\RegisterController@confirmEmail');
156 -Route::get('/register/confirm/{token}/email', 'Auth\AuthController@viewConfirmEmail'); 157 +Route::post('/register', 'Auth\RegisterController@postRegister');
157 -Route::get('/register/service/{socialDriver}', 'Auth\AuthController@socialRegister');
158 -Route::post('/register', 'Auth\AuthController@postRegister');
159 158
160 // Password reset link request routes... 159 // Password reset link request routes...
161 -Route::get('/password/email', 'Auth\PasswordController@getEmail'); 160 +Route::get('/password/email', 'Auth\ForgotPasswordController@showLinkRequestForm');
162 -Route::post('/password/email', 'Auth\PasswordController@postEmail'); 161 +Route::post('/password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
162 +
163 // Password reset routes... 163 // Password reset routes...
164 -Route::get('/password/reset/{token}', 'Auth\PasswordController@getReset');
165 -Route::post('/password/reset', 'Auth\PasswordController@postReset');
...\ No newline at end of file ...\ No newline at end of file
164 +Route::get('/password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
165 +Route::post('/password/reset', 'Auth\ResetPasswordController@reset');
...\ No newline at end of file ...\ No newline at end of file
......
1 <?php 1 <?php
2 2
3 -use BookStack\EmailConfirmation; 3 +use BookStack\Notifications\ConfirmEmail;
4 +use Illuminate\Support\Facades\Notification;
4 5
5 class AuthTest extends TestCase 6 class AuthTest extends TestCase
6 { 7 {
...@@ -57,15 +58,13 @@ class AuthTest extends TestCase ...@@ -57,15 +58,13 @@ class AuthTest extends TestCase
57 58
58 public function test_confirmed_registration() 59 public function test_confirmed_registration()
59 { 60 {
61 + // Fake notifications
62 + Notification::fake();
63 +
60 // Set settings and get user instance 64 // Set settings and get user instance
61 $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']); 65 $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
62 $user = factory(\BookStack\User::class)->make(); 66 $user = factory(\BookStack\User::class)->make();
63 67
64 - // Mock Mailer to ensure mail is being sent
65 - $mockMailer = Mockery::mock('Illuminate\Contracts\Mail\Mailer');
66 - $mockMailer->shouldReceive('send')->with('emails/email-confirmation', Mockery::type('array'), Mockery::type('callable'))->twice();
67 - $this->app->instance('mailer', $mockMailer);
68 -
69 // Go through registration process 68 // Go through registration process
70 $this->visit('/register') 69 $this->visit('/register')
71 ->see('Sign Up') 70 ->see('Sign Up')
...@@ -76,6 +75,10 @@ class AuthTest extends TestCase ...@@ -76,6 +75,10 @@ class AuthTest extends TestCase
76 ->seePageIs('/register/confirm') 75 ->seePageIs('/register/confirm')
77 ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]); 76 ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
78 77
78 + // Ensure notification sent
79 + $dbUser = \BookStack\User::where('email', '=', $user->email)->first();
80 + Notification::assertSentTo($dbUser, ConfirmEmail::class);
81 +
79 // Test access and resend confirmation email 82 // Test access and resend confirmation email
80 $this->login($user->email, $user->password) 83 $this->login($user->email, $user->password)
81 ->seePageIs('/register/confirm/awaiting') 84 ->seePageIs('/register/confirm/awaiting')
...@@ -84,19 +87,18 @@ class AuthTest extends TestCase ...@@ -84,19 +87,18 @@ class AuthTest extends TestCase
84 ->seePageIs('/register/confirm/awaiting') 87 ->seePageIs('/register/confirm/awaiting')
85 ->press('Resend Confirmation Email'); 88 ->press('Resend Confirmation Email');
86 89
87 - // Get confirmation 90 + // Get confirmation and confirm notification matches
88 - $user = $user->where('email', '=', $user->email)->first(); 91 + $emailConfirmation = DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
89 - $emailConfirmation = EmailConfirmation::where('user_id', '=', $user->id)->first(); 92 + Notification::assertSentTo($dbUser, ConfirmEmail::class, function($notification, $channels) use ($emailConfirmation) {
90 - 93 + return $notification->token === $emailConfirmation->token;
91 - 94 + });
92 - // Check confirmation email button and confirmation activation. 95 +
93 - $this->visit('/register/confirm/' . $emailConfirmation->token . '/email') 96 + // Check confirmation email confirmation activation.
94 - ->see('Email Confirmation') 97 + $this->visit('/register/confirm/' . $emailConfirmation->token)
95 - ->click('Confirm Email')
96 ->seePageIs('/') 98 ->seePageIs('/')
97 ->see($user->name) 99 ->see($user->name)
98 ->notSeeInDatabase('email_confirmations', ['token' => $emailConfirmation->token]) 100 ->notSeeInDatabase('email_confirmations', ['token' => $emailConfirmation->token])
99 - ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]); 101 + ->seeInDatabase('users', ['name' => $dbUser->name, 'email' => $dbUser->email, 'email_confirmed' => true]);
100 } 102 }
101 103
102 public function test_restricted_registration() 104 public function test_restricted_registration()
......
...@@ -236,8 +236,9 @@ class EntityTest extends TestCase ...@@ -236,8 +236,9 @@ class EntityTest extends TestCase
236 ->type('super test page', '#name') 236 ->type('super test page', '#name')
237 ->press('Save Page') 237 ->press('Save Page')
238 // Check redirect 238 // Check redirect
239 - ->seePageIs($newPageUrl) 239 + ->seePageIs($newPageUrl);
240 - ->visit($pageUrl) 240 +
241 + $this->visit($pageUrl)
241 ->seePageIs($newPageUrl); 242 ->seePageIs($newPageUrl);
242 } 243 }
243 244
......
...@@ -86,7 +86,7 @@ class PageDraftTest extends TestCase ...@@ -86,7 +86,7 @@ class PageDraftTest extends TestCase
86 ->visit($chapter->getUrl() . '/create-page') 86 ->visit($chapter->getUrl() . '/create-page')
87 ->visit($book->getUrl()) 87 ->visit($book->getUrl())
88 ->seeInElement('.page-list', 'New Page'); 88 ->seeInElement('.page-list', 'New Page');
89 - 89 +
90 $this->asAdmin() 90 $this->asAdmin()
91 ->visit($book->getUrl()) 91 ->visit($book->getUrl())
92 ->dontSeeInElement('.page-list', 'New Page') 92 ->dontSeeInElement('.page-list', 'New Page')
......
...@@ -59,8 +59,10 @@ class ImageTest extends TestCase ...@@ -59,8 +59,10 @@ class ImageTest extends TestCase
59 59
60 $this->assertTrue(file_exists(public_path($relPath)), 'Uploaded image exists'); 60 $this->assertTrue(file_exists(public_path($relPath)), 'Uploaded image exists');
61 61
62 + $this->deleteImage($relPath);
63 +
62 $this->seeInDatabase('images', [ 64 $this->seeInDatabase('images', [
63 - 'url' => $relPath, 65 + 'url' => $this->baseUrl . $relPath,
64 'type' => 'gallery', 66 'type' => 'gallery',
65 'uploaded_to' => $page->id, 67 'uploaded_to' => $page->id,
66 'path' => $relPath, 68 'path' => $relPath,
...@@ -69,7 +71,7 @@ class ImageTest extends TestCase ...@@ -69,7 +71,7 @@ class ImageTest extends TestCase
69 'name' => $imageName 71 'name' => $imageName
70 ]); 72 ]);
71 73
72 - $this->deleteImage($relPath); 74 +
73 } 75 }
74 76
75 public function test_image_delete() 77 public function test_image_delete()
...@@ -85,7 +87,7 @@ class ImageTest extends TestCase ...@@ -85,7 +87,7 @@ class ImageTest extends TestCase
85 $this->assertResponseOk(); 87 $this->assertResponseOk();
86 88
87 $this->dontSeeInDatabase('images', [ 89 $this->dontSeeInDatabase('images', [
88 - 'url' => $relPath, 90 + 'url' => $this->baseUrl . $relPath,
89 'type' => 'gallery' 91 'type' => 'gallery'
90 ]); 92 ]);
91 93
......