mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
URL Handling: Removed referrer-based redirect handling
Swapped back handling to instead be pre-determined instead of being based upon session/referrer which would cause inconsistent results when referrer data was not available (redirect to app-loaded images/files). To support, this adds a mechansism to provide a URL through request data. Also cleaned up some imports in code while making changes. Closes #4656.
This commit is contained in:
parent
11955e270c
commit
45ce7a7126
@ -9,11 +9,6 @@ use Illuminate\Support\Facades\Password;
|
|||||||
|
|
||||||
class ForgotPasswordController extends Controller
|
class ForgotPasswordController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('guest');
|
$this->middleware('guest');
|
||||||
@ -30,10 +25,6 @@ class ForgotPasswordController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a reset link to the given user.
|
* Send a reset link to the given user.
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
|
||||||
*/
|
*/
|
||||||
public function sendResetLinkEmail(Request $request)
|
public function sendResetLinkEmail(Request $request)
|
||||||
{
|
{
|
||||||
@ -56,13 +47,13 @@ class ForgotPasswordController extends Controller
|
|||||||
$message = trans('auth.reset_password_sent', ['email' => $request->get('email')]);
|
$message = trans('auth.reset_password_sent', ['email' => $request->get('email')]);
|
||||||
$this->showSuccessNotification($message);
|
$this->showSuccessNotification($message);
|
||||||
|
|
||||||
return back()->with('status', trans($response));
|
return redirect('/password/email')->with('status', trans($response));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an error was returned by the password broker, we will get this message
|
// If an error was returned by the password broker, we will get this message
|
||||||
// translated so we can notify a user of the problem. We'll redirect back
|
// translated so we can notify a user of the problem. We'll redirect back
|
||||||
// to where the users came from so they can attempt this process again.
|
// to where the users came from so they can attempt this process again.
|
||||||
return back()->withErrors(
|
return redirect('/password/email')->withErrors(
|
||||||
['email' => trans($response)]
|
['email' => trans($response)]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ class ResetPasswordController extends Controller
|
|||||||
// redirect them back to where they came from with their error message.
|
// redirect them back to where they came from with their error message.
|
||||||
return $response === Password::PASSWORD_RESET
|
return $response === Password::PASSWORD_RESET
|
||||||
? $this->sendResetResponse()
|
? $this->sendResetResponse()
|
||||||
: $this->sendResetFailedResponse($request, $response);
|
: $this->sendResetFailedResponse($request, $response, $request->get('token'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,7 +83,7 @@ class ResetPasswordController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Get the response for a failed password reset.
|
* Get the response for a failed password reset.
|
||||||
*/
|
*/
|
||||||
protected function sendResetFailedResponse(Request $request, string $response): RedirectResponse
|
protected function sendResetFailedResponse(Request $request, string $response, string $token): RedirectResponse
|
||||||
{
|
{
|
||||||
// We show invalid users as invalid tokens as to not leak what
|
// We show invalid users as invalid tokens as to not leak what
|
||||||
// users may exist in the system.
|
// users may exist in the system.
|
||||||
@ -91,7 +91,7 @@ class ResetPasswordController extends Controller
|
|||||||
$response = Password::INVALID_TOKEN;
|
$response = Password::INVALID_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back()
|
return redirect("/password/reset/{$token}")
|
||||||
->withInput($request->only('email'))
|
->withInput($request->only('email'))
|
||||||
->withErrors(['email' => trans($response)]);
|
->withErrors(['email' => trans($response)]);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ class SocialController extends Controller
|
|||||||
return $this->socialRegisterCallback($socialDriver, $socialUser);
|
return $this->socialRegisterCallback($socialDriver, $socialUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
namespace BookStack\Activity\Controllers;
|
namespace BookStack\Activity\Controllers;
|
||||||
|
|
||||||
use BookStack\Activity\Models\Favouritable;
|
|
||||||
use BookStack\App\Model;
|
|
||||||
use BookStack\Entities\Models\Entity;
|
|
||||||
use BookStack\Entities\Queries\TopFavourites;
|
use BookStack\Entities\Queries\TopFavourites;
|
||||||
use BookStack\Entities\Tools\MixedEntityRequestHelper;
|
use BookStack\Entities\Tools\MixedEntityRequestHelper;
|
||||||
use BookStack\Http\Controller;
|
use BookStack\Http\Controller;
|
||||||
@ -52,7 +49,7 @@ class FavouriteController extends Controller
|
|||||||
'name' => $entity->name,
|
'name' => $entity->name,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect($entity->getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,6 +67,6 @@ class FavouriteController extends Controller
|
|||||||
'name' => $entity->name,
|
'name' => $entity->name,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect($entity->getUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,6 @@ class WatchController extends Controller
|
|||||||
|
|
||||||
$this->showSuccessNotification(trans('activities.watch_update_level_notification'));
|
$this->showSuccessNotification(trans('activities.watch_update_level_notification'));
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect($watchable->getUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ use BookStack\Entities\Tools\HierarchyTransformer;
|
|||||||
use BookStack\Entities\Tools\NextPreviousContentLocator;
|
use BookStack\Entities\Tools\NextPreviousContentLocator;
|
||||||
use BookStack\Exceptions\MoveOperationException;
|
use BookStack\Exceptions\MoveOperationException;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
|
use BookStack\Exceptions\NotifyException;
|
||||||
use BookStack\Exceptions\PermissionsException;
|
use BookStack\Exceptions\PermissionsException;
|
||||||
use BookStack\Http\Controller;
|
use BookStack\Http\Controller;
|
||||||
use BookStack\References\ReferenceFetcher;
|
use BookStack\References\ReferenceFetcher;
|
||||||
@ -170,7 +171,7 @@ class ChapterController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Perform the move action for a chapter.
|
* Perform the move action for a chapter.
|
||||||
*
|
*
|
||||||
* @throws NotFoundException
|
* @throws NotFoundException|NotifyException
|
||||||
*/
|
*/
|
||||||
public function move(Request $request, string $bookSlug, string $chapterSlug)
|
public function move(Request $request, string $bookSlug, string $chapterSlug)
|
||||||
{
|
{
|
||||||
@ -184,13 +185,13 @@ class ChapterController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$newBook = $this->chapterRepo->move($chapter, $entitySelection);
|
$this->chapterRepo->move($chapter, $entitySelection);
|
||||||
} catch (PermissionsException $exception) {
|
} catch (PermissionsException $exception) {
|
||||||
$this->showPermissionError();
|
$this->showPermissionError();
|
||||||
} catch (MoveOperationException $exception) {
|
} catch (MoveOperationException $exception) {
|
||||||
$this->showErrorNotification(trans('errors.selected_book_not_found'));
|
$this->showErrorNotification(trans('errors.selected_book_not_found'));
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect($chapter->getUrl('/move'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($chapter->getUrl());
|
return redirect($chapter->getUrl());
|
||||||
@ -231,7 +232,7 @@ class ChapterController extends Controller
|
|||||||
if (is_null($newParentBook)) {
|
if (is_null($newParentBook)) {
|
||||||
$this->showErrorNotification(trans('errors.selected_book_not_found'));
|
$this->showErrorNotification(trans('errors.selected_book_not_found'));
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect($chapter->getUrl('/copy'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->checkOwnablePermission('chapter-create', $newParentBook);
|
$this->checkOwnablePermission('chapter-create', $newParentBook);
|
||||||
|
@ -391,7 +391,7 @@ class PageController extends Controller
|
|||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
$this->showErrorNotification(trans('errors.selected_book_chapter_not_found'));
|
$this->showErrorNotification(trans('errors.selected_book_chapter_not_found'));
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect($page->getUrl('/move'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($page->getUrl());
|
return redirect($page->getUrl());
|
||||||
@ -431,7 +431,7 @@ class PageController extends Controller
|
|||||||
if (is_null($newParent)) {
|
if (is_null($newParent)) {
|
||||||
$this->showErrorNotification(trans('errors.selected_book_chapter_not_found'));
|
$this->showErrorNotification(trans('errors.selected_book_chapter_not_found'));
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect($page->getUrl('/copy'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->checkOwnablePermission('page-create', $newParent);
|
$this->checkOwnablePermission('page-create', $newParent);
|
||||||
|
@ -9,6 +9,8 @@ use BookStack\Facades\Activity;
|
|||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
abstract class Controller extends BaseController
|
abstract class Controller extends BaseController
|
||||||
@ -165,4 +167,20 @@ abstract class Controller extends BaseController
|
|||||||
{
|
{
|
||||||
return ['image_extension', 'mimes:jpeg,png,gif,webp', 'max:' . (config('app.upload_limit') * 1000)];
|
return ['image_extension', 'mimes:jpeg,png,gif,webp', 'max:' . (config('app.upload_limit') * 1000)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to the URL provided in the request as a '_return' parameter.
|
||||||
|
* Will check that the parameter leads to a URL under the root path of the system.
|
||||||
|
*/
|
||||||
|
protected function redirectToRequest(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$basePath = url('/');
|
||||||
|
$returnUrl = $request->input('_return') ?? $basePath;
|
||||||
|
|
||||||
|
if (!str_starts_with($returnUrl, $basePath)) {
|
||||||
|
return redirect($basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect($returnUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +154,7 @@ class RoleController extends Controller
|
|||||||
} catch (PermissionsException $e) {
|
} catch (PermissionsException $e) {
|
||||||
$this->showErrorNotification($e->getMessage());
|
$this->showErrorNotification($e->getMessage());
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect("/settings/roles/delete/{$id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect('/settings/roles');
|
return redirect('/settings/roles');
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
namespace BookStack\Users\Controllers;
|
namespace BookStack\Users\Controllers;
|
||||||
|
|
||||||
use BookStack\Http\Controller;
|
use BookStack\Http\Controller;
|
||||||
use BookStack\Permissions\PermissionApplicator;
|
|
||||||
use BookStack\Settings\UserNotificationPreferences;
|
|
||||||
use BookStack\Settings\UserShortcutMap;
|
|
||||||
use BookStack\Users\UserRepo;
|
use BookStack\Users\UserRepo;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@ -23,7 +20,7 @@ class UserPreferencesController extends Controller
|
|||||||
{
|
{
|
||||||
$valueViewTypes = ['books', 'bookshelves', 'bookshelf'];
|
$valueViewTypes = ['books', 'bookshelves', 'bookshelf'];
|
||||||
if (!in_array($type, $valueViewTypes)) {
|
if (!in_array($type, $valueViewTypes)) {
|
||||||
return redirect()->back(500);
|
return $this->redirectToRequest($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
$view = $request->get('view');
|
$view = $request->get('view');
|
||||||
@ -34,7 +31,7 @@ class UserPreferencesController extends Controller
|
|||||||
$key = $type . '_view_type';
|
$key = $type . '_view_type';
|
||||||
setting()->putForCurrentUser($key, $view);
|
setting()->putForCurrentUser($key, $view);
|
||||||
|
|
||||||
return redirect()->back(302, [], "/");
|
return $this->redirectToRequest($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,7 +41,7 @@ class UserPreferencesController extends Controller
|
|||||||
{
|
{
|
||||||
$validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users', 'roles', 'webhooks', 'tags', 'page_revisions'];
|
$validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users', 'roles', 'webhooks', 'tags', 'page_revisions'];
|
||||||
if (!in_array($type, $validSortTypes)) {
|
if (!in_array($type, $validSortTypes)) {
|
||||||
return redirect()->back(500);
|
return $this->redirectToRequest($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
$sort = substr($request->get('sort') ?: 'name', 0, 50);
|
$sort = substr($request->get('sort') ?: 'name', 0, 50);
|
||||||
@ -55,18 +52,18 @@ class UserPreferencesController extends Controller
|
|||||||
setting()->putForCurrentUser($sortKey, $sort);
|
setting()->putForCurrentUser($sortKey, $sort);
|
||||||
setting()->putForCurrentUser($orderKey, $order);
|
setting()->putForCurrentUser($orderKey, $order);
|
||||||
|
|
||||||
return redirect()->back(302, [], "/");
|
return $this->redirectToRequest($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle dark mode for the current user.
|
* Toggle dark mode for the current user.
|
||||||
*/
|
*/
|
||||||
public function toggleDarkMode()
|
public function toggleDarkMode(Request $request)
|
||||||
{
|
{
|
||||||
$enabled = setting()->getForCurrentUser('dark-mode-enabled');
|
$enabled = setting()->getForCurrentUser('dark-mode-enabled');
|
||||||
setting()->putForCurrentUser('dark-mode-enabled', $enabled ? 'false' : 'true');
|
setting()->putForCurrentUser('dark-mode-enabled', $enabled ? 'false' : 'true');
|
||||||
|
|
||||||
return redirect()->back();
|
return $this->redirectToRequest($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<form action="{{ url('/preferences/toggle-dark-mode') }}" method="post">
|
<form action="{{ url('/preferences/toggle-dark-mode') }}" method="post">
|
||||||
{{ csrf_field() }}
|
{{ csrf_field() }}
|
||||||
{{ method_field('patch') }}
|
{{ method_field('patch') }}
|
||||||
|
<input type="hidden" name="_return" value="{{ url()->current() }}">
|
||||||
@if(setting()->getForCurrentUser('dark-mode-enabled'))
|
@if(setting()->getForCurrentUser('dark-mode-enabled'))
|
||||||
<button class="{{ $classes ?? '' }}"><span>@icon('light-mode')</span><span>{{ trans('common.light_mode') }}</span></button>
|
<button class="{{ $classes ?? '' }}"><span>@icon('light-mode')</span><span>{{ trans('common.light_mode') }}</span></button>
|
||||||
@else
|
@else
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
method="post"
|
method="post"
|
||||||
@endif
|
@endif
|
||||||
>
|
>
|
||||||
|
<input type="hidden" name="_return" value="{{ url()->current() }}">
|
||||||
|
|
||||||
@if($useQuery ?? false)
|
@if($useQuery ?? false)
|
||||||
@foreach(array_filter(request()->except(['sort', 'order'])) as $key => $value)
|
@foreach(array_filter(request()->except(['sort', 'order'])) as $key => $value)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<form action="{{ url("/preferences/change-view/" . $type) }}" method="POST" class="inline">
|
<form action="{{ url("/preferences/change-view/" . $type) }}" method="POST" class="inline">
|
||||||
{!! csrf_field() !!}
|
{{ csrf_field() }}
|
||||||
{!! method_field('PATCH') !!}
|
{{ method_field('patch') }}
|
||||||
|
<input type="hidden" name="_return" value="{{ url()->current() }}">
|
||||||
|
|
||||||
@if ($view === 'list')
|
@if ($view === 'list')
|
||||||
<button type="submit" name="view" value="grid" class="icon-list-item text-link">
|
<button type="submit" name="view" value="grid" class="icon-list-item text-link">
|
||||||
@ -10,7 +11,7 @@
|
|||||||
</button>
|
</button>
|
||||||
@else
|
@else
|
||||||
<button type="submit" name="view" value="list" class="icon-list-item text-link">
|
<button type="submit" name="view" value="list" class="icon-list-item text-link">
|
||||||
<span>@icon('list')</span>
|
<span class="icon">@icon('list')</span>
|
||||||
<span>{{ trans('common.list_view') }}</span>
|
<span>{{ trans('common.list_view') }}</span>
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
|
@ -63,8 +63,7 @@ class WatchTest extends TestCase
|
|||||||
$editor = $this->users->editor();
|
$editor = $this->users->editor();
|
||||||
$book = $this->entities->book();
|
$book = $this->entities->book();
|
||||||
|
|
||||||
$this->actingAs($editor)->get($book->getUrl());
|
$resp = $this->actingAs($editor)->put('/watching/update', [
|
||||||
$resp = $this->put('/watching/update', [
|
|
||||||
'type' => $book->getMorphClass(),
|
'type' => $book->getMorphClass(),
|
||||||
'id' => $book->id,
|
'id' => $book->id,
|
||||||
'level' => 'comments'
|
'level' => 'comments'
|
||||||
|
@ -56,6 +56,23 @@ class FavouriteTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_add_and_remove_redirect_to_entity_without_history()
|
||||||
|
{
|
||||||
|
$page = $this->entities->page();
|
||||||
|
|
||||||
|
$resp = $this->asEditor()->post('/favourites/add', [
|
||||||
|
'type' => $page->getMorphClass(),
|
||||||
|
'id' => $page->id,
|
||||||
|
]);
|
||||||
|
$resp->assertRedirect($page->getUrl());
|
||||||
|
|
||||||
|
$resp = $this->asEditor()->post('/favourites/remove', [
|
||||||
|
'type' => $page->getMorphClass(),
|
||||||
|
'id' => $page->id,
|
||||||
|
]);
|
||||||
|
$resp->assertRedirect($page->getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
public function test_favourite_flow_with_own_permissions()
|
public function test_favourite_flow_with_own_permissions()
|
||||||
{
|
{
|
||||||
$book = $this->entities->book();
|
$book = $this->entities->book();
|
||||||
|
@ -235,7 +235,7 @@ class RoleManagementTest extends TestCase
|
|||||||
/** @var Role $publicRole */
|
/** @var Role $publicRole */
|
||||||
$publicRole = Role::getSystemRole('public');
|
$publicRole = Role::getSystemRole('public');
|
||||||
$resp = $this->asAdmin()->delete('/settings/roles/delete/' . $publicRole->id);
|
$resp = $this->asAdmin()->delete('/settings/roles/delete/' . $publicRole->id);
|
||||||
$resp->assertRedirect('/');
|
$resp->assertRedirect('/settings/roles/delete/' . $publicRole->id);
|
||||||
|
|
||||||
$this->get('/settings/roles/delete/' . $publicRole->id);
|
$this->get('/settings/roles/delete/' . $publicRole->id);
|
||||||
$resp = $this->delete('/settings/roles/delete/' . $publicRole->id);
|
$resp = $this->delete('/settings/roles/delete/' . $publicRole->id);
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace Tests\User;
|
namespace Tests\User;
|
||||||
|
|
||||||
use BookStack\Activity\Tools\UserEntityWatchOptions;
|
|
||||||
use BookStack\Activity\WatchLevels;
|
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
class UserPreferencesTest extends TestCase
|
class UserPreferencesTest extends TestCase
|
||||||
@ -40,7 +38,7 @@ class UserPreferencesTest extends TestCase
|
|||||||
'sort' => 'name',
|
'sort' => 'name',
|
||||||
'order' => 'asc',
|
'order' => 'asc',
|
||||||
]);
|
]);
|
||||||
$updateRequest->assertStatus(500);
|
$updateRequest->assertRedirect();
|
||||||
|
|
||||||
$this->assertNotEmpty('name', setting()->getForCurrentUser('bookshelves_sort'));
|
$this->assertNotEmpty('name', setting()->getForCurrentUser('bookshelves_sort'));
|
||||||
$this->assertNotEmpty('asc', setting()->getForCurrentUser('bookshelves_sort_order'));
|
$this->assertNotEmpty('asc', setting()->getForCurrentUser('bookshelves_sort_order'));
|
||||||
@ -141,7 +139,10 @@ class UserPreferencesTest extends TestCase
|
|||||||
->assertElementNotExists('.featured-image-container')
|
->assertElementNotExists('.featured-image-container')
|
||||||
->assertElementExists('.content-wrap .entity-list-item');
|
->assertElementExists('.content-wrap .entity-list-item');
|
||||||
|
|
||||||
$req = $this->patch("/preferences/change-view/bookshelf", ['view' => 'grid']);
|
$req = $this->patch("/preferences/change-view/bookshelf", [
|
||||||
|
'view' => 'grid',
|
||||||
|
'_return' => $shelf->getUrl(),
|
||||||
|
]);
|
||||||
$req->assertRedirect($shelf->getUrl());
|
$req->assertRedirect($shelf->getUrl());
|
||||||
|
|
||||||
$resp = $this->actingAs($editor)->get($shelf->getUrl())
|
$resp = $this->actingAs($editor)->get($shelf->getUrl())
|
||||||
|
Loading…
Reference in New Issue
Block a user