Showing
20 changed files
with
242 additions
and
21 deletions
| ... | @@ -23,7 +23,6 @@ class Handler extends ExceptionHandler | ... | @@ -23,7 +23,6 @@ class Handler extends ExceptionHandler |
| 23 | * This is a great spot to send exceptions to Sentry, Bugsnag, etc. | 23 | * This is a great spot to send exceptions to Sentry, Bugsnag, etc. |
| 24 | * | 24 | * |
| 25 | * @param \Exception $e | 25 | * @param \Exception $e |
| 26 | - * @return void | ||
| 27 | */ | 26 | */ |
| 28 | public function report(Exception $e) | 27 | public function report(Exception $e) |
| 29 | { | 28 | { |
| ... | @@ -39,6 +38,11 @@ class Handler extends ExceptionHandler | ... | @@ -39,6 +38,11 @@ class Handler extends ExceptionHandler |
| 39 | */ | 38 | */ |
| 40 | public function render($request, Exception $e) | 39 | public function render($request, Exception $e) |
| 41 | { | 40 | { |
| 41 | + if($e instanceof NotifyException) { | ||
| 42 | + \Session::flash('error', $e->message); | ||
| 43 | + return response()->redirectTo($e->redirectLocation); | ||
| 44 | + } | ||
| 45 | + | ||
| 42 | return parent::render($request, $e); | 46 | return parent::render($request, $e); |
| 43 | } | 47 | } |
| 44 | } | 48 | } | ... | ... |
app/Exceptions/NotifyException.php
0 → 100644
| 1 | +<?php namespace Oxbow\Exceptions; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +class NotifyException extends \Exception | ||
| 5 | +{ | ||
| 6 | + | ||
| 7 | + public $message; | ||
| 8 | + public $redirectLocation; | ||
| 9 | + | ||
| 10 | + /** | ||
| 11 | + * NotifyException constructor. | ||
| 12 | + * @param string $message | ||
| 13 | + * @param string $redirectLocation | ||
| 14 | + */ | ||
| 15 | + public function __construct($message, $redirectLocation) | ||
| 16 | + { | ||
| 17 | + $this->message = $message; | ||
| 18 | + $this->redirectLocation = $redirectLocation; | ||
| 19 | + parent::__construct(); | ||
| 20 | + } | ||
| 21 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
app/Exceptions/SocialDriverNotConfigured.php
0 → 100644
app/Exceptions/UserNotFound.php
0 → 100644
| ... | @@ -2,11 +2,15 @@ | ... | @@ -2,11 +2,15 @@ |
| 2 | 2 | ||
| 3 | namespace Oxbow\Http\Controllers\Auth; | 3 | namespace Oxbow\Http\Controllers\Auth; |
| 4 | 4 | ||
| 5 | +use Oxbow\Exceptions\SocialDriverNotConfigured; | ||
| 6 | +use Oxbow\Exceptions\UserNotFound; | ||
| 7 | +use Oxbow\Repos\UserRepo; | ||
| 5 | use Oxbow\User; | 8 | use Oxbow\User; |
| 6 | use Validator; | 9 | use Validator; |
| 7 | use Oxbow\Http\Controllers\Controller; | 10 | use Oxbow\Http\Controllers\Controller; |
| 8 | use Illuminate\Foundation\Auth\ThrottlesLogins; | 11 | use Illuminate\Foundation\Auth\ThrottlesLogins; |
| 9 | use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; | 12 | use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; |
| 13 | +use Laravel\Socialite\Contracts\Factory as Socialite; | ||
| 10 | 14 | ||
| 11 | class AuthController extends Controller | 15 | class AuthController extends Controller |
| 12 | { | 16 | { |
| ... | @@ -27,15 +31,21 @@ class AuthController extends Controller | ... | @@ -27,15 +31,21 @@ class AuthController extends Controller |
| 27 | protected $redirectPath = '/'; | 31 | protected $redirectPath = '/'; |
| 28 | protected $redirectAfterLogout = '/login'; | 32 | protected $redirectAfterLogout = '/login'; |
| 29 | 33 | ||
| 34 | + protected $validSocialDrivers = ['google', 'github']; | ||
| 35 | + | ||
| 36 | + protected $socialite; | ||
| 37 | + protected $userRepo; | ||
| 30 | 38 | ||
| 31 | /** | 39 | /** |
| 32 | * Create a new authentication controller instance. | 40 | * Create a new authentication controller instance. |
| 33 | - * | 41 | + * @param Socialite $socialite |
| 34 | - * @return void | 42 | + * @param UserRepo $userRepo |
| 35 | */ | 43 | */ |
| 36 | - public function __construct() | 44 | + public function __construct(Socialite $socialite, UserRepo $userRepo) |
| 37 | { | 45 | { |
| 38 | $this->middleware('guest', ['except' => 'getLogout']); | 46 | $this->middleware('guest', ['except' => 'getLogout']); |
| 47 | + $this->socialite = $socialite; | ||
| 48 | + $this->userRepo = $userRepo; | ||
| 39 | } | 49 | } |
| 40 | 50 | ||
| 41 | /** | 51 | /** |
| ... | @@ -67,4 +77,99 @@ class AuthController extends Controller | ... | @@ -67,4 +77,99 @@ class AuthController extends Controller |
| 67 | 'password' => bcrypt($data['password']), | 77 | 'password' => bcrypt($data['password']), |
| 68 | ]); | 78 | ]); |
| 69 | } | 79 | } |
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * Show the application login form. | ||
| 83 | + * | ||
| 84 | + * @return \Illuminate\Http\Response | ||
| 85 | + */ | ||
| 86 | + public function getLogin() | ||
| 87 | + { | ||
| 88 | + | ||
| 89 | + if (view()->exists('auth.authenticate')) { | ||
| 90 | + return view('auth.authenticate'); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + $socialDrivers = $this->getActiveSocialDrivers(); | ||
| 94 | + | ||
| 95 | + return view('auth.login', ['socialDrivers' => $socialDrivers]); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + /** | ||
| 99 | + * Redirect to the relevant social site. | ||
| 100 | + * @param $socialDriver | ||
| 101 | + * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
| 102 | + */ | ||
| 103 | + public function getSocialLogin($socialDriver) | ||
| 104 | + { | ||
| 105 | + $driver = $this->validateSocialDriver($socialDriver); | ||
| 106 | + return $this->socialite->driver($driver)->redirect(); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + /** | ||
| 110 | + * The callback for social login services. | ||
| 111 | + * | ||
| 112 | + * @param $socialDriver | ||
| 113 | + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector | ||
| 114 | + * @throws UserNotFound | ||
| 115 | + */ | ||
| 116 | + public function socialCallback($socialDriver) | ||
| 117 | + { | ||
| 118 | + $driver = $this->validateSocialDriver($socialDriver); | ||
| 119 | + // Get user details from social driver | ||
| 120 | + $socialUser = $this->socialite->driver($driver)->user(); | ||
| 121 | + $user = $this->userRepo->getByEmail($socialUser->getEmail()); | ||
| 122 | + | ||
| 123 | + // Redirect if the email is not a current user. | ||
| 124 | + if ($user === null) { | ||
| 125 | + throw new UserNotFound('A user with the email ' . $socialUser->getEmail() . ' was not found.', '/login'); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + \Auth::login($user, true); | ||
| 129 | + return redirect($this->redirectPath); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + /** | ||
| 133 | + * Ensure the social driver is correct and supported. | ||
| 134 | + * | ||
| 135 | + * @param $socialDriver | ||
| 136 | + * @return string | ||
| 137 | + * @throws SocialDriverNotConfigured | ||
| 138 | + */ | ||
| 139 | + protected function validateSocialDriver($socialDriver) | ||
| 140 | + { | ||
| 141 | + $driver = trim(strtolower($socialDriver)); | ||
| 142 | + | ||
| 143 | + if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found'); | ||
| 144 | + if(!$this->checkSocialDriverConfigured($driver)) throw new SocialDriverNotConfigured; | ||
| 145 | + | ||
| 146 | + return $driver; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + /** | ||
| 150 | + * Check a social driver has been configured correctly. | ||
| 151 | + * @param $driver | ||
| 152 | + * @return bool | ||
| 153 | + */ | ||
| 154 | + protected function checkSocialDriverConfigured($driver) | ||
| 155 | + { | ||
| 156 | + $upperName = strtoupper($driver); | ||
| 157 | + $config = [env($upperName . '_APP_ID', false), env($upperName . '_APP_SECRET', false), env('APP_URL', false)]; | ||
| 158 | + return (!in_array(false, $config) && !in_array(null, $config)); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + /** | ||
| 162 | + * Gets the names of the active social drivers. | ||
| 163 | + * @return array | ||
| 164 | + */ | ||
| 165 | + protected function getActiveSocialDrivers() | ||
| 166 | + { | ||
| 167 | + $activeDrivers = []; | ||
| 168 | + foreach($this->validSocialDrivers as $driverName) { | ||
| 169 | + if($this->checkSocialDriverConfigured($driverName)) { | ||
| 170 | + $activeDrivers[$driverName] = true; | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + return $activeDrivers; | ||
| 174 | + } | ||
| 70 | } | 175 | } | ... | ... |
| ... | @@ -82,6 +82,10 @@ Route::group(['middleware' => 'auth'], function () { | ... | @@ -82,6 +82,10 @@ Route::group(['middleware' => 'auth'], function () { |
| 82 | Route::get('/login', 'Auth\AuthController@getLogin'); | 82 | Route::get('/login', 'Auth\AuthController@getLogin'); |
| 83 | Route::post('/login', 'Auth\AuthController@postLogin'); | 83 | Route::post('/login', 'Auth\AuthController@postLogin'); |
| 84 | Route::get('/logout', 'Auth\AuthController@getLogout'); | 84 | Route::get('/logout', 'Auth\AuthController@getLogout'); |
| 85 | +// Login using social authentication | ||
| 86 | +Route::get('/login/service/{socialService}', 'Auth\AuthController@getSocialLogin'); | ||
| 87 | +Route::get('/login/service/{socialService}/callback', 'Auth\AuthController@socialCallback'); | ||
| 88 | + | ||
| 85 | // Password reset link request routes... | 89 | // Password reset link request routes... |
| 86 | Route::get('/password/email', 'Auth\PasswordController@getEmail'); | 90 | Route::get('/password/email', 'Auth\PasswordController@getEmail'); |
| 87 | Route::post('/password/email', 'Auth\PasswordController@postEmail'); | 91 | Route::post('/password/email', 'Auth\PasswordController@postEmail'); | ... | ... |
app/Repos/UserRepo.php
0 → 100644
| 1 | +<?php namespace Oxbow\Repos; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +use Oxbow\User; | ||
| 5 | + | ||
| 6 | +class UserRepo | ||
| 7 | +{ | ||
| 8 | + | ||
| 9 | + protected $user; | ||
| 10 | + | ||
| 11 | + /** | ||
| 12 | + * UserRepo constructor. | ||
| 13 | + * @param $user | ||
| 14 | + */ | ||
| 15 | + public function __construct(User $user) | ||
| 16 | + { | ||
| 17 | + $this->user = $user; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + public function getByEmail($email) { | ||
| 22 | + return $this->user->where('email', '=', $email)->first(); | ||
| 23 | + } | ||
| 24 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| ... | @@ -8,7 +8,8 @@ | ... | @@ -8,7 +8,8 @@ |
| 8 | "php": ">=5.5.9", | 8 | "php": ">=5.5.9", |
| 9 | "laravel/framework": "5.1.*", | 9 | "laravel/framework": "5.1.*", |
| 10 | "intervention/image": "^2.3", | 10 | "intervention/image": "^2.3", |
| 11 | - "barryvdh/laravel-ide-helper": "^2.1" | 11 | + "barryvdh/laravel-ide-helper": "^2.1", |
| 12 | + "laravel/socialite": "^2.0" | ||
| 12 | }, | 13 | }, |
| 13 | "require-dev": { | 14 | "require-dev": { |
| 14 | "fzaninotto/faker": "~1.4", | 15 | "fzaninotto/faker": "~1.4", | ... | ... |
This diff is collapsed.
Click to expand it.
| ... | @@ -26,7 +26,7 @@ return [ | ... | @@ -26,7 +26,7 @@ return [ |
| 26 | | | 26 | | |
| 27 | */ | 27 | */ |
| 28 | 28 | ||
| 29 | - 'url' => 'http://localhost', | 29 | + 'url' => env('APP_URL', 'http://localhost'), |
| 30 | 30 | ||
| 31 | /* | 31 | /* |
| 32 | |-------------------------------------------------------------------------- | 32 | |-------------------------------------------------------------------------- |
| ... | @@ -136,6 +136,7 @@ return [ | ... | @@ -136,6 +136,7 @@ return [ |
| 136 | Illuminate\Translation\TranslationServiceProvider::class, | 136 | Illuminate\Translation\TranslationServiceProvider::class, |
| 137 | Illuminate\Validation\ValidationServiceProvider::class, | 137 | Illuminate\Validation\ValidationServiceProvider::class, |
| 138 | Illuminate\View\ViewServiceProvider::class, | 138 | Illuminate\View\ViewServiceProvider::class, |
| 139 | + Laravel\Socialite\SocialiteServiceProvider::class, | ||
| 139 | 140 | ||
| 140 | /** | 141 | /** |
| 141 | * Third Party | 142 | * Third Party |
| ... | @@ -199,6 +200,7 @@ return [ | ... | @@ -199,6 +200,7 @@ return [ |
| 199 | 'URL' => Illuminate\Support\Facades\URL::class, | 200 | 'URL' => Illuminate\Support\Facades\URL::class, |
| 200 | 'Validator' => Illuminate\Support\Facades\Validator::class, | 201 | 'Validator' => Illuminate\Support\Facades\Validator::class, |
| 201 | 'View' => Illuminate\Support\Facades\View::class, | 202 | 'View' => Illuminate\Support\Facades\View::class, |
| 203 | + 'Socialite' => Laravel\Socialite\Facades\Socialite::class, | ||
| 202 | 204 | ||
| 203 | /** | 205 | /** |
| 204 | * Third Party | 206 | * Third Party | ... | ... |
| ... | @@ -35,4 +35,16 @@ return [ | ... | @@ -35,4 +35,16 @@ return [ |
| 35 | 'secret' => '', | 35 | 'secret' => '', |
| 36 | ], | 36 | ], |
| 37 | 37 | ||
| 38 | + 'github' => [ | ||
| 39 | + 'client_id' => env('GITHUB_APP_ID', false), | ||
| 40 | + 'client_secret' => env('GITHUB_APP_SECRET', false), | ||
| 41 | + 'redirect' => env('APP_URL') . '/login/service/github/callback', | ||
| 42 | + ], | ||
| 43 | + | ||
| 44 | + 'google' => [ | ||
| 45 | + 'client_id' => env('GOOGLE_APP_ID', false), | ||
| 46 | + 'client_secret' => env('GOOGLE_APP_SECRET', false), | ||
| 47 | + 'redirect' => env('APP_URL') . '/login/service/google/callback', | ||
| 48 | + ], | ||
| 49 | + | ||
| 38 | ]; | 50 | ]; | ... | ... |
| ... | @@ -3,6 +3,7 @@ $(function () { | ... | @@ -3,6 +3,7 @@ $(function () { |
| 3 | // Notification hiding | 3 | // Notification hiding |
| 4 | $('.notification').click(function () { | 4 | $('.notification').click(function () { |
| 5 | $(this).fadeOut(100); | 5 | $(this).fadeOut(100); |
| 6 | + | ||
| 6 | }); | 7 | }); |
| 7 | 8 | ||
| 8 | // Dropdown toggles | 9 | // Dropdown toggles | ... | ... |
| ... | @@ -42,6 +42,9 @@ | ... | @@ -42,6 +42,9 @@ |
| 42 | animation-duration: 3s; | 42 | animation-duration: 3s; |
| 43 | animation-timing-function: ease-in-out; | 43 | animation-timing-function: ease-in-out; |
| 44 | animation-fill-mode: forwards; | 44 | animation-fill-mode: forwards; |
| 45 | + &.stopped { | ||
| 46 | + animation-name: notificationStopped; | ||
| 47 | + } | ||
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | @keyframes notification { | 50 | @keyframes notification { |
| ... | @@ -58,6 +61,17 @@ | ... | @@ -58,6 +61,17 @@ |
| 58 | transform: translate3d(580px, 0, 0); | 61 | transform: translate3d(580px, 0, 0); |
| 59 | } | 62 | } |
| 60 | } | 63 | } |
| 64 | +@keyframes notificationStopped { | ||
| 65 | + 0% { | ||
| 66 | + transform: translate3d(580px, 0, 0); | ||
| 67 | + } | ||
| 68 | + 10% { | ||
| 69 | + transform: translate3d(0, 0, 0); | ||
| 70 | + } | ||
| 71 | + 100% { | ||
| 72 | + transform: translate3d(0, 0, 0); | ||
| 73 | + } | ||
| 74 | +} | ||
| 61 | 75 | ||
| 62 | @keyframes menuIn { | 76 | @keyframes menuIn { |
| 63 | from { | 77 | from { | ... | ... |
| ... | @@ -30,7 +30,6 @@ $button-border-radius: 2px; | ... | @@ -30,7 +30,6 @@ $button-border-radius: 2px; |
| 30 | border-radius: $button-border-radius; | 30 | border-radius: $button-border-radius; |
| 31 | cursor: pointer; | 31 | cursor: pointer; |
| 32 | transition: all ease-in-out 120ms; | 32 | transition: all ease-in-out 120ms; |
| 33 | - text-transform: uppercase; | ||
| 34 | box-shadow: 0 0.5px 1.5px 0 rgba(0, 0, 0, 0.21); | 33 | box-shadow: 0 0.5px 1.5px 0 rgba(0, 0, 0, 0.21); |
| 35 | @include generate-button-colors(#EEE, $primary); | 34 | @include generate-button-colors(#EEE, $primary); |
| 36 | } | 35 | } | ... | ... |
| 1 | @extends('public') | 1 | @extends('public') |
| 2 | 2 | ||
| 3 | -@section('sidebar') | 3 | +@section('content') |
| 4 | 4 | ||
| 5 | <div class="center-box"> | 5 | <div class="center-box"> |
| 6 | <h1>Log In</h1> | 6 | <h1>Log In</h1> |
| ... | @@ -23,6 +23,16 @@ | ... | @@ -23,6 +23,16 @@ |
| 23 | <button class="button block pos">Sign In</button> | 23 | <button class="button block pos">Sign In</button> |
| 24 | </div> | 24 | </div> |
| 25 | </form> | 25 | </form> |
| 26 | + @if(count($socialDrivers) > 0) | ||
| 27 | + <hr class="margin-top"> | ||
| 28 | + <h3 class="text-muted">Social Login</h3> | ||
| 29 | + @if(isset($socialDrivers['google'])) | ||
| 30 | + <a href="/login/service/google" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a> | ||
| 31 | + @endif | ||
| 32 | + @if(isset($socialDrivers['github'])) | ||
| 33 | + <a href="/login/service/github" style="color:#000;"><i class="zmdi zmdi-github zmdi-hc-4x"></i></a> | ||
| 34 | + @endif | ||
| 35 | + @endif | ||
| 26 | </div> | 36 | </div> |
| 27 | 37 | ||
| 28 | @stop | 38 | @stop |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -31,7 +31,7 @@ | ... | @@ -31,7 +31,7 @@ |
| 31 | @endif | 31 | @endif |
| 32 | 32 | ||
| 33 | @if(Session::has('error')) | 33 | @if(Session::has('error')) |
| 34 | - <div class="notification anim neg"> | 34 | + <div class="notification anim neg stopped"> |
| 35 | <i class="zmdi zmdi-alert-circle"></i> <span>{{ Session::get('error') }}</span> | 35 | <i class="zmdi zmdi-alert-circle"></i> <span>{{ Session::get('error') }}</span> |
| 36 | </div> | 36 | </div> |
| 37 | @endif | 37 | @endif | ... | ... |
| ... | @@ -6,14 +6,25 @@ | ... | @@ -6,14 +6,25 @@ |
| 6 | <link rel="stylesheet" href="/css/app.css"> | 6 | <link rel="stylesheet" href="/css/app.css"> |
| 7 | <link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'> | 7 | <link href='//fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,300italic,100,300' rel='stylesheet' type='text/css'> |
| 8 | <link rel="stylesheet" href="/bower/material-design-iconic-font/dist/css/material-design-iconic-font.min.css"> | 8 | <link rel="stylesheet" href="/bower/material-design-iconic-font/dist/css/material-design-iconic-font.min.css"> |
| 9 | + | ||
| 10 | + <!-- Scripts --> | ||
| 9 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> | 11 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> |
| 12 | + <script src="/js/common.js"></script> | ||
| 10 | 13 | ||
| 11 | </head> | 14 | </head> |
| 12 | <body class="@yield('body-class')"> | 15 | <body class="@yield('body-class')"> |
| 13 | 16 | ||
| 14 | -<section id="sidebar"> | 17 | +@if(Session::has('success')) |
| 15 | - @yield('sidebar') | 18 | + <div class="notification anim pos"> |
| 16 | -</section> | 19 | + <i class="zmdi zmdi-mood"></i> <span>{{ Session::get('success') }}</span> |
| 20 | + </div> | ||
| 21 | +@endif | ||
| 22 | + | ||
| 23 | +@if(Session::has('error')) | ||
| 24 | + <div class="notification anim neg stopped"> | ||
| 25 | + <i class="zmdi zmdi-alert-circle"></i> <span>{{ Session::get('error') }}</span> | ||
| 26 | + </div> | ||
| 27 | +@endif | ||
| 17 | 28 | ||
| 18 | <section class="container"> | 29 | <section class="container"> |
| 19 | @yield('content') | 30 | @yield('content') | ... | ... |
| ... | @@ -8,18 +8,18 @@ | ... | @@ -8,18 +8,18 @@ |
| 8 | @include('form/text', ['name' => 'email']) | 8 | @include('form/text', ['name' => 'email']) |
| 9 | </div> | 9 | </div> |
| 10 | 10 | ||
| 11 | -@if(isset($model)) | 11 | +@if($currentUser->can('user-update')) |
| 12 | <div class="form-group"> | 12 | <div class="form-group"> |
| 13 | - <span class="text-muted"> | 13 | + <label for="role">User Role</label> |
| 14 | - Only fill the below if you would like <br>to change your password: | 14 | + @include('form.role-select', ['name' => 'role', 'options' => \Oxbow\Role::all(), 'displayKey' => 'display_name']) |
| 15 | - </span> | ||
| 16 | </div> | 15 | </div> |
| 17 | @endif | 16 | @endif |
| 18 | 17 | ||
| 19 | -@if($currentUser->can('user-update')) | 18 | +@if(isset($model)) |
| 20 | <div class="form-group"> | 19 | <div class="form-group"> |
| 21 | - <label for="role">User Role</label> | 20 | + <span class="text-muted"> |
| 22 | - @include('form.role-select', ['name' => 'role', 'options' => \Oxbow\Role::all(), 'displayKey' => 'display_name']) | 21 | + Only fill the below if you would like <br>to change your password: |
| 22 | + </span> | ||
| 23 | </div> | 23 | </div> |
| 24 | @endif | 24 | @endif |
| 25 | 25 | ... | ... |
-
Please register or sign in to post a comment