mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Guest control: Cleaned methods involved in fetching/handling
- Moves guest user caching from User class to app container for simplicity. - Updates test to use simpler $this->users->guest() method for consistency. - Streamlined helpers to avoid function overlap for simplicity. - Extracted user profile dropdown while doing changes.
This commit is contained in:
parent
9ac932fc28
commit
b90033a730
@ -41,7 +41,7 @@ class View extends Model
|
|||||||
public static function incrementFor(Viewable $viewable): int
|
public static function incrementFor(Viewable $viewable): int
|
||||||
{
|
{
|
||||||
$user = user();
|
$user = user();
|
||||||
if (is_null($user) || $user->isDefault()) {
|
if (is_null($user) || $user->isGuest()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class UserEntityWatchOptions
|
|||||||
|
|
||||||
public function canWatch(): bool
|
public function canWatch(): bool
|
||||||
{
|
{
|
||||||
return $this->user->can('receive-notifications') && !$this->user->isDefault();
|
return $this->user->can('receive-notifications') && !$this->user->isGuest();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getWatchLevel(): string
|
public function getWatchLevel(): string
|
||||||
|
@ -9,6 +9,7 @@ use BookStack\Access\LdapService;
|
|||||||
use BookStack\Access\LoginService;
|
use BookStack\Access\LoginService;
|
||||||
use BookStack\Access\RegistrationService;
|
use BookStack\Access\RegistrationService;
|
||||||
use BookStack\Api\ApiTokenGuard;
|
use BookStack\Api\ApiTokenGuard;
|
||||||
|
use BookStack\Users\Models\User;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Validation\Rules\Password;
|
use Illuminate\Validation\Rules\Password;
|
||||||
@ -65,5 +66,11 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
Auth::provider('external-users', function ($app, array $config) {
|
Auth::provider('external-users', function ($app, array $config) {
|
||||||
return new ExternalBaseUserProvider($config['model']);
|
return new ExternalBaseUserProvider($config['model']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Bind and provide the default system user as a singleton to the app instance when needed.
|
||||||
|
// This effectively "caches" fetching the user at an app-instance level.
|
||||||
|
$this->app->singleton('users.default', function () {
|
||||||
|
return User::query()->where('system_name', '=', 'public')->first();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,23 +35,7 @@ function versioned_asset(string $file = ''): string
|
|||||||
*/
|
*/
|
||||||
function user(): User
|
function user(): User
|
||||||
{
|
{
|
||||||
return auth()->user() ?: User::getDefault();
|
return auth()->user() ?: User::getGuest();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if current user is a signed in user.
|
|
||||||
*/
|
|
||||||
function signedInUser(): bool
|
|
||||||
{
|
|
||||||
return auth()->user() && !auth()->user()->isDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current user has general access.
|
|
||||||
*/
|
|
||||||
function hasAppAccess(): bool
|
|
||||||
{
|
|
||||||
return !auth()->guest() || setting('app-public');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,7 +45,7 @@ function hasAppAccess(): bool
|
|||||||
function userCan(string $permission, Model $ownable = null): bool
|
function userCan(string $permission, Model $ownable = null): bool
|
||||||
{
|
{
|
||||||
if ($ownable === null) {
|
if ($ownable === null) {
|
||||||
return user() && user()->can($permission);
|
return user()->can($permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check permission on ownable item
|
// Check permission on ownable item
|
||||||
|
@ -10,7 +10,7 @@ class RecentlyViewed extends EntityQuery
|
|||||||
public function run(int $count, int $page): Collection
|
public function run(int $count, int $page): Collection
|
||||||
{
|
{
|
||||||
$user = user();
|
$user = user();
|
||||||
if ($user === null || $user->isDefault()) {
|
if ($user === null || $user->isGuest()) {
|
||||||
return collect();
|
return collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class TopFavourites extends EntityQuery
|
|||||||
public function run(int $count, int $skip = 0)
|
public function run(int $count, int $skip = 0)
|
||||||
{
|
{
|
||||||
$user = user();
|
$user = user();
|
||||||
if ($user->isDefault()) {
|
if ($user->isGuest()) {
|
||||||
return collect();
|
return collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ abstract class Controller extends BaseController
|
|||||||
*/
|
*/
|
||||||
protected function preventGuestAccess(): void
|
protected function preventGuestAccess(): void
|
||||||
{
|
{
|
||||||
if (!signedInUser()) {
|
if (user()->isGuest()) {
|
||||||
$this->showPermissionError();
|
$this->showPermissionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ class ApiAuthenticate
|
|||||||
{
|
{
|
||||||
// Return if the user is already found to be signed in via session-based auth.
|
// Return if the user is already found to be signed in via session-based auth.
|
||||||
// This is to make it easy to browser the API via browser after just logging into the system.
|
// This is to make it easy to browser the API via browser after just logging into the system.
|
||||||
if (signedInUser() || session()->isStarted()) {
|
if (!user()->isGuest() || session()->isStarted()) {
|
||||||
if (!$this->sessionUserHasApiAccess()) {
|
if (!$this->sessionUserHasApiAccess()) {
|
||||||
throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
|
throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
|
||||||
}
|
}
|
||||||
@ -53,6 +53,6 @@ class ApiAuthenticate
|
|||||||
{
|
{
|
||||||
$hasApiPermission = user()->can('access-api');
|
$hasApiPermission = user()->can('access-api');
|
||||||
|
|
||||||
return $hasApiPermission && hasAppAccess();
|
return $hasApiPermission && user()->hasAppAccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ class Authenticate
|
|||||||
*/
|
*/
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next)
|
||||||
{
|
{
|
||||||
if (!hasAppAccess()) {
|
if (!user()->hasAppAccess()) {
|
||||||
if ($request->ajax()) {
|
if ($request->ajax()) {
|
||||||
return response('Unauthorized.', 401);
|
return response('Unauthorized.', 401);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class PreventAuthenticatedResponseCaching
|
|||||||
/** @var Response $response */
|
/** @var Response $response */
|
||||||
$response = $next($request);
|
$response = $next($request);
|
||||||
|
|
||||||
if (signedInUser()) {
|
if (!user()->isGuest()) {
|
||||||
$response->headers->set('Cache-Control', 'max-age=0, no-store, private');
|
$response->headers->set('Cache-Control', 'max-age=0, no-store, private');
|
||||||
$response->headers->set('Pragma', 'no-cache');
|
$response->headers->set('Pragma', 'no-cache');
|
||||||
$response->headers->set('Expires', 'Sun, 12 Jul 2015 19:01:00 GMT');
|
$response->headers->set('Expires', 'Sun, 12 Jul 2015 19:01:00 GMT');
|
||||||
|
@ -34,7 +34,7 @@ class SettingController extends Controller
|
|||||||
return view('settings.' . $category, [
|
return view('settings.' . $category, [
|
||||||
'category' => $category,
|
'category' => $category,
|
||||||
'version' => $version,
|
'version' => $version,
|
||||||
'guestUser' => User::getDefault(),
|
'guestUser' => User::getGuest(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class SettingService
|
|||||||
$default = config('setting-defaults.user.' . $key, false);
|
$default = config('setting-defaults.user.' . $key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user->isDefault()) {
|
if ($user->isGuest()) {
|
||||||
return $this->getFromSession($key, $default);
|
return $this->getFromSession($key, $default);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ class SettingService
|
|||||||
*/
|
*/
|
||||||
public function putUser(User $user, string $key, string $value): bool
|
public function putUser(User $user, string $key, string $value): bool
|
||||||
{
|
{
|
||||||
if ($user->isDefault()) {
|
if ($user->isGuest()) {
|
||||||
session()->put($key, $value);
|
session()->put($key, $value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -75,7 +75,7 @@ class LanguageManager
|
|||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user->isDefault() && config('app.auto_detect_locale')) {
|
if ($user->isGuest() && config('app.auto_detect_locale')) {
|
||||||
return $this->autoDetectLocale($request, $default);
|
return $this->autoDetectLocale($request, $default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class UserSearchController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function forSelect(Request $request)
|
public function forSelect(Request $request)
|
||||||
{
|
{
|
||||||
$hasPermission = signedInUser() && (
|
$hasPermission = !user()->isGuest() && (
|
||||||
userCan('users-manage')
|
userCan('users-manage')
|
||||||
|| userCan('restrictions-manage-own')
|
|| userCan('restrictions-manage-own')
|
||||||
|| userCan('restrictions-manage-all')
|
|| userCan('restrictions-manage-all')
|
||||||
|
@ -88,38 +88,31 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
*/
|
*/
|
||||||
protected string $avatarUrl = '';
|
protected string $avatarUrl = '';
|
||||||
|
|
||||||
/**
|
|
||||||
* This holds the default user when loaded.
|
|
||||||
*/
|
|
||||||
protected static ?User $defaultUser = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default public user.
|
* Returns the default public user.
|
||||||
|
* Fetches from the container as a singleton to effectively cache at an app level.
|
||||||
*/
|
*/
|
||||||
public static function getDefault(): self
|
public static function getGuest(): self
|
||||||
{
|
{
|
||||||
if (!is_null(static::$defaultUser)) {
|
return app()->make('users.default');
|
||||||
return static::$defaultUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
static::$defaultUser = static::query()->where('system_name', '=', 'public')->first();
|
|
||||||
|
|
||||||
return static::$defaultUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function clearDefault(): void
|
|
||||||
{
|
|
||||||
static::$defaultUser = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the user is the default public user.
|
* Check if the user is the default public user.
|
||||||
*/
|
*/
|
||||||
public function isDefault(): bool
|
public function isGuest(): bool
|
||||||
{
|
{
|
||||||
return $this->system_name === 'public';
|
return $this->system_name === 'public';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user has general access to the application.
|
||||||
|
*/
|
||||||
|
public function hasAppAccess(): bool
|
||||||
|
{
|
||||||
|
return !$this->isGuest() || setting('app-public');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The roles that belong to the user.
|
* The roles that belong to the user.
|
||||||
*
|
*
|
||||||
|
@ -142,7 +142,7 @@
|
|||||||
@if($watchOptions->canWatch() && !$watchOptions->isWatching())
|
@if($watchOptions->canWatch() && !$watchOptions->isWatching())
|
||||||
@include('entities.watch-action', ['entity' => $book])
|
@include('entities.watch-action', ['entity' => $book])
|
||||||
@endif
|
@endif
|
||||||
@if(signedInUser())
|
@if(!user()->isGuest())
|
||||||
@include('entities.favourite-action', ['entity' => $book])
|
@include('entities.favourite-action', ['entity' => $book])
|
||||||
@endif
|
@endif
|
||||||
@if(userCan('content-export'))
|
@if(userCan('content-export'))
|
||||||
|
@ -160,7 +160,7 @@
|
|||||||
@if($watchOptions->canWatch() && !$watchOptions->isWatching())
|
@if($watchOptions->canWatch() && !$watchOptions->isWatching())
|
||||||
@include('entities.watch-action', ['entity' => $chapter])
|
@include('entities.watch-action', ['entity' => $chapter])
|
||||||
@endif
|
@endif
|
||||||
@if(signedInUser())
|
@if(!user()->isGuest())
|
||||||
@include('entities.favourite-action', ['entity' => $chapter])
|
@include('entities.favourite-action', ['entity' => $chapter])
|
||||||
@endif
|
@endif
|
||||||
@if(userCan('content-export'))
|
@if(userCan('content-export'))
|
||||||
|
47
resources/views/common/header-user-menu.blade.php
Normal file
47
resources/views/common/header-user-menu.blade.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<div class="dropdown-container" component="dropdown" option:dropdown:bubble-escapes="true">
|
||||||
|
<span class="user-name py-s hide-under-l" refs="dropdown@toggle"
|
||||||
|
aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.profile_menu') }}" tabindex="0">
|
||||||
|
<img class="avatar" src="{{$user->getAvatar(30)}}" alt="{{ $user->name }}">
|
||||||
|
<span class="name">{{ $user->getShortName(9) }}</span> @icon('caret-down')
|
||||||
|
</span>
|
||||||
|
<ul refs="dropdown@menu" class="dropdown-menu" role="menu">
|
||||||
|
<li>
|
||||||
|
<a href="{{ url('/favourites') }}" data-shortcut="favourites_view" class="icon-item">
|
||||||
|
@icon('star')
|
||||||
|
<div>{{ trans('entities.my_favourites') }}</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{{ $user->getProfileUrl() }}" data-shortcut="profile_view" class="icon-item">
|
||||||
|
@icon('user')
|
||||||
|
<div>{{ trans('common.view_profile') }}</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{{ $user->getEditUrl() }}" class="icon-item">
|
||||||
|
@icon('edit')
|
||||||
|
<div>{{ trans('common.edit_profile') }}</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<form action="{{ url(config('auth.method') === 'saml2' ? '/saml2/logout' : '/logout') }}"
|
||||||
|
method="post">
|
||||||
|
{{ csrf_field() }}
|
||||||
|
<button class="icon-item" data-shortcut="logout">
|
||||||
|
@icon('logout')
|
||||||
|
<div>{{ trans('auth.logout') }}</div>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li>
|
||||||
|
<a href="{{ url('/preferences') }}" class="icon-item">
|
||||||
|
@icon('user-preferences')
|
||||||
|
<div>{{ trans('preferences.preferences') }}</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
@include('common.dark-mode-toggle', ['classes' => 'icon-item'])
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-container-column items-center justify-center hide-under-l">
|
<div class="flex-container-column items-center justify-center hide-under-l">
|
||||||
@if (hasAppAccess())
|
@if (user()->hasAppAccess())
|
||||||
<form component="global-search" action="{{ url('/search') }}" method="GET" class="search-box" role="search" tabindex="0">
|
<form component="global-search" action="{{ url('/search') }}" method="GET" class="search-box" role="search" tabindex="0">
|
||||||
<button id="header-search-box-button"
|
<button id="header-search-box-button"
|
||||||
refs="global-search@button"
|
refs="global-search@button"
|
||||||
@ -44,76 +44,29 @@
|
|||||||
|
|
||||||
<nav refs="header-mobile-toggle@menu" class="header-links">
|
<nav refs="header-mobile-toggle@menu" class="header-links">
|
||||||
<div class="links text-center">
|
<div class="links text-center">
|
||||||
@if (hasAppAccess())
|
@if (user()->hasAppAccess())
|
||||||
<a class="hide-over-l" href="{{ url('/search') }}">@icon('search'){{ trans('common.search') }}</a>
|
<a class="hide-over-l" href="{{ url('/search') }}">@icon('search'){{ trans('common.search') }}</a>
|
||||||
@if(userCanOnAny('view', \BookStack\Entities\Models\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
|
@if(userCanOnAny('view', \BookStack\Entities\Models\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
|
||||||
<a href="{{ url('/shelves') }}" data-shortcut="shelves_view">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
|
<a href="{{ url('/shelves') }}" data-shortcut="shelves_view">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
|
||||||
@endif
|
@endif
|
||||||
<a href="{{ url('/books') }}" data-shortcut="books_view">@icon('books'){{ trans('entities.books') }}</a>
|
<a href="{{ url('/books') }}" data-shortcut="books_view">@icon('books'){{ trans('entities.books') }}</a>
|
||||||
@if(signedInUser() && userCan('settings-manage'))
|
@if(!user()->isGuest() && userCan('settings-manage'))
|
||||||
<a href="{{ url('/settings') }}" data-shortcut="settings_view">@icon('settings'){{ trans('settings.settings') }}</a>
|
<a href="{{ url('/settings') }}" data-shortcut="settings_view">@icon('settings'){{ trans('settings.settings') }}</a>
|
||||||
@endif
|
@endif
|
||||||
@if(signedInUser() && userCan('users-manage') && !userCan('settings-manage'))
|
@if(!user()->isGuest() && userCan('users-manage') && !userCan('settings-manage'))
|
||||||
<a href="{{ url('/settings/users') }}" data-shortcut="settings_view">@icon('users'){{ trans('settings.users') }}</a>
|
<a href="{{ url('/settings/users') }}" data-shortcut="settings_view">@icon('users'){{ trans('settings.users') }}</a>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if(!signedInUser())
|
@if(user()->isGuest())
|
||||||
@if(setting('registration-enabled') && config('auth.method') === 'standard')
|
@if(setting('registration-enabled') && config('auth.method') === 'standard')
|
||||||
<a href="{{ url('/register') }}">@icon('new-user'){{ trans('auth.sign_up') }}</a>
|
<a href="{{ url('/register') }}">@icon('new-user'){{ trans('auth.sign_up') }}</a>
|
||||||
@endif
|
@endif
|
||||||
<a href="{{ url('/login') }}">@icon('login'){{ trans('auth.log_in') }}</a>
|
<a href="{{ url('/login') }}">@icon('login'){{ trans('auth.log_in') }}</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if(signedInUser())
|
@if(!user()->isGuest())
|
||||||
<?php $currentUser = user(); ?>
|
@include('common.header-user-menu', ['user' => user()])
|
||||||
<div class="dropdown-container" component="dropdown" option:dropdown:bubble-escapes="true">
|
|
||||||
<span class="user-name py-s hide-under-l" refs="dropdown@toggle"
|
|
||||||
aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.profile_menu') }}" tabindex="0">
|
|
||||||
<img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
|
|
||||||
<span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
|
|
||||||
</span>
|
|
||||||
<ul refs="dropdown@menu" class="dropdown-menu" role="menu">
|
|
||||||
<li>
|
|
||||||
<a href="{{ url('/favourites') }}" data-shortcut="favourites_view" class="icon-item">
|
|
||||||
@icon('star')
|
|
||||||
<div>{{ trans('entities.my_favourites') }}</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="{{ $currentUser->getProfileUrl() }}" data-shortcut="profile_view" class="icon-item">
|
|
||||||
@icon('user')
|
|
||||||
<div>{{ trans('common.view_profile') }}</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="{{ $currentUser->getEditUrl() }}" class="icon-item">
|
|
||||||
@icon('edit')
|
|
||||||
<div>{{ trans('common.edit_profile') }}</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<form action="{{ url(config('auth.method') === 'saml2' ? '/saml2/logout' : '/logout') }}"
|
|
||||||
method="post">
|
|
||||||
{{ csrf_field() }}
|
|
||||||
<button class="icon-item" data-shortcut="logout">
|
|
||||||
@icon('logout')
|
|
||||||
<div>{{ trans('auth.logout') }}</div>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
<li><hr></li>
|
|
||||||
<li>
|
|
||||||
<a href="{{ url('/preferences') }}" class="icon-item">
|
|
||||||
@icon('user-preferences')
|
|
||||||
<div>{{ trans('preferences.preferences') }}</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
@include('common.dark-mode-toggle', ['classes' => 'icon-item'])
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
@ -1,55 +1,55 @@
|
|||||||
@extends('layouts.simple')
|
@extends('layouts.simple')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container mt-l">
|
<div class="container mt-l">
|
||||||
|
|
||||||
<div class="card mb-xl px-l pb-l pt-l">
|
<div class="card mb-xl px-l pb-l pt-l">
|
||||||
<div class="grid half v-center">
|
<div class="grid half v-center">
|
||||||
<div>
|
<div>
|
||||||
@include('errors.parts.not-found-text', [
|
@include('errors.parts.not-found-text', [
|
||||||
'title' => $message ?? trans('errors.404_page_not_found'),
|
'title' => $message ?? trans('errors.404_page_not_found'),
|
||||||
'subtitle' => $subtitle ?? trans('errors.sorry_page_not_found'),
|
'subtitle' => $subtitle ?? trans('errors.sorry_page_not_found'),
|
||||||
'details' => $details ?? trans('errors.sorry_page_not_found_permission_warning'),
|
'details' => $details ?? trans('errors.sorry_page_not_found_permission_warning'),
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
@if(!signedInUser())
|
@if(user()->isGuest())
|
||||||
<a href="{{ url('/login') }}" class="button outline">{{ trans('auth.log_in') }}</a>
|
<a href="{{ url('/login') }}" class="button outline">{{ trans('auth.log_in') }}</a>
|
||||||
@endif
|
@endif
|
||||||
<a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a>
|
<a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (setting('app-public') || !user()->isGuest())
|
||||||
|
<div class="grid third gap-xxl">
|
||||||
|
<div>
|
||||||
|
<div class="card mb-xl">
|
||||||
|
<h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
|
||||||
|
<div class="px-m">
|
||||||
|
@include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['page']), 'style' => 'compact'])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="card mb-xl">
|
||||||
|
<h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
|
||||||
|
<div class="px-m">
|
||||||
|
@include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['book']), 'style' => 'compact'])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="card mb-xl">
|
||||||
|
<h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
|
||||||
|
<div class="px-m">
|
||||||
|
@include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['chapter']), 'style' => 'compact'])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (setting('app-public') || !user()->isDefault())
|
|
||||||
<div class="grid third gap-xxl">
|
|
||||||
<div>
|
|
||||||
<div class="card mb-xl">
|
|
||||||
<h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
|
|
||||||
<div class="px-m">
|
|
||||||
@include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['page']), 'style' => 'compact'])
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="card mb-xl">
|
|
||||||
<h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
|
|
||||||
<div class="px-m">
|
|
||||||
@include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['book']), 'style' => 'compact'])
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="card mb-xl">
|
|
||||||
<h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
|
|
||||||
<div class="px-m">
|
|
||||||
@include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['chapter']), 'style' => 'compact'])
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@stop
|
@stop
|
@ -188,7 +188,7 @@
|
|||||||
@if($watchOptions->canWatch() && !$watchOptions->isWatching())
|
@if($watchOptions->canWatch() && !$watchOptions->isWatching())
|
||||||
@include('entities.watch-action', ['entity' => $page])
|
@include('entities.watch-action', ['entity' => $page])
|
||||||
@endif
|
@endif
|
||||||
@if(signedInUser())
|
@if(!user()->isGuest())
|
||||||
@include('entities.favourite-action', ['entity' => $page])
|
@include('entities.favourite-action', ['entity' => $page])
|
||||||
@endif
|
@endif
|
||||||
@if(userCan('content-export'))
|
@if(userCan('content-export'))
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<h6>{{ trans('entities.search_tags') }}</h6>
|
<h6>{{ trans('entities.search_tags') }}</h6>
|
||||||
@include('search.parts.term-list', ['type' => 'tags', 'currentList' => $options->tags])
|
@include('search.parts.term-list', ['type' => 'tags', 'currentList' => $options->tags])
|
||||||
|
|
||||||
@if(signedInUser())
|
@if(!user()->isGuest())
|
||||||
<h6>{{ trans('entities.search_options') }}</h6>
|
<h6>{{ trans('entities.search_options') }}</h6>
|
||||||
|
|
||||||
@component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'viewed_by_me', 'value' => null])
|
@component('search.parts.boolean-filter', ['filters' => $options->filters, 'name' => 'viewed_by_me', 'value' => null])
|
||||||
|
@ -143,7 +143,7 @@
|
|||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if(signedInUser())
|
@if(!user()->isGuest())
|
||||||
<hr class="primary-background">
|
<hr class="primary-background">
|
||||||
@include('entities.favourite-action', ['entity' => $shelf])
|
@include('entities.favourite-action', ['entity' => $shelf])
|
||||||
@endif
|
@endif
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@if(signedInUser() && userCan('receive-notifications'))
|
@if(!user()->isGuest() && userCan('receive-notifications'))
|
||||||
<section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row wrap">
|
<section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row wrap">
|
||||||
<div class="flex min-width-m">
|
<div class="flex min-width-m">
|
||||||
<h2 class="list-heading">{{ trans('preferences.notifications') }}</h2>
|
<h2 class="list-heading">{{ trans('preferences.notifications') }}</h2>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</section>
|
</section>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if(signedInUser())
|
@if(!user()->isGuest())
|
||||||
<section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row wrap">
|
<section class="card content-wrap auto-height items-center justify-space-between gap-m flex-container-row wrap">
|
||||||
<div class="flex min-width-m">
|
<div class="flex min-width-m">
|
||||||
<h2 class="list-heading">{{ trans('settings.users_edit_profile') }}</h2>
|
<h2 class="list-heading">{{ trans('settings.users_edit_profile') }}</h2>
|
||||||
|
@ -136,7 +136,7 @@ class PageRevisionTest extends TestCase
|
|||||||
$page = $this->entities->page();
|
$page = $this->entities->page();
|
||||||
$this->createRevisions($page, 2);
|
$this->createRevisions($page, 2);
|
||||||
|
|
||||||
$pageView = $this->get($page->getUrl());
|
$pageView = $this->asViewer()->get($page->getUrl());
|
||||||
$pageView->assertSee('Revision #' . $page->revision_count);
|
$pageView->assertSee('Revision #' . $page->revision_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class UserRoleProvider
|
|||||||
*/
|
*/
|
||||||
public function guest(): User
|
public function guest(): User
|
||||||
{
|
{
|
||||||
return User::getDefault();
|
return User::getGuest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,7 +103,7 @@ class PublicActionTest extends TestCase
|
|||||||
$resp = $this->post($chapter->getUrl('/create-guest-page'), ['name' => 'My guest page']);
|
$resp = $this->post($chapter->getUrl('/create-guest-page'), ['name' => 'My guest page']);
|
||||||
$resp->assertRedirect($chapter->book->getUrl('/page/my-guest-page/edit'));
|
$resp->assertRedirect($chapter->book->getUrl('/page/my-guest-page/edit'));
|
||||||
|
|
||||||
$user = User::getDefault();
|
$user = $this->users->guest();
|
||||||
$this->assertDatabaseHas('pages', [
|
$this->assertDatabaseHas('pages', [
|
||||||
'name' => 'My guest page',
|
'name' => 'My guest page',
|
||||||
'chapter_id' => $chapter->id,
|
'chapter_id' => $chapter->id,
|
||||||
@ -197,7 +197,7 @@ class PublicActionTest extends TestCase
|
|||||||
public function test_public_view_can_take_on_other_roles()
|
public function test_public_view_can_take_on_other_roles()
|
||||||
{
|
{
|
||||||
$this->setSettings(['app-public' => 'true']);
|
$this->setSettings(['app-public' => 'true']);
|
||||||
$newRole = $this->users->attachNewRole(User::getDefault(), []);
|
$newRole = $this->users->attachNewRole($this->users->guest(), []);
|
||||||
$page = $this->entities->page();
|
$page = $this->entities->page();
|
||||||
$this->permissions->disableEntityInheritedPermissions($page);
|
$this->permissions->disableEntityInheritedPermissions($page);
|
||||||
$this->permissions->addEntityPermission($page, ['view', 'update'], $newRole);
|
$this->permissions->addEntityPermission($page, ['view', 'update'], $newRole);
|
||||||
|
@ -42,7 +42,6 @@ abstract class TestCase extends BaseTestCase
|
|||||||
$this->permissions = new PermissionsProvider($this->users);
|
$this->permissions = new PermissionsProvider($this->users);
|
||||||
$this->files = new FileProvider();
|
$this->files = new FileProvider();
|
||||||
|
|
||||||
User::clearDefault();
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
// We can uncomment the below to run tests with failings upon deprecations.
|
// We can uncomment the below to run tests with failings upon deprecations.
|
||||||
|
@ -191,7 +191,7 @@ class UserManagementTest extends TestCase
|
|||||||
|
|
||||||
public function test_guest_profile_shows_limited_form()
|
public function test_guest_profile_shows_limited_form()
|
||||||
{
|
{
|
||||||
$guest = User::getDefault();
|
$guest = $this->users->guest();
|
||||||
$resp = $this->asAdmin()->get('/settings/users/' . $guest->id);
|
$resp = $this->asAdmin()->get('/settings/users/' . $guest->id);
|
||||||
$resp->assertSee('Guest');
|
$resp->assertSee('Guest');
|
||||||
$this->withHtml($resp)->assertElementNotExists('#password');
|
$this->withHtml($resp)->assertElementNotExists('#password');
|
||||||
@ -199,7 +199,7 @@ class UserManagementTest extends TestCase
|
|||||||
|
|
||||||
public function test_guest_profile_cannot_be_deleted()
|
public function test_guest_profile_cannot_be_deleted()
|
||||||
{
|
{
|
||||||
$guestUser = User::getDefault();
|
$guestUser = $this->users->guest();
|
||||||
$resp = $this->asAdmin()->get('/settings/users/' . $guestUser->id . '/delete');
|
$resp = $this->asAdmin()->get('/settings/users/' . $guestUser->id . '/delete');
|
||||||
$resp->assertSee('Delete User');
|
$resp->assertSee('Delete User');
|
||||||
$resp->assertSee('Guest');
|
$resp->assertSee('Guest');
|
||||||
|
@ -57,8 +57,7 @@ class UserSearchTest extends TestCase
|
|||||||
public function test_select_requires_logged_in_user()
|
public function test_select_requires_logged_in_user()
|
||||||
{
|
{
|
||||||
$this->setSettings(['app-public' => true]);
|
$this->setSettings(['app-public' => true]);
|
||||||
$defaultUser = User::getDefault();
|
$this->permissions->grantUserRolePermissions($this->users->guest(), ['users-manage']);
|
||||||
$this->permissions->grantUserRolePermissions($defaultUser, ['users-manage']);
|
|
||||||
|
|
||||||
$resp = $this->get('/search/users/select?search=a');
|
$resp = $this->get('/search/users/select?search=a');
|
||||||
$this->assertPermissionError($resp);
|
$this->assertPermissionError($resp);
|
||||||
|
Loading…
Reference in New Issue
Block a user