User configurable locale (language) (#10204)

* Support for system APP_LOCALE

* Start preferences re-write

* port 2fa form

* Working user preferences

* Language user preference

* Don't look up locale from the DB every request

* Device list working

* Deny demo user middleware

* Finish password changing

* remove used resource methods

* remove leftover use

* warn that translation is incomplete

* fix style
This commit is contained in:
Tony Murray 2019-05-23 10:05:45 -05:00 committed by GitHub
parent 90bb68f026
commit 90a67c2ece
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 449 additions and 250 deletions

View File

@ -55,7 +55,7 @@ class TwoFactor
/**
* Base32 Decoding dictionary
*/
private static $base32 = array(
private static $base32 = [
"A" => 0,
"B" => 1,
"C" => 2,
@ -88,7 +88,7 @@ class TwoFactor
"5" => 29,
"6" => 30,
"7" => 31
);
];
/**
* Base32 Encoding dictionary
@ -195,4 +195,20 @@ class TwoFactor
(ord($hash[$offset + 3]) & 0xff)) % pow(10, self::OTP_SIZE);
return str_pad($truncated, self::OTP_SIZE, '0', STR_PAD_LEFT);
}
/**
* Generate 2fa URI
* @param string $username
* @param string $key
* @param bool $counter if type is counter (false for time based)
* @return string
*/
public static function generateUri($username, $key, $counter = false)
{
$title = "LibreNMS:" . urlencode($username);
return $counter ?
"otpauth://hotp/$title?issuer=LibreNMS&counter=1&secret=$key" : // counter based
"otpauth://totp/$title?issuer=LibreNMS&secret=$key"; // time based
}
}

View File

@ -89,7 +89,7 @@ class TwoFactorController extends Controller
return view('auth.2fa')->with([
'key' => $twoFactorSettings['key'],
'uri' => $this->genUri($request->user(), $twoFactorSettings),
'uri' => TwoFactor::generateUri($request->user()->username, $twoFactorSettings['key'], $twoFactorSettings['counter'] !== false),
])->withErrors($errors);
}
@ -211,18 +211,4 @@ class TwoFactorController extends Controller
return UserPref::getPref($user, 'twofactor');
}
private function genUri($user, $settings)
{
$title = "LibreNMS:" . urlencode($user->username);
$key = $settings['key'];
// time based
if ($settings['counter'] === false) {
return "otpauth://totp/$title?issuer=LibreNMS&secret=$key";
}
// counter based
return "otpauth://hotp/$title?issuer=LibreNMS&counter=1&secret=$key";
}
}

View File

@ -30,15 +30,18 @@ use App\Http\Requests\UpdateUserRequest;
use App\Models\Dashboard;
use App\Models\User;
use App\Models\UserPref;
use Hash;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use LibreNMS\Authentication\LegacyAuth;
use LibreNMS\Config;
use Toastr;
use URL;
class UserController extends Controller
{
public function __construct()
{
$this->middleware('deny-demo');
}
/**
* Display a listing of the resource.
*
@ -159,9 +162,8 @@ class UserController extends Controller
}
$user->fill($request->all());
$user->can_modify_passwd = $request->get('can_modify_passwd'); // checkboxes are missing when unchecked
if ($this->updateDashboard($user, $request->get('dashboard'))) {
if ($request->has('dashboard') && $this->updateDashboard($user, $request->get('dashboard'))) {
Toastr::success(__('Updated dashboard for :username', ['username' => $user->username]));
}
@ -174,7 +176,7 @@ class UserController extends Controller
}
}
return redirect(route('users.index'));
return redirect(route(str_contains(URL::previous(), 'preferences') ? 'preferences.index' : 'users.index'));
}
/**

View File

@ -0,0 +1,109 @@
<?php
/**
* UserPreferencesController.php
*
* -Description-
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package LibreNMS
* @link http://librenms.org
* @copyright 2019 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace App\Http\Controllers;
use App\Models\Dashboard;
use App\Models\Device;
use App\Models\UserPref;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use LibreNMS\Authentication\LegacyAuth;
use LibreNMS\Authentication\TwoFactor;
use LibreNMS\Config;
use Session;
class UserPreferencesController extends Controller
{
private $valid_prefs = [
'dashboard' => 'required|integer',
'add_schedule_note_to_device' => 'required|integer',
'locale' => 'required|in:en,ru',
];
public function __construct()
{
$this->middleware('deny-demo');
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$user = $request->user();
$data = [
'user' => $user,
'can_change_password' => LegacyAuth::get()->canUpdatePasswords($user->username),
'dashboards' => Dashboard::allAvailable($user)->with('user')->get(),
'default_dashboard' => UserPref::getPref($user, 'dashboard'),
'note_to_device' => UserPref::getPref($user, 'add_schedule_note_to_device'),
'locale' => UserPref::getPref($user, 'locale') ?: 'en',
'locales' => [
'en' => 'English',
'ru' => 'русский',
],
];
if (Config::get('twofactor')) {
$twofactor = UserPref::getPref($user, 'twofactor');
if ($twofactor) {
$data['twofactor_uri'] = TwoFactor::generateUri($user->username, $twofactor['key'], $twofactor['counter'] !== false);
}
$data['twofactor'] = $twofactor;
}
if (!$user->hasGlobalRead()) {
$data['devices'] = Device::hasAccess($user)->get();
}
return view('user.preferences', $data);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'pref' => ['required', Rule::in(array_keys($this->valid_prefs))],
'value' => $this->valid_prefs[$request->pref] ?? 'required|integer',
]);
UserPref::setPref($request->user(), $request->pref, $request->value);
if ($request->pref == 'locale') {
Session::put('locale', $request->value);
}
return response()->json(['status' => 'success']);
}
}

View File

@ -34,6 +34,7 @@ class Kernel extends HttpKernel
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\App\Http\Middleware\SetLocale::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\LegacyExternalAuth::class,
@ -66,6 +67,7 @@ class Kernel extends HttpKernel
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'deny-demo' => \App\Http\Middleware\DenyDemoUser::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use Closure;
class DenyDemoUser
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->user()->isDemo()) {
return response()->view('auth.deny-demo');
}
return $next($request);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Http\Middleware;
use App;
use App\Models\UserPref;
use Closure;
use Session;
class SetLocale
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (Session::has('locale')) {
$locale = Session::get('locale');
} elseif (!is_null($request->user())) {
$locale = UserPref::getPref($request->user(), 'locale');
Session::put('locale', $locale);
}
if (!empty($locale)) {
App::setLocale($locale);
}
return $next($request);
}
}

View File

@ -43,6 +43,7 @@ class UpdateUserRequest extends FormRequest
'level' => 'int',
'old_password' => 'nullable|string',
'new_password' => 'nullable|confirmed|min:' . Config::get('password.min_length', 8),
'new_password_confirmation' => 'nullable|same:new_password',
'dashboard' => 'int',
];
}

View File

@ -88,7 +88,7 @@ return [
|
*/
'locale' => 'en',
'locale' => env('APP_LOCALE', 'en'),
/*
|--------------------------------------------------------------------------

View File

@ -1,223 +0,0 @@
<?php
use LibreNMS\Authentication\LegacyAuth;
use LibreNMS\Authentication\TwoFactor;
$no_refresh = true;
$pagetitle[] = 'Preferences';
echo '<h2>User Preferences</h2>';
echo '<hr>';
if (LegacyAuth::user()->isDemoUser()) {
demo_account();
} else {
if ($_POST['action'] == 'changepass') {
if (LegacyAuth::get()->authenticate(['username' => LegacyAuth::user()->username, 'password' => $_POST['old_pass']])) {
if ($_POST['new_pass'] == '' || $_POST['new_pass2'] == '') {
$changepass_message = 'Password must not be blank.';
} elseif ($_POST['new_pass'] == $_POST['new_pass2']) {
LegacyAuth::get()->changePassword(LegacyAuth::user()->username, $_POST['new_pass']);
$changepass_message = 'Password Changed.';
} else {
$changepass_message = "Passwords don't match.";
}
} else {
$changepass_message = 'Incorrect password';
}
}
if ($vars['action'] === 'changedash') {
if (!empty($vars['dashboard'])) {
set_user_pref('dashboard', (int)$vars['dashboard']);
$updatedashboard_message = "User default dashboard updated";
}
}
if ($vars['action'] === 'changenote') {
set_user_pref('add_schedule_note_to_device', (bool)$vars['notetodevice']);
if ($vars['notetodevice']) {
$updatenote_message = "Schedule notes will now be added to device notes";
} else {
$updatenote_message = "Schedule notes will no longer be added to device notes";
}
}
include 'includes/html/update-preferences-password.inc.php';
if (LegacyAuth::get()->canUpdatePasswords(LegacyAuth::user()->username)) {
echo '<h3>Change Password</h3>';
echo '<hr>';
echo "<div class='well'>";
echo $changepass_message;
echo "<form method='post' action='preferences/' class='form-horizontal' role='form'>
<input type=hidden name='action' value='changepass'>
<div class='form-group'>
<label for='old_pass' class='col-sm-2 control-label'>Current Password</label>
<div class='col-sm-4'>
<input type=password name=old_pass autocomplete='off' class='form-control input-sm'>
</div>
<div class='col-sm-6'>
</div>
</div>
<div class='form-group'>
<label for='new_pass' class='col-sm-2 control-label'>New Password</label>
<div class='col-sm-4'>
<input type=password name=new_pass autocomplete='off' class='form-control input-sm'>
</div>
<div class='col-sm-6'>
</div>
</div>
<div class='form-group'>
<label for='new_pass2' class='col-sm-2 control-label'>New Password</label>
<div class='col-sm-4'>
<input type=password name=new_pass2 autocomplete='off' class='form-control input-sm'>
<br>
<center><button type='submit' class='btn btn-default'>Submit</button></center>
</div>
<div class='col-sm-6'>
</div>
</div>
</form>";
echo '</div>';
}//end if
if ($config['twofactor'] === true) {
$twofactor = get_user_pref('twofactor');
echo '<script src="js/jquery.qrcode.min.js"></script>';
echo '<h3>Two-Factor Authentication</h3>';
echo '<hr>';
echo '<div class="well">';
if (!empty($twofactor)) {
$twofactor['text'] = "<div class='form-group'>
<label for='twofactorkey' class='col-sm-2 control-label'>Secret Key</label>
<div class='col-sm-4'>
<input type='text' name='twofactorkey' autocomplete='off' disabled class='form-control input-sm' value='".$twofactor['key']."' />
</div>
</div>";
if ($twofactor['counter'] !== false) {
$twofactor['uri'] = 'otpauth://hotp/'.LegacyAuth::user()->username.'?issuer=LibreNMS&counter='.$twofactor['counter'].'&secret='.$twofactor['key'];
$twofactor['text'] .= "<div class='form-group'>
<label for='twofactorcounter' class='col-sm-2 control-label'>Counter</label>
<div class='col-sm-4'>
<input type='text' name='twofactorcounter' autocomplete='off' disabled class='form-control input-sm' value='".$twofactor['counter']."' />
</div>
</div>";
} else {
$twofactor['uri'] = 'otpauth://totp/'.LegacyAuth::user()->username.'?issuer=LibreNMS&secret='.$twofactor['key'];
}
echo '<div id="twofactorqrcontainer">
<div id="twofactorqr"></div>
<button class="btn btn-default" onclick="$(\'#twofactorkeycontainer\').show(); $(\'#twofactorqrcontainer\').hide();">Manual</button>
</div>';
echo '<div id="twofactorkeycontainer">
<form id="twofactorkey" class="form-horizontal" role="form">'.$twofactor['text'].'</form>
<button class="btn btn-default" onclick="$(\'#twofactorkeycontainer\').hide(); $(\'#twofactorqrcontainer\').show();">QR</button>
</div>';
echo '<script>$("#twofactorqr").qrcode({"text": "'.$twofactor['uri'].'"}); $("#twofactorkeycontainer").hide();</script>';
echo '<br/><form method="post" class="form-horizontal" role="form" action="2fa/remove">
<button class="btn btn-danger" type="submit">Disable TwoFactor</button>
</form>';
} else {
echo '<form method="post" class="form-horizontal" role="form" action="2fa/add">
<div class="form-group">
<label for="twofactortype" class="col-sm-2 control-label">TwoFactor Type</label>
<div class="col-sm-4">
<select name="twofactortype" class="select form-control">
<option value="time">Time Based (TOTP)</option>
<option value="counter">Counter Based (HOTP)</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="submit" id="twofactor-generate">Generate TwoFactor Secret Key</button>
</div>
</div>
</form>';
}//end if
echo '</div>';
}//end if
}//end if
echo "<h3>Default Dashboard</h3>
<hr>
<div class='well'>";
if (!empty($updatedashboard_message)) {
print_message($updatedashboard_message);
}
echo "
<form method='post' action='preferences/' class='form-horizontal' role='form'>
<div class='form-group'>
<input type=hidden name='action' value='changedash'>
<div class='form-group'>
<label for='dashboard' class='col-sm-2 control-label'>Dashboard</label>
<div class='col-sm-4'>
<select class='form-control' name='dashboard'>";
foreach (get_dashboards() as $dash) {
echo "
<option value='".$dash['dashboard_id']."'".($dash['default'] ? ' selected' : '').">".display($dash['username']).':'.display($dash['dashboard_name'])."</option>";
}
echo '
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2"><button type="submit" class="btn btn-default">Update Dashboard</button></div>
</div>
</div>
</form>
</div>';
echo "<h3>Add schedule notes to devices notes</h3>
<hr>
<div class='well'>";
if (!empty($updatenote_message)) {
print_message($updatenote_message);
}
echo "
<form method='post' action='preferences/' class='form-horizontal' role='form'>
<div class='form-group'>
<input type=hidden name='action' value='changenote'>
<div class='form-group'>
<label for='dashboard' class='col-sm-3 control-label'>Add schedule notes to devices notes</label>
<div class='col-sm-4'>
<input id='notetodevice' type='checkbox' name='notetodevice' data-size='small' " . ((get_user_pref('add_schedule_note_to_device', false)) ? 'checked' : '') . ">
</div>
</div>
<div class='form-group'>
<div class='col-sm-4 col-sm-offset-3'>
<button type='submit' class='btn btn-default'>Update preferences</button>
</div>
<div class='col-sm-6'></div>
</div>
</div>
</form>
</div>";
echo "<h3>Device Permissions</h3>";
echo "<hr>";
echo '<div class="well">';
if (LegacyAuth::user()->hasGlobalAdmin()) {
echo "<strong class='blue'>Global Administrative Access</strong>";
} elseif (LegacyAuth::user()->hasGlobalRead()) {
echo "<strong class='green'>Global Viewing Access</strong>";
} else {
foreach (dbFetchRows('SELECT * FROM `devices_perms` AS P, `devices` AS D WHERE `user_id` = ? AND P.device_id = D.device_id', array(LegacyAuth::id())) as $perm) {
// FIXME generatedevicelink?
echo "<a href='device/device=".$perm['device_id']."'>".$perm['hostname'].'</a><br />';
$dev_access = 1;
}
if (!$dev_access) {
echo 'No access!';
}
}
echo '</div>';
echo "<script>$(\"[name='notetodevice']\").bootstrapSwitch('offColor','danger');</script>";

View File

@ -0,0 +1,11 @@
@extends('layouts.librenmsv1')
@section('content')
<div class="container">
<div class="row">
<div class="alert alert-danger col-md-6 col-md-offset-3"><i class="fa fa-fw fa-exclamation-circle" aria-hidden="true"></i>
@lang('You are logged in as a demo account, this page is not accessible to you')
</div>
</div>
</div>
@endsection

View File

@ -108,6 +108,7 @@
@yield('content')
@yield('scripts')
{!! Toastr::render() !!}

View File

@ -72,6 +72,7 @@
<div class="col-sm-9 col-sm-offset-3">
<div class="checkbox">
<label class="checkbox-inline">
<input type="hidden" value="0" name="can_modify_passwd">
<input type="checkbox" id="can_modify_passwd" name="can_modify_passwd" @if(old('can_modify_passwd', $user->can_modify_passwd)) checked @endif> @lang('Can Modify Password')
</label>
</div>

View File

@ -0,0 +1,235 @@
@extends('layouts.librenmsv1')
@section('title', __('Preferences'))
@section('content')
<div class="container">
<row>
<legend>@lang('User Preferences')</legend>
</row>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@if($can_change_password)
<div class="panel panel-default panel-condensed">
<div class="panel-heading">@lang('Change Password')</div>
<div class="panel-body">
<form method="POST" action="{{ route('users.update', [$user->user_id]) }}" class="form-horizontal" role="form">
<input type="hidden" name="_method" value="PATCH">
<div class="form-group">
<label for="old_password" class="col-sm-4 control-label">@lang('Current Password')</label>
<div class="col-sm-4">
<input type="password" name="old_password" autocomplete="off" class="form-control input-sm">
</div>
</div>
<div class="form-group">
<label for="new_password" class="col-sm-4 control-label">@lang('New Password')</label>
<div class="col-sm-4">
<input type="password" name="new_password" autocomplete="off" class="form-control input-sm">
</div>
</div>
<div class="form-group">
<label for="new_password_confirmation" class="col-sm-4 control-label">@lang('Verify New Password')</label>
<div class="col-sm-4">
<input type="password" name="new_password_confirmation" autocomplete="off" class="form-control input-sm">
</div>
</div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-3">
<button type="submit" class="btn btn-default">@lang('Change Password')</button>
</div>
</div>
</form>
</div>
</div>
@endif
<div class="panel panel-default panel-condensed">
<div class="panel-heading">@lang('Preferences')</div>
<div class="panel-body">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="dashboard" class="col-sm-4 control-label">@lang('Dashboard')</label>
<div class="col-sm-4">
<select class="form-control ajax-select" name="dashboard" data-pref="dashboard" data-previous="{{ $default_dashboard }}">
@foreach($dashboards as $dash)
<option value="{{ $dash->dashboard_id }}" @if($dash->dashboard_id == $default_dashboard) selected @endif>{{ $dash->user ? $dash->user->username : __('<deleted>') }}:{{ $dash->dashboard_name }}</option>
@endforeach
</select>
</div>
</div>
<div class="form-group">
<label for="locale" class="col-sm-4 control-label">@lang('Language')</label>
<div class="col-sm-4">
<select class="form-control ajax-select" name="locale" data-pref="locale" data-previous="{{ $locale }}">
@foreach($locales as $lang => $descr)
<option value="{{ $lang }}" @if($lang == $locale) selected @endif>{{ $descr }}</option>
@endforeach
</select>
</div>
<div class="col-sm-4">
<small>* @lang('Translation not fully supported')</small>
</div>
</div>
<div class="form-group">
<label for="notetodevice" class="col-sm-4 control-label">@lang('Add schedule notes to devices notes')</label>
<div class="col-sm-4">
<input id="notetodevice" type="checkbox" name="notetodevice" @if($note_to_device) checked @endif>
</div>
</div>
</form>
</div>
</div>
@config('twofactor')
<div class="panel panel-default panel-condensed">
<div class="panel-heading">@lang('Two-Factor Authentication')</div>
<div class="panel-body">
@if($twofactor)
<div id="twofactorqrcontainer">
<div id="twofactorqr"></div>
<script>$("#twofactorqr").qrcode({"text": "{{ $twofactor_uri }}"});</script>
<button class="btn btn-default" onclick="$('#twofactorkeycontainer').show(); $('#twofactorqrcontainer').hide();">@lang('Manual')</button>
</div>
<div id="twofactorkeycontainer">
<form id="twofactorkey" class="form-horizontal" role="form">
<div class="form-group">
<label for="twofactorkey" class="col-sm-4 control-label">@lang('Secret Key')</label>
<div class="col-sm-4">
<input type="text" name="twofactorkey" autocomplete="off" disabled class="form-control input-sm" value="{{ $twofactor['key'] }}" />
</div>
</div>
@if($twofactor['counter'] !== false)
<div class="form-group">
<label for="twofactorcounter" class="col-sm-4 control-label">@lang('Counter')</label>
<div class="col-sm-4">
<input type="text" name="twofactorcounter" autocomplete="off" disabled class="form-control input-sm" value="{{ $twofactor['counter'] }}" />
</div>
</div>
@endif
</form>
<button class="btn btn-default" onclick="$('#twofactorkeycontainer').hide(); $('#twofactorqrcontainer').show();">@lang('QR')</button>
</div>
<br/>
<form method="post" class="form-horizontal" role="form" action="{{ route('2fa.remove') }}">
<button class="btn btn-danger" type="submit">@lang('Disable TwoFactor')</button>
</form>
@else
<form method="post" class="form-horizontal" role="form" action="{{ route('2fa.add') }}">
<div class="form-group">
<label for="twofactortype" class="col-sm-4 control-label">@lang('TwoFactor Type')</label>
<div class="col-sm-4">
<select name="twofactortype" class="select form-control">
<option value="time">@lang('Time Based (TOTP)')</option>
<option value="counter">@lang('Counter Based (HOTP)')</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-3">
<button class="btn btn-default" type="submit" id="twofactor-generate">@lang('Generate TwoFactor Secret Key')</button>
</div>
</div>
</form>
@endif
</div>
</div>
@endconfig
<div class="panel panel-default panel-condensed">
<div class="panel-heading">@lang('Device Permissions')</div>
<div class="panel-body">
@if(auth()->user()->hasGlobalAdmin())
<strong class="blue">@lang('Global Administrative Access')</strong>
@elseif(auth()->user()->hasGlobalRead())
<strong class="green">@lang('Global Viewing Access')</strong>
@else
@forelse($devices as $device)
{!! \LibreNMS\Util\Url::deviceLink($device) !!} <br />
@empty
<strong class="red">@lang('No access!')</strong>
@endforelse
@endif
</div>
</div>
</div>
@endsection
@section('javascript')
<script src="{{ asset('js/jquery.qrcode.min.js') }}"></script>
@endsection
@section('scripts')
<script>
$("[name='notetodevice']")
.bootstrapSwitch('offColor', 'danger')
.on('switchChange.bootstrapSwitch', function (e, state) {
var $this = $(this);
$.ajax({
url: '{{ route('preferences.store') }}',
dataType: 'json',
type: 'POST',
data: {
pref: 'add_schedule_note_to_device',
value: state ? 1 : 0
},
success: function () {
$this.closest('.form-group').addClass('has-success');
setTimeout(function () {
$this.closest('.form-group').removeClass('has-success');
}, 2000);
},
error: function () {
$this.bootstrapSwitch('toggleState', true);
$this.closest('.form-group').addClass('has-error');
setTimeout(function(){
$this.closest('.form-group').removeClass('has-error');
}, 2000);
}
});
});
$('.ajax-select').change(function () {
var $this = $(this);
var value = $this.val();
console.log($this.data('pref'));
$.ajax({
url: '{{ route('preferences.store') }}',
dataType: 'json',
type: 'POST',
data: {
pref: $this.data('pref'),
value: value
},
success: function () {
$this.data('previous', value);
$this.closest('.form-group').addClass('has-success');
setTimeout(function () {
$this.closest('.form-group').removeClass('has-success');
}, 2000);
},
error: function () {
$this.val($this.data('previous'));
$this.closest('.form-group').addClass('has-error');
setTimeout(function(){
$this.closest('.form-group').removeClass('has-error');
}, 2000);
}
});
});
</script>
@endsection
@section('css')
<style>
#twofactorkeycontainer { display: none; }
</style>
@endsection

View File

@ -23,6 +23,8 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
// pages
Route::get('locations', 'LocationController@index');
Route::resource('preferences', 'UserPreferencesController', ['only' => ['index', 'store']]);
Route::resource('users', 'UserController');
// old route redirects
Route::permanentRedirect('poll-log', 'pollers/tab=log/');
@ -31,7 +33,7 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
Route::group(['prefix' => '2fa', 'namespace' => 'Auth'], function () {
Route::get('', 'TwoFactorController@showTwoFactorForm')->name('2fa.form');
Route::post('', 'TwoFactorController@verifyTwoFactor')->name('2fa.verify');
Route::post('add', 'TwoFactorController@create');
Route::post('add', 'TwoFactorController@create')->name('2fa.add');
Route::post('cancel', 'TwoFactorController@cancelAdd')->name('2fa.cancel');
Route::post('remove', 'TwoFactorController@destroy')->name('2fa.remove');
@ -39,8 +41,6 @@ Route::group(['middleware' => ['auth', '2fa'], 'guard' => 'auth'], function () {
Route::delete('{user}', 'TwoFactorManagementController@destroy')->name('2fa.delete');
});
Route::resource('users', 'UserController');
// Ajax routes
Route::group(['prefix' => 'ajax'], function () {
// page ajax controllers