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 | ... | ... |
app/EmailConfirmation.php
deleted
100644 → 0
| 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 | -} |
app/Events/Event.php
deleted
100644 → 0
| ... | @@ -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 | ... | ... |
app/Jobs/Job.php
deleted
100644 → 0
| 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 | -} |
app/Listeners/.gitkeep
deleted
100644 → 0
File mode changed
app/Notifications/ConfirmEmail.php
0 → 100644
| 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 | +} |
app/Notifications/ResetPassword.php
0 → 100644
| 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 | +} |
app/Providers/BroadcastServiceProvider.php
0 → 100644
| 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, | ... | ... |
phpspec.yml
deleted
100644 → 0
resources/lang/en/auth.php
0 → 100644
| 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 | ... | ... |
resources/views/vendor/.gitkeep
deleted
100644 → 0
File mode changed
| 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 | + © {{ 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 | ... | ... |
-
Please register or sign in to post a comment